TcpConnector
TcpConnector adapter خروجی TCP است. line را از نود قبلی میگیرد، مقصد را از تنظیمات نود یا routing context انتخاب میکند، اتصال TCP را باز میکند و payloadها را میان chain و server راه دور جابهجا میسازد.
جایگاه رایج:
TcpListener -> ... -> TcpConnector -> سرویس TCP راه دور
این نود معمولاً انتهای یک stream chain است.
قابلیتها
- انتخاب آدرس و پورت مقصد برای هر line.
- resolve کردن نام domain از طریق یک
DomainResolverداخلی و async. - باز کردن یک socket TCP خروجی.
- نوشتن payloadهای upstream از نود قبلی روی socket راه دور.
- فرستادن دادههای خواندهشده از socket راه دور بهعنوان payload downstream به نود قبلی.
- فرستادن downstream
Estپس از برقراری موفق اتصال TCP خروجی. - اعمال گزینههای socket مانند
TCP_NODELAY،TCP_FASTOPEN، اندازه bufferها،SO_MARK، device binding و source-IP binding در صورت پشتیبانی platform.
TcpConnector در انتهای chain قرار میگیرد، با upstream Init راه میافتد و به نود next نیاز ندارد.
نمونه تنظیم
{
"name": "outbound-tcp",
"type": "TcpConnector",
"settings": {
"address": "example.com",
"port": 443,
"nodelay": true,
"fastopen": false,
"large-send-buffer": true,
"large-recv-buffer": 4194304,
"fwmark": 10,
"interface": "eth0",
"source-ip": "192.0.2.10",
"domain-strategy": "prefer-ipv4"
}
}
نمونه چند مقصد وزندار
{
"name": "outbound-tcp",
"type": "TcpConnector",
"settings": {
"addresses": [
{
"address": "192.0.2.10",
"port": 443,
"weight": 5,
"nodelay": true,
"interface": "eth0",
"domain-strategy": "prefer-ipv4"
},
{
"address": "example.org",
"port": "dest_context->port",
"weight": 1,
"source-ip": "198.51.100.10",
"domain-strategy": "only-ipv4"
},
{
"address": "2001:db8:1::/64",
"port": 443,
"weight": 1,
"interface": null,
"source-ip": null,
"domain-strategy": "only-ipv6"
}
],
"nodelay": true,
"fastopen": false,
"large-send-buffer": true,
"large-recv-buffer": true,
"interface": "eth0",
"domain-strategy": "prefer-ipv4"
}
}
برای هر line جدید، یکی از objectهای addresses با احتمالی متناسب با weight آن انتخاب میشود. گزینههایی که در object مقصد نوشته نشدهاند از سطح بالای settings به ارث میرسند. برای interface و source-ip میتوانید با مقدار null، مقدار بهارثرسیده را فقط برای همان مقصد غیرفعال کنید.
فیلدهای ضروری
فیلدهای سطح بالا:
| فیلد | نوع | توضیح |
|---|---|---|
name | string | نام دلخواه نود. باید داخل فایل config یکتا باشد. |
type | string | باید دقیقاً "TcpConnector" باشد. |
settings | object | تنظیمات مقصد و socket. |
settings باید دقیقاً از یک سبک مقصد استفاده کند:
| سبک | فیلدهای لازم | توضیح |
|---|---|---|
| مقصد تکی | address و port | هر line از همان قانون مقصد استفاده میکند. |
| مقصدهای وزندار | addresses | برای هر line یک مقصد بر اساس وزن انتخاب میشود. |
addresses را با address یا port top-level ترکیب نکنید.
انتخاب مقصد
مقصد تکی
از address و port مستقیماً زیر settings استفاده کنید.
{
"settings": {
"address": "93.184.216.34",
"port": 443
}
}
مقادیر معتبر برای address:
| مقدار | رفتار |
|---|---|
| string IPv4 | اتصال به آن آدرس IPv4. |
| string IPv6 | اتصال به آن آدرس IPv6. |
| string domain | resolve کردن domain پیش از اتصال. |
"src_context->address" | استفاده از آدرس source در routing context مربوط به line. |
"dest_context->address" | استفاده از آدرس destination در routing context مربوط به line. |
مقادیر معتبر برای port:
| مقدار | رفتار |
|---|---|
| عدد | اتصال به آن پورت ثابت. باید 1 تا 65535 باشد. |
"src_context->port" | استفاده از پورت source در routing context مربوط به line. |
"dest_context->port" | استفاده از پورت destination در routing context مربوط به line. |
random(x,y) در parser فعلی برای TcpConnector پشتیبانی نمیشود.
مقصدهای weighted
برای انتخاب یک مقصد وزندار بهازای هر line از addresses استفاده کنید.
{
"settings": {
"addresses": [
{
"address": "192.0.2.10",
"port": 443,
"weight": 3
},
{
"address": "198.51.100.20",
"port": 443,
"weight": 1
}
]
}
}
هر object باید شامل باشد:
| فیلد | نوع | توضیح |
|---|---|---|
address | string | همان فرمهای معتبر address در مقصد تکی. |
port | عدد یا string خاص | همان فرمهای معتبر port در مقصد تکی. |
weight | عدد صحیح مثبت | وزن نسبی این مقصد هنگام انتخاب. |
هر object میتواند گزینههای زیر را هم برای همان مقصد تغییر دهد:
| گزینه |
|---|
nodelay |
fastopen |
large-send-buffer |
large-recv-buffer |
fwmark |
interface |
source-ip |
domain-strategy |
parser برای سازگاری کلید قدیمی و غلطنویسیشده adresses را هم میپذیرد، اما در config جدید از addresses استفاده کنید و هر دو نام را همزمان ننویسید.
تنظیمات اختیاری
| گزینه | نوع | پیشفرض | توضیح |
|---|---|---|---|
nodelay | boolean | true | فعالسازی TCP_NODELAY روی socketهای خروجی. |
fastopen | boolean | false | در صورت پشتیبانی platform، TCP_FASTOPEN را درخواست میکند. |
large-send-buffer | boolean یا عدد مثبت | false، یا true اگر mux وجود داشته باشد و حذف شده باشد | تنظیم SO_SNDBUF. |
large-recv-buffer | boolean یا عدد مثبت | false، یا true اگر mux وجود داشته باشد و حذف شده باشد | تنظیم SO_RCVBUF. |
fwmark | integer | تنظیم نشده | اعمال socket mark به سبک Linux از طریق SO_MARK در صورت پشتیبانی. |
interface | string | تنظیم نشده | محدود کردن socketهای خروجی به یک network device محلی در صورت پشتیبانی. |
source-ip | string | تنظیم نشده | socket خروجی را با یک source port موقت به source IP محلی مشخص bind میکند. |
domain-strategy | string یا integer | dns.domain-strategy در core | نحوه انتخاب نتایج DNS برای مقصدهای domain. |
هنگام استفاده از addresses میتوانید این فیلدها را در سطح بالای settings بهعنوان پیشفرض بنویسید؛ هر object مقصد میتواند مقدار خودش را جایگزین کند.
گزینههای socket buffer
large-send-buffer و large-recv-buffer مقادیر زیر را میپذیرند:
| مقدار | معنا |
|---|---|
true | استفاده از buffer بزرگ پیشفرض WaterWall برای socket؛ در حال حاضر 4194304 بایت. |
false | تنظیم نکردن صریح اندازه buffer و استفاده از مقدار پیشفرض kernel. |
| عدد مثبت | درخواست مستقیم آن تعداد بایت. |
اگر یکی از این گزینهها را ننویسید و chain نهایی شامل MuxClient یا MuxServer باشد، TcpConnector بهطور خودکار buffer بزرگ پیشفرض را برای همان جهت فعال میکند. مقدار صریح false همچنان مانع تنظیم اندازه buffer میشود.
domain strategy
domain-strategy مشخص میکند وقتی DNS هم IPv4 و هم IPv6 برمیگرداند، کدام آدرس برای مقصد domain انتخاب شود.
اگر این گزینه را ننویسید، مقدار dns.domain-strategy در core به کار میرود. در نبود آن هم WaterWall از "prefer-ipv4" استفاده میکند.
مقادیر معتبر string:
| مقدار | رفتار |
|---|---|
"prefer-ipv4" | نخستین نتیجه IPv4 را انتخاب میکند و اگر وجود نداشت به IPv6 برمیگردد. |
"prefer-ipv6" | نخستین نتیجه IPv6 را انتخاب میکند و اگر وجود نداشت به IPv4 برمیگردد. |
"only-ipv4" | فقط نتیجه IPv4 را میپذیرد؛ نبودن IPv4 یعنی resolution برای آن line قابل استفاده نیست. |
"only-ipv6" | فقط نتیجه IPv6 را میپذیرد؛ نبودن IPv6 یعنی resolution برای آن line قابل استفاده نیست. |
"accept-dns-returned-order" | نخستین آدرس قابل استفاده در ترتیب بازگشتی resolver را انتخاب میکند. |
مقادیر integer قدیمی هم پذیرفته میشوند:
| مقدار | استراتژی |
|---|---|
0 | accept-dns-returned-order |
1 | prefer-ipv4 |
2 | prefer-ipv6 |
3 | only-ipv4 |
4 | only-ipv6 |
در addresses وزندار، هر object میتواند domain-strategy مخصوص lineهایی را تعریف کند که همان مقصد را انتخاب کردهاند.
interface، source IP و egress pinning
interface socket خروجی را به یک device محلی محدود میکند.
در Linux، WaterWall در صورت امکان از SO_BINDTODEVICE استفاده میکند. روی platformهای بدون device binding، socket به آدرس IPv4 مربوط به interface bind میشود. اگر آدرسی پیدا نشود، راهاندازی اتصال شکست میخورد.
source-ip socket خروجی را قبل از connect به یک source IP محلی مشخص bind میکند. خانواده آدرس باید با خانواده مقصد مطابقت داشته باشد.
اگر loop protection در TunDevice یک egress pin خودکار ایجاد کرده باشد و interface را ننوشته باشید، TcpConnector از همان pin استفاده میکند. source-ip بهتنهایی آن interface را تغییر نمیدهد؛ یا source IP را از همان interface انتخاب کنید، یا interface را صریحاً بنویسید.
نمونههای رایج
مقصد راه دور ثابت
{
"name": "out",
"type": "TcpConnector",
"settings": {
"address": "example.com",
"port": 443,
"domain-strategy": "prefer-ipv4"
}
}
اتصال به destination context موجود
{
"name": "context-out",
"type": "TcpConnector",
"settings": {
"address": "dest_context->address",
"port": "dest_context->port"
}
}
این حالت پس از نودهایی کاربرد دارد که metadata مقصد را میخوانند، بازنویسی یا مسیریابی میکنند.
bind به یک آدرس محلی مشخص
{
"name": "egress-ip-a",
"type": "TcpConnector",
"settings": {
"address": "203.0.113.10",
"port": 443,
"source-ip": "192.0.2.10"
}
}
استخر egress weighted
{
"name": "egress-pool",
"type": "TcpConnector",
"settings": {
"addresses": [
{
"address": "edge-a.example.net",
"port": 443,
"weight": 4,
"interface": "eth0"
},
{
"address": "edge-b.example.net",
"port": 443,
"weight": 1,
"interface": "eth1"
}
],
"domain-strategy": "prefer-ipv4"
}
}
رفتار chain
هنگام upstream Init، TcpConnector state مربوط به line را مقداردهی میکند، قانون مقصد را برمیگزیند، address و port را در destination context میگذارد و domain strategy را تنظیم میکند.
جریان داخلی:
- تنظیمات مقصد شامل
address،port، گزینههای socket و اطلاعات اختیاری CIDR randomization انتخاب میشوند. DomainResolverداخلی مقصد را در صورت domain بودن resolve میکند.- پس از تبدیل مقصد به IP و port،
TcpConnectorsocket خروجی را میسازد و اتصال async را آغاز میکند. - با موفقیت اتصال، downstream
Estبه نود قبلی فرستاده میشود. - payloadهای upstream روی socket نوشته میشوند و دادههای خواندهشده از socket به payload downstream تبدیل میشوند.
اگر آمادهسازی مقصد، DNS resolution، ساخت socket یا اتصال ناموفق باشد، line در جهت downstream و به سمت نود قبلی finish میشود.
domain resolution
resolve کردن domain بهشکل async انجام میشود. TcpConnector یک DomainResolver داخلی میسازد و پیش از resolution، domain-strategy انتخابشده را در destination context ذخیره میکند.
payloadهای رسیده در زمان انتظار برای DNS نزد resolver میمانند. payloadهایی که پس از موفقیت DNS و پیش از برقراری TCP میرسند نیز تا آماده شدن socket در TcpConnector صف میشوند.
CIDR randomization روی IPهای ثابت
یک address ثابت IP میتواند پسوند CIDR داشته باشد. در این حالت TcpConnector پیش از اتصال، host part را بهصورت تصادفی انتخاب میکند.
{
"settings": {
"address": "198.51.100.0/24",
"port": 443
}
}
{
"settings": {
"address": "2001:db8:1::/64",
"port": 443
}
}
پسوندهای CIDR فقط روی آدرسهای IP ثابت معتبر هستند. روی domainها، "src_context->address" یا "dest_context->address" معتبر نیستند.
قوانین پیادهسازی:
| خانواده آدرس | طول prefix معتبر | توضیح |
|---|---|---|
| IPv4 | 0 تا 32 | /32 مثل یک آدرس ثابت رفتار میکند. prefixهای بالاتر از /32 رد میشوند. |
| IPv6 | 64 تا 128 | /64 شصتوچهار بیت پایینی را تصادفی میکند؛ prefixهای کوتاهتر از /64 پذیرفته نمیشوند. |
اگر بازه محاسبهشده صفر باشد، مقصد مثل یک آدرس ثابت رفتار میکند.
کنترل جریان و buffering
در حالی که DNS، connect یا socket writeها در انتظار هستند، TcpConnector ممکن است payloadهای upstream را صف کند.
آستانههای فعلی:
| وضعیت صف | رفتار |
|---|---|
بیشتر از 1 KB در صف | نود قبلی pause میشود. |
| تکمیل writeهای pending | نود قبلی resume میشود. |
بیشتر از 16 MB در صف | اتصال بسته و line finish میشود. |
این آستانهها نمیگذارند هنگام کند بودن server راه دور یا آماده نبودن اتصال، مصرف حافظه بیحد رشد کند.
idle timeout
هر اتصال خروجی در جدول idle همان worker دنبال میشود. timeout فعلی نزدیک به 300 ثانیه است و هر read یا write آن را تمدید میکند.
اگر اتصال منقضی شود، socket بسته میشود و downstream finish به نود قبلی فرستاده میشود.
نکات و هشدارها
TcpConnectorیک chain end خروجی است و بهnextنیاز ندارد.- DNS resolution async است و از
domain-strategyانتخابشده استفاده میکند. fwmark،TCP_FASTOPENو device binding به platform وابسته هستند.fwmarkروی Windows در دسترس نیست.source-ipباید یک آدرس IP معتبر باشد و باید با خانواده آدرس مقصد مطابقت داشته باشد.- کلید قدیمی
adressesبرای سازگاری پذیرفته میشود، اماaddressesنامگذاری مستندشده است.