پرش به مطلب اصلی

TcpOverUdpServer

TcpOverUdpServer peer سمت server برای TcpOverUdpClient است. datagram‌های KCP را بدون از بین بردن مرز packet‌ها از node قبلی می‌گیرد، byte stream اصلی را بازسازی می‌کند و آن را به node بعدی می‌فرستد. پاسخ node بعدی نیز دوباره به datagram‌های KCP تبدیل می‌شود و در جهت downstream به سمت packet قبلی برمی‌گردد.

این node معمولاً با TcpOverUdpClient در سمت مقابل استفاده می‌شود.

جایگاه رایج​

سمت server:

UdpListener -> TcpOverUdpServer -> TcpConnector
UdpListener -> TcpOverUdpServer -> MuxServer -> TcpConnector

سمت client متناظر:

TcpListener -> TcpOverUdpClient -> UdpConnector
TcpListener -> MuxClient -> TcpOverUdpClient -> UdpConnector

node قبلی TcpOverUdpServer باید مرز datagram‌ها را حفظ کند؛ node بعدی با stream سروکار دارد.

نمونه پایه​

{
"name": "tcp-over-udp-server",
"type": "TcpOverUdpServer",
"settings": {
"fec": false,
"kcp-send-window": 8192,
"kcp-recv-window": 8192,
"ping-interval-ms": 10000,
"no-recv-timeout-ms": 30000
},
"next": "service-out"
}

connector سرویس در ادامه مسیر:

{
"name": "service-out",
"type": "TcpConnector",
"settings": {
"address": "127.0.0.1",
"port": 8080
}
}

نمونه با FEC​

{
"name": "tcp-over-udp-server",
"type": "TcpOverUdpServer",
"settings": {
"fec": true,
"fec-data-shards": 10,
"fec-parity-shards": 3
},
"next": "service-out"
}

تنظیمات FEC باید دقیقاً با سمت TcpOverUdpClient یکسان باشد.

فیلدهای ضروری​

settings اختیاری است. اگر آن را ننویسید یا مقدارش object نباشد، مقادیر پیش‌فرض به کار می‌روند.

در حالت معمول next لازم است و باید به مسیر stream-side سرویس اشاره کند؛ همان مسیری که payload بازسازی‌شده را دریافت خواهد کرد.

تنظیمات اختیاری​

نام setting‌ها و مقدارهای پیش‌فرض TcpOverUdpServer با TcpOverUdpClient یکسان است.

فیلدپیش‌فرضاعتبارسنجیتوضیح
fecfalsebooleanفعال‌سازی Reed-Solomon forward error correction روی datagram‌های KCP.
fec-data-shards10مثبت، با مجموع shard‌ها حداکثر 255تعداد data shard در هر FEC block. فقط وقتی fec فعال است استفاده می‌شود.
fec-parity-shards3مثبت، با مجموع shard‌ها حداکثر 255تعداد parity shard در هر FEC block. فقط وقتی fec فعال است استفاده می‌شود.
kcp-nodelaytruebooleanفعال‌سازی KCP nodelay mode.
kcp-interval-ms1010..5000فاصله زمانی بین update‌های KCP.
kcp-resend2>= 0آستانه fast-resend در KCP.
kcp-no-congestion-controltruebooleanغیرفعال کردن congestion control در KCP.
kcp-send-window8192>= 1send window بر حسب segment.
kcp-recv-window8192>= 1receive window بر حسب segment.
kcp-initial-cwndنصف kcp-send-window1..kcp-send-windowcongestion window اولیه KCP.
kcp-rx-minrto-ms30>= 1حداقل retransmission timeout.
kcp-send-buffer-limit0>= 0آستانه backpressure. با مقدار 0، حد از مجموع local send window، remote window و 10 محاسبه و به 2147483647 محدود می‌شود.
ping-interval-ms10000>= 1اگر تا این مدت داده‌ای دریافت نشود، یک ping frame داخلی فرستاده می‌شود.
no-recv-timeout-ms30000بزرگ‌تر از ping-interval-msاگر تا این مدت داده‌ای دریافت نشود، line بسته می‌شود.

مدل KCP transport​

