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

HalfDuplexClient

HalfDuplexClient یک line عادی full-duplex را از node قبلی می‌گیرد و آن را به دو line خروجی جداگانه به سمت همان next تبدیل می‌کند:

  • یک line برای upload، یعنی انتقال payload از کلاینت به سرور
  • یک line برای download، یعنی انتقال payload از سرور به کلاینت

این node باید در سمت مقابل با HalfDuplexServer استفاده شود تا آن دو اتصال دوباره به یک اتصال منطقی عادی تبدیل شوند.

جایگاه رایج​

نمونه مستقیم داخل یک chain:

TesterClient -> HalfDuplexClient -> HalfDuplexServer -> TesterServer

نمونه روی TCP:

client side: TcpListener -> HalfDuplexClient -> TcpConnector
server side: TcpListener -> HalfDuplexServer -> TcpConnector

هر node stream که میان کلاینت و سرور قرار می‌گیرد باید بایت‌های دو half-connection را بدون تغییر عبور دهد. برای مثال اگر بعد از HalfDuplexClient یک TlsClient بگذارید، هر دو اتصال upload و download handshake جداگانه TLS خواهند داشت.

عملکرد​

هر اتصال ورودی به این node باعث ساخته شدن دو اتصال خروجی می‌شود. یعنی در عمل یک اتصال منطقی اصلی و دو اتصال در سمت transport داریم. با بسته شدن هرکدام از این مسیرها، مسیرهای جفت‌شده هم بسته می‌شوند.

این node socket باز نمی‌کند و packet line هم نمی‌سازد؛ فقط روی line‌های stream در وسط chain کار می‌کند.

نمونه تنظیم​

{
"name": "halfduplex-client",
"type": "HalfDuplexClient",
"settings": {},
"next": "outbound-transport"
}

فیلدهای لازم​

فیلدنوعتوضیح
namestringنام دلخواه و یکتای node.
typestringباید دقیقاً "HalfDuplexClient" باشد.
nextstringnode‌ای که هر دو half-connection خروجی را دریافت می‌کند.

این node باید هم node قبلی داشته باشد و هم next. برای ابتدا یا انتهای chain نیست.

تنظیمات​

در پیاده‌سازی فعلی هیچ تنظیم اختصاصی اجباری یا اختیاری ندارد.

هر دو اتصال upload و download به همان node‌ای می‌روند که در next آمده است. نسخه فعلی فیلد جداگانه‌ای مثل upload-next یا download-next ندارد.

Intro برای جفت کردن اتصال‌ها​

نخستین payload در مسیر upstream به‌شکل ویژه پردازش می‌شود. همراه همان payload، HalfDuplexClient با CSPRNG سریع یک شناسه تازه ۱۲۸ بیتی تولید می‌کند. intro دقیقاً ۱۷ بایت است: بایت صفر فرمان دقیق نقش و بایت‌های ۱ تا ۱۶ شناسه pair هستند:

Lineرفتار intro
upload lineintro هفده‌بایتی به ابتدای نخستین payload واقعی کاربر اضافه می‌شود.
download lineفقط intro هفده‌بایتی، بدون payload کاربر، ارسال می‌شود.

بعد از اولین payload:

  • همه payload‌های upstream فقط از upload line ارسال می‌شوند.
  • payload‌های downstream باید از download line برگردند.

این intro جزئیات داخلی پروتکل بین HalfDuplexClient و HalfDuplexServer است و تنظیم کاربر محسوب نمی‌شود. شناسه pair فقط metadata مسیریابی داخلی است و احراز هویت یا حفاظت transport را فراهم نمی‌کند؛ برای این ویژگی‌ها باید security node مناسب config شود.

رفتار جهت‌ها​

جهترفتار
upstream payload از node قبلیپس از introِ نخستین payload، روی upload line به next فرستاده می‌شود.
downstream payload از node بعدیبه main line اصلی و node قبلی برگردانده می‌شود.
upstream pause/resumeبه download line فرستاده می‌شود.
downstream pause/resumeاگر main line هنوز وجود داشته باشد، به آن برگردانده می‌شود.

upload line مسیر اصلی داده از کلاینت به سرور است و download line مسیر برگشت داده از سرور به کلاینت.

Lifecycle​

در upstream Init، node state مربوط به main line را مقداردهی می‌کند و دو line جدید روی همان worker می‌سازد:

  1. upload line
  2. download line

هر دو line از طریق next مقداردهی اولیه می‌شوند.

رویداد transport Est از هر کدام از child‌ها با latch همین node فقط یک بار به برنامه اعلام می‌شود. اگر Est همزمان داخل Init اولین child برسد، تنها تا پذیرش Init هر دو child منتظر می‌ماند؛ handshake جفت‌سازی مانع Est نیست. پس از این Init، payload معتبر پیش از Est هم پذیرفته می‌شود و callback آن می‌تواند اولین payload را ارسال یا جفت را ببندد.

