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 یکسان است.
| فیلد | پیشفرض | اعتبارسنجی | توضیح |
|---|---|---|---|
fec | false | boolean | فعالسازی Reed-Solomon forward error correction روی datagramهای KCP. |
fec-data-shards | 10 | مثبت، با مجموع shardها حداکثر 255 | تعداد data shard در هر FEC block. فقط وقتی fec فعال است استفاده میشود. |
fec-parity-shards | 3 | مثبت، با مجموع shardها حداکثر 255 | تعداد parity shard در هر FEC block. فقط وقتی fec فعال است استفاده میشود. |
kcp-nodelay | true | boolean | فعالسازی KCP nodelay mode. |
kcp-interval-ms | 10 | 10..5000 | فاصله زمانی بین updateهای KCP. |
kcp-resend | 2 | >= 0 | آستانه fast-resend در KCP. |
kcp-no-congestion-control | true | boolean | غیرفعال کردن congestion control در KCP. |
kcp-send-window | 8192 | >= 1 | send window بر حسب segment. |
kcp-recv-window | 8192 | >= 1 | receive window بر حسب segment. |
kcp-initial-cwnd | نصف kcp-send-window | 1..kcp-send-window | congestion window اولیه KCP. |
kcp-rx-minrto-ms | 30 | >= 1 | حداقل retransmission timeout. |
kcp-send-buffer-limit | 0 | >= 0 | آستانه backpressure. با مقدار 0، حد از مجموع local send window، remote window و 10 محاسبه میشود. |
ping-interval-ms | 10000 | >= 1 | اگر تا این مدت دادهای دریافت نشود، یک ping frame داخلی فرستاده میشود. |
no-recv-timeout-ms | 30000 | بزرگتر از 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 از نود قبلی میرسد:
- در صورت فعال بودن، FEC decoding اعمال میشود.
- KCP datagram را میگیرد.
- payloadهای کاملشده از KCP خوانده میشوند.
- flag یکبایتی داخلی حذف میشود.
- data frameها به شکل payload stream بازسازی میشوند و در جهت upstream به نود بعدی میروند.
وقتی payload stream از نود بعدی برمیگردد:
- payload به chunkهایی با حداکثر اندازه KCP write MTU موثر تقسیم میشود.
- هر chunk data flag یکبایتی prefix میگیرد.
- chunk فریمشده از طریق KCP ارسال میشود.
- datagramهای خروجی KCP در جهت downstream به سمت packet قبلی فرستاده میشوند.
flagهای داخلی
داخل payload KCP، TcpOverUdpServer از همان flagهای frame یکبایتی سمت client استفاده میکند:
| Flag | معنی |
|---|---|
0x00 | data frame. byteهای بعد از flag همان payload stream واقعی هستند. |
0xF0 | ping frame؛ نشانگر داخلی keepalive. |
0xFF | close 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 این مراحل را انجام میدهد:
- جهت upstream را بسته علامت میزند تا callbackهای re-entrant به سمتی که finish شده برنگردند
- یک close frame داخلی از طریق KCP میفرستد
- خروجی KCP را flush میکند
- per-line state محلی را از بین میبرد
- downstream
Finishرا به سمت packet میفرستد
اگر close frame از peer برسد، server ابتدا state محلی را از بین میبرد و سمت stream را finish میکند؛ سپس، اگر line هنوز زنده باشد، سمت packet را هم finish میکند.
اگر به مدت ping-interval-ms چیزی دریافت نشود، یک ping frame فرستاده میشود. اگر این بیخبری بیشتر از no-recv-timeout-ms طول بکشد، line بسته خواهد شد.
متادیتای نود
| ویژگی | مقدار |
|---|---|
| Node flag | kNodeFlagNone |
| نود قبلی | مجاز |
| نود بعدی | مجاز، در استفاده معمول لازم |
| layer group | kNodeLayerAnything |
required_padding_left | 1 |
اشتباههای رایج
TcpOverUdpServerرا از نود stream-only قبلی تغذیه نکنید. سمت قبلی باید packet boundary را حفظ کند.- بدون
TcpOverUdpClientمتناظر استفاده نکنید. - FEC را فقط در یک سمت فعال نکنید.
- تعداد FEC shardها باید در هر دو سمت یکسان باشد.
TcpOverUdpServerرا chain end در نظر نگیرید؛ payload stream بازسازیشده را بهnextمیفرستد.