هنگام دریافت upstream Init، server ابتدا per-line state را مقداردهی می‌کند، یک session برای KCP می‌سازد و timer loop آن را راه می‌اندازد. اگر FEC فعال باشد state مربوط به آن را هم می‌سازد، یک ping frame اولیه در صف می‌گذارد و در پایان upstream Init را به node stream-side بعدی می‌فرستد.

وقتی payload packet از node قبلی می‌رسد:

  1. در صورت فعال بودن، FEC decoding اعمال می‌شود.
  2. KCP datagram را می‌گیرد.
  3. payload‌های کامل‌شده از KCP خوانده می‌شوند.
  4. flag یک‌بایتی داخلی حذف می‌شود.
  5. data frame‌ها به شکل payload stream بازسازی می‌شوند و در جهت upstream به node بعدی می‌روند.

وقتی payload stream از node بعدی برمی‌گردد:

  1. payload به chunk‌هایی با حداکثر اندازه KCP write MTU موثر تقسیم می‌شود.
  2. هر chunk data flag یک‌بایتی prefix می‌گیرد.
  3. chunk فریم‌شده از طریق KCP ارسال می‌شود.
  4. datagram‌های خروجی KCP در جهت downstream به سمت packet قبلی فرستاده می‌شوند.

flag‌های داخلی​

داخل payload KCP، TcpOverUdpServer از همان flag‌های frame یک‌بایتی سمت client استفاده می‌کند:

Flagمعنی
0x00data frame. بایت‌های بعد از flag همان payload stream واقعی هستند.
0xF0ping frame؛ نشانگر داخلی keepalive.
0xFFclose frame؛ درخواست بستن line در peer.

data frame خالی دور ریخته می‌شود.

MTU و padding​

گزینهٔ اختیاری و صحیح mtu هنگام ساخت از misc.mtu هسته مقدار می‌گیرد؛ پیش‌فرض هسته 1500 است. بازهٔ معتبر 78..65535 بدون FEC و 86..65535 با FEC است. این مقدار بودجهٔ بسته برحسب بایت با فرض IPv4 است. مقدار صریح یا ارث‌رسیدهٔ نامعتبر ساخت node را رد می‌کند؛ با override معتبر می‌توان پیش‌فرض نامناسب هسته را جایگزین کرد. null، نوع نادرست، عدد اعشاری، صفر، عدد منفی و مقدار خارج از بازه خطا هستند. هر نمونه مقدار معتبر خود را مستقل و ثابت نگه می‌دارد.

با F = 0 بدون FEC و F = 8 با FEC:

KCP MTU = min(mtu - 20 - 8 - F, 64000)
application chunk = KCP MTU - 24 - 1

سهم ثابت IPv4 برابر 20 بایت و UDP برابر 8 بایت است. سقف ده‌دهی 64000 شامل کل بستهٔ KCP و هدر 24 بایتی آن است. پرچم یک‌بایتی داخل payload قرار دارد و node required_padding_left = 1 اعلام می‌کند. همین MTU خروجی داده و ACK/control تجمیع‌شده را محدود می‌کند؛ FEC حداکثر 8 بایت، شامل بسته‌های parity، اضافه می‌کند.

برای mtu: 1500، MTU مؤثر KCP و chunk برنامه بدون FEC برابر 1472/1447 و با FEC برابر 1464/1439 هستند. بودجهٔ 64028 بدون FEC یا 64036 با FEC به سقف می‌رسد. مقادیر معتبر بزرگ‌تر، از جمله 65535، پذیرفته می‌شوند و MTU مؤثر 64000 و chunk برابر 63975 دارند؛ خود تنظیم به 64000 کاهش نمی‌یابد.

این تنظیم کشف یا مذاکرهٔ MTU مسیر نیست. هدر پایهٔ IPv6 بیست بایت بیشتر نیاز دارد؛ بودجهٔ 1500 می‌تواند بستهٔ IPv6 با طول 1520 بسازد. گزینه‌های IPv4، هدرهای توسعهٔ IPv6 و پوشش‌های بعدی به حاشیهٔ اضافی نیاز دارند. تشخیص خودکار خانواده انجام نمی‌شود.

رفتار FEC​