فقط ورودی موقت لازم برای ترتیب Init و intro نگهداری می‌شود: سقف شامل برابری 2 MiB و 1,024 buffer است. پیش از callback وضعیت ارسال intro ثبت می‌شود؛ ورودی تو در تو هنگام intro دانلود، پشت intro آپلود قبلی و با همان شناسه جفت قرار می‌گیرد. ورودی پذیرفته‌شده می‌تواند پس از Pause تکمیل شود، ولی تخلیه بعدی backlog مربوط به Init، Pause تجمیعی child‌ها را رعایت می‌کند. Resume یک child دلیل Pause child دیگر را رفع نمی‌کند. Pause خواندن برنامه که پیش از ایجاد child دانلود برسد، پس از Init آن اعمال می‌شود، مگر اینکه Resume بعدی آن را لغو کند. پس از مقداردهی اولیهٔ این child، همهٔ Pause/Resume‌های خواندن برنامه، حتی اعلان‌های تکراری، مستقیماً به آن نگاشت می‌شوند. مسیر آماده داده، صف transport اضافی ندارد.

کلاینت در تمام طول عمر جفت فعال می‌ماند: نگاشت payload، فشار و Finish میان main line از نوع borrowed و child‌های متعلق به node با bypass دائمی روی همان line سازگار نیست. تجمیع فشار child‌ها و کنترل تخلیهٔ backlog مربوط به Init، مسئولیت‌های endpoint هستند؛ حتی وقتی این node در میانهٔ زنجیرهٔ پیکربندی‌شده قرار دارد.

پیش از هر Finish خروجی، لینک‌ها و state محلی جفت جدا می‌شوند. هر دو child متعلق به node به‌شکل همزمان و با نگهداری reference دقیق بسته می‌شوند؛ هیچ callback به سمت فرستنده Finish بازتاب نمی‌شود. main line از نوع borrowed از طریق مالک خودش بسته می‌شود و هیچ child جداشده‌ای منتظر task قابل لغو برای بسته‌شدن نمی‌ماند.

callback‌های UpStreamEst و DownStreamInit برای این node غیرفعال هستند.

محدودیت‌ها و Padding​

مقداراندازه
intro داخلی17 bytes (یک بایت نقش و یک شناسه تازه ۱۲۸ بیتی)
required_padding_left17 bytes

این node برای intro نخستین payload آپلود، ۱۷ بایت padding چپ رزرو می‌کند. اگر buffer معمولی فضای کافی داشته باشد، همان buffer استفاده می‌شود؛ ورودی splice یا buffer بدون فضای کافی به یک buffer معمولی جدید با padding کامل مسیر بعدی منتقل می‌شود. intro مربوط به download نیز در یک buffer معمولی جداگانه با padding کامل مسیر بعدی ارسال می‌شود.

نکته‌ها​

  • باید با HalfDuplexServer استفاده شود.
  • هر اتصال منطقی ورودی، دو اتصال خروجی می‌سازد.
  • intro تنها پس از رسیدن نخستین payloadِ upstream ارسال می‌شود.
  • در حال حاضر مسیرهای upload و download هر دو از همان next استفاده می‌کنند.

متادیتای نود​

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

ویژگیمقدار
node flagskNodeFlagSupportsSplice
can_have_prevtrue
can_have_nexttrue
layer_groupkNodeLayer4
layer_group_prev_nodekNodeLayer4
layer_group_next_nodekNodeLayer4
required_padding_left17 bytes

Splice و حافظهٔ مرحلهٔ آغازین​

اولین تحویل upstream، حتی تحویل خالی، دو intro هفده‌بایتی با شناسهٔ ۱۲۸ بیتی یکسان ایجاد می‌کند. intro آپلود و تمام payload اول در یک buffer معمولی ترکیب می‌شوند. buffer معمولی با فضای کافی برای header بدون کپی استفاده می‌شود؛ در صورت کمبود فضا یک buffer جدید ساخته می‌شود. ورودی splice کاملاً materialize می‌شود و prefix موجود در حافظه و بدنهٔ pipe به ترتیب کپی می‌شوند. intro دانلود نیز معمولی است. تحویل‌های بعدی در هر دو جهت نمایش معمولی یا splice خود را حفظ می‌کنند. صف موقت Init/intro می‌تواند wrapper‌های splice را نگه دارد. سقف‌های ۲ MiB و ۱٬۰۲۴ buffer، با مجاز بودن رسیدن به خود سقف، تغییر نکرده و محدودیت هر تحویل در مسیر آماده نیست. buffer‌های خروجی padding لازم برای node‌های بعدی را حفظ می‌کنند.

هر دو node پرچم kNodeFlagSupportsSplice دارند. کلاینت برای intro آپلود به ۱۷ بایت padding چپ نیاز دارد؛ padding موردنیاز سرور صفر است. خواندن splice تنها با پشتیبانی پلتفرم و build، فعال بودن misc.splice و پشتیبانی تمام node‌های chainِ گسترش‌یافته، از جمله wrapper سرور و همسایه‌ها، فعال می‌شود. buffer معمولی در همهٔ وضعیت‌ها پذیرفته می‌شود. تنظیم یا فیلد جدیدی به پروتکل اضافه نشده است و ترجیح دائمی برای خواندن معمولی ثبت نمی‌شود.