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

TcpOverUdpServer

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

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

جایگاه رایج

سمت server:

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

سمت client متناظر:

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

نود قبلی TcpOverUdpServer باید مرز datagramها را حفظ کند؛ نود بعدی با 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 محاسبه می‌شود.
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 را به نود stream-side بعدی می‌فرستد.

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

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

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

  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. byteهای بعد از flag همان payload stream واقعی هستند.
0xF0ping frame؛ نشانگر داخلی keepalive.
0xFFclose frame؛ درخواست بستن line در peer.

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

MTU و padding

KCP MTU از GLOBAL_MTU_SIZE می‌آید. با فعال بودن FEC، 8 بایت header بیرونی FEC از KCP MTU و بودجه write stream کم می‌شود.

بودجه موثر payload برای write stream:

GLOBAL_MTU_SIZE - 20 - 8 - 24 - 1 - fec_overhead

نود مقدار required_padding_left = 1 را اعلام می‌کند تا بتواند پیش از تحویل داده به KCP، flag داخلی را به ابتدای آن اضافه کند.

رفتار 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

اگر حجم صف ارسال KCP از kcp-send-buffer-limit بالاتر برود، server ارسال upstream Pause به سمت stream بعدی را زمان‌بندی می‌کند. وقتی صف خروجی KCP دوباره از آستانه پایین‌تر بیاید، callback خروجی ارسال upstream Resume را زمان‌بندی می‌کند.

به این ترتیب، وقتی سمت packet نمی‌تواند با سرعت کافی داده را مصرف کند، صف بی‌نهایت رشد نمی‌کند.

رفتار 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 بسته خواهد شد.

متادیتای نود

ویژگیمقدار
Node flagkNodeFlagNone
نود قبلیمجاز
نود بعدیمجاز، در استفاده معمول لازم
layer groupkNodeLayerAnything
required_padding_left1

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

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