با fec: true، datagram‌های خروجی KCP در packet‌های Reed-Solomon FEC قرار می‌گیرند. data shard‌ها بلافاصله و parity shard‌ها پس از کامل شدن هر block فرستاده می‌شوند.

در receive، decoder:

  • datagram‌های KCP wrap‌شده با FEC را می‌پذیرد
  • data shard‌های معتبر را به KCP می‌دهد
  • تلاش می‌کند datagram‌های KCP گم‌شده را از parity shard‌ها بازسازی کند
  • packet‌های FEC نامعتبر را دور می‌ریزد

FEC در دو سمت این pair رفتاری متقارن دارد. آن را در هر دو peer فعال کنید و تعداد shard‌ها را یکسان بگذارید.

Backpressure​

سیگنال‌های ورودی Pause/Resume راهنما هستند و حتی در حالت تکراری عبور داده می‌شوند. این سیگنال‌ها update، ارسال مجدد، ping یا خروجی آمادهٔ KCP را متوقف نمی‌کنند.

پس از پذیرش کامل ورودی و تحویل packet‌های خروجی تولیدشده، server تعداد segmentهای صف‌شده و تأییدنشدهٔ KCP را بررسی می‌کند. بالاتر از kcp-send-buffer-limit، یک upstream Pause محلی به سمت stream بعدی می‌فرستد؛ پایین‌تر از آستانه، این توقف محلی را با Resume آزاد می‌کند. در مقدار برابر، وضعیت فعلی حفظ می‌شود. حتی update ناشی از ACK تنها نیز می‌تواند Resume بدهد و نیازی به تولید packet خروجی دیگری نیست. وضعیت محلی پیش از callback ثبت می‌شود و پس از Finish، سیگنالی به سمت بسته‌شده فرستاده نمی‌شود. task جداگانه یا صف مخصوص Resume لازم نیست.

این تنظیم آستانهٔ فشار راهنماست، نه سقف سخت حافظه یا پذیرش داده. پردازش KCP/FEC و timerهای session در تمام طول line لازم‌اند؛ بنابراین node نمی‌تواند خودش را به‌طور دائمی bypass کند.

رفتار Close و timeout​

اگر سمت stream بعدی Finish شود، TcpOverUdpServer این مراحل را انجام می‌دهد:

  1. جهت upstream را بسته علامت می‌زند تا callback‌های re-entrant به سمتی که finish شده برنگردند
  2. یک close frame داخلی از طریق KCP می‌فرستد
  3. خروجی KCP را flush می‌کند
  4. per-line state محلی را از بین می‌برد
  5. downstream Finish را به سمت packet می‌فرستد

اگر close frame از peer برسد، server ابتدا state محلی را از بین می‌برد و سمت stream را finish می‌کند؛ سپس، اگر line هنوز زنده باشد، سمت packet را هم finish می‌کند.

اگر به مدت ping-interval-ms چیزی دریافت نشود، یک ping frame فرستاده می‌شود. اگر این بی‌خبری بیشتر از no-recv-timeout-ms طول بکشد، line بسته خواهد شد.

اشتباه‌های رایج​

  • TcpOverUdpServer را از node stream-only قبلی تغذیه نکنید. سمت قبلی باید packet boundary را حفظ کند.
  • بدون TcpOverUdpClient متناظر استفاده نکنید.
  • FEC را فقط در یک سمت فعال نکنید.
  • تعداد FEC shard‌ها باید در هر دو سمت یکسان باشد.
  • TcpOverUdpServer را chain end در نظر نگیرید؛ payload stream بازسازی‌شده را به next می‌فرستد.

متادیتای node​

متادیتای برگرفته از کد منبع:

این node با kNodeFlagBlocksSplice استفاده از splice را برای کل chain صریحاً غیرفعال می‌کند. این پرچم حتی در صورت وجود هم‌زمان kNodeFlagSupportsSplice اولویت دارد.

ویژگیمقدار
node flagskNodeFlagBlocksSplice
can_have_prevtrue
can_have_nexttrue
layer_groupkNodeLayer4
layer_group_prev_nodekNodeLayer4
layer_group_next_nodekNodeLayer4
required_padding_left1 byte