ConnectionFisherClient
ConnectionFisherClient بخش کلاینت ConnectionFisherServer است. برای هر line ورودی، چند child line را همزمان به سمت next باز میکند و probe پنجبایتی FISH? را روی هرکدام میفرستد. نخستین child line که پاسخ معتبر FISH! بگیرد انتخاب میشود و بقیه بسته میشوند.
این نود زمانی کاربرد دارد که مسیر خروجی پایدار نیست یا میخواهید چند اتصال را همزمان امتحان کنید و اولین مسیری را برگزینید که رسیدن به ConnectionFisherServer متناظر را تأیید میکند.
جایگاه رایج
طرحبندی ساده TCP:
TcpListener -> ConnectionFisherClient -> TcpConnector
TcpListener -> ConnectionFisherServer -> TcpConnector
همراه با تونلهای stream دیگر در میانه مسیر:
TesterClient -> ConnectionFisherClient -> MuxClient -> MuxServer -> ConnectionFisherServer -> TesterServer
کلاینت و سرور باید دو سر یک مسیر منطقی stream باشند. هر تونل میانی باید بایتهای ثابت probe را بدون تغییر عبور دهد.
این نود چه میکند؟
- مقدار
settings.simultaneous-tries-perlineرا میخواند. - با هر
Initدر جهت upstream روی main line، به تعداد تنظیمشده child line داخلی میسازد. - upstream
Initرا روی هر child line بهnextمیفرستد. - probe ثابت
FISH?را روی هر child line میفرستد. - منتظر پاسخ ثابت
FISH!میماند. - تا زمان انتخاب یک child، payloadِ upstream روی main line را در صف نگه میدارد.
- اولین child که پاسخ معتبر دریافت کند را انتخاب میکند.
- همه child lineهای انتخابنشده را میبندد.
- payloadهای upstream صفشده را به ترتیب برای child انتخابشده میفرستد.
- payloadهای downstream از child انتخابشده را به main line برمیگرداند.
ConnectionFisherClient یک تونل میانی stream است. مستقیماً socket یا packet line نمیسازد.
نمونه تنظیم
{
"name": "fisher-client",
"type": "ConnectionFisherClient",
"settings": {
"simultaneous-tries-perline": 3
},
"next": "tcp-out"
}
سمت سرور متناظر:
{
"name": "fisher-server",
"type": "ConnectionFisherServer",
"next": "service"
}
فیلدهای اجباری
فیلدهای سطح اصلی:
| فیلد | نوع | توضیح |
|---|---|---|
name | string | نام یکتای نود در پیکربندی. |
type | string | باید دقیقاً "ConnectionFisherClient" باشد. |
next | string | اجباری. نود stream بعدی که child lineهای نامزد را میگیرد. |
ConnectionFisherClient باید نود قبلی نیز داشته باشد؛ این نود ابتدا یا انتهای chain نیست.
تنظیمات اختیاری
| گزینه | نوع | پیشفرض | توضیح |
|---|---|---|---|
simultaneous-tries-perline | integer >= 1 | 2 | تعداد child lineهای نامزد که برای هر main line ورودی ساخته میشوند. |
settings میتواند حذف شود. مقادیر کمتر از 1 رد میشوند.
تبادل Probe
Probe مقدار ثابتی دارد و secret قابل تنظیمی برای آن وجود ندارد:
| جهت | Bytes | معنی |
|---|---|---|
| کلاینت به سرور | FISH? | بلافاصله پس از Init در upstream، روی هر child line فرستاده میشود. |
| سرور به کلاینت | FISH! | پاسخ الزامی ConnectionFisherServer. |
این markerها فقط نشان میدهند که child line به ConnectionFisherServer متناظر رسیده است؛ هیچ احراز هویت، رمزنگاری یا تضمین صحتی برای ترافیک فراهم نمیکنند.
مدل Line
ConnectionFisherClient از دو نوع line استفاده میکند:
| Line | هدف |
|---|---|
| main line | line ورودی اصلی از نود قبلی که در سمت previous باقی میماند. |
| child lines | lineهای نامزد داخلی که کلاینت میسازد و فقط به سمت next میروند. |
Main line هیچگاه مستقیم به next فرستاده نمیشود و child lineها نیز هرگز به سمت prev نمیروند. پس از انتخاب یک child، تونل payload و callbackهای Est، Pause، Resume و Finish را بهصورت دستی میان main line و child منتخب جابهجا میکند.
جریان انتخاب
برای هر upstream Init روی main line:
- state مربوط به main line مقداردهی اولیه میشود.
simultaneous-tries-perlinechild line روی همان worker ساخته میشود.- هر child line به سمت
nextمقداردهی اولیه میشود. - هر child،
FISH?میفرستد. - برای main line یک timeout انتخاب به مدت
5000 msزمانبندی میشود. - تا مشخص شدن child برنده، payloadِ upstream روی main line در صف میماند.
- اولین child که
FISH!دریافت کند، child انتخابشده میشود. - child lineهای بازنده بسته میشوند.
- payloadهای upstream صفشده برای child منتخب فرستاده میشوند.
- بایتهای اضافه downstream که پس از
FISH!از قبل بافر شدهاند، بهعنوان payloadِ downstream به نود قبلی فرستاده میشوند.
اگر هیچ child پیش از timeout معتبر شناخته نشود، main line بسته میشود.
محدودیتها و Buffer
| محدودیت | مقدار | رفتار |
|---|---|---|
| Selection timeout | 5000 ms | اگر childای انتخاب نشود، main line بسته میشود. |
| Pending upstream payload | 1 MiB | اگر payload صفشده از این حد بگذرد، main line بسته میشود. |
| Child handshake buffer | 4096 bytes | اگر یک child انتخابنشده بیش از حد داده handshake جمع کند، main line بسته میشود. |
required_padding_left | 0 | نود header پروتکل را به بافرهای موجود اضافه نمیکند. |
برای probeِ FISH? یک بافر کوچک جداگانه گرفته میشود. Payload کاربر در همان بافرهای WaterWall در صف میماند یا ارسال میشود.
رفتار Lifecycle
قبل از انتخاب:
- payload upstream از main line در صف قرار میگیرد
- payload downstream از child بهعنوان داده handshake در نظر گرفته میشود
Estدر downstream، child را برقرارشده علامت میزند؛ اما تنها وقتی به main line فرستاده میشود که آن child از قبل انتخاب شده باشد.- pause و resume تا زمان انتخاب child، به main line منعکس نمیشوند
بعد از انتخاب:
- payloadِ upstream روی main line با
tunnelNextUpStreamPayloadبه child منتخب فرستاده میشود. - payloadِ downstream روی child منتخب با
tunnelPrevDownStreamPayloadبه نود قبلی میرود. Estدر downstream از child منتخب تنها یک بار به نود قبلی فرستاده میشود.- Pause و resume فقط میان child منتخب و main line جابهجا میشوند.
اگر child منتخب بسته شود، main line نیز بسته میشود. اگر همه childها پیش از انتخاب برنده بسته شوند، main line هم بسته خواهد شد.
نکتهها
- با
ConnectionFisherServerمتناظر استفاده کنید. - برای chainهای stream طراحی شده است، نه packet-line.
- افزایش
simultaneous-tries-perlineتعداد و بار اتصالها را نیز بالا میبرد. - Probe ثابت و عمومی است؛ در مسیرهایی که به امنیت نیاز دارند از TLS یا نودهای رمزنگاری، احراز هویت و مسیریابی استفاده کنید.
- upstream
Estو downstreamInitبرای این نود غیرفعال هستند.