تصویر مقاله
عکس از Zhen H در Unsplash

در بسیاری از لایه‌های ارتباطی، مفهومی به نام داده‌های خارج از باند (out-of-band data) وجود دارد که با نام داده‌های اضطراری (expedited data) نیز شناخته می‌شود. این سازوکار روشی است برای اینکه در صورت وقوع یک رخداد مهم در یک سمت ارتباط، بتوان موضوع را با فوریت به طرف مقابل اطلاع داد.

این داده‌ها باید سریع‌تر از داده‌های عادیِ درون‌باندی (in-band) که در صف ارسال منتظرند منتقل شوند و حتی مواردی مانند کنترل جریان (flow control) نیز نباید مانع ارسال آن‌ها شود.

از آنجا که نحوه پیاده‌سازی داده‌های خارج از باند در لایه‌های انتقال مختلف با یکدیگر تفاوت دارد، در این بخش تمرکز خود را منحصراً روی پروتکل TCP قرار می‌دهیم.

داده‌های خارج از باند در TCP

لایه انتقال TCP قابلیت داده‌های خارج از باند را در قالب «حالت اضطراری» (urgent mode) ارائه می‌دهد.

تصویر مقاله
C

اشاره‌گر اضطراری (urgent pointer): مقدار این اشاره‌گر، یک واحد بزرگ‌تر از شماره توالی (sequence number) تک‌بایتی است که با فلگ MSG_OOB ارسال شده است.

سگمنت بعدی که توسط TCP ارسال می‌شود، فلگ URG را در هدر TCP به همراه خواهد داشت. آفست اضطراری (urgent offset) در هدر TCP، به بایتِ بعد از بایتِ خارج از باند (out-of-band byte) اشاره می‌کند.

ویژگی‌های مهم حالت اضطراری TCP

هدر TCP نشان می‌دهد که فرستنده وارد حالت اضطراری شده است، اما نیازی به ارسال حتمیِ داده اضطراری واقعی نیست. به بیان دیگر، حتی اگر ارسال داده‌های TCP به دلیل کنترل جریان (flow control) متوقف شده باشد، اعلان اضطراری (urgent notification) همچنان به مقصد تحویل داده می‌شود.

اگر مانند مثال زیر چند بایت به عنوان داده خارج از باند ارسال شود، آخرین بایت یعنی c به عنوان بایتِ خارج از باند در نظر گرفته خواهد شد و اشاره‌گر اضطراری نیز به بایت بعد از آن اشاره می‌کند.

C

نحوه پردازش در سمت گیرنده داده‌های خارج از باند

  1. هنگامی که TCP سگمنتی با فلگ فعال URG دریافت می‌کند، ابتدا بررسی می‌کند که آیا مقدار اشاره‌گر اضطراری نشان‌دهنده داده‌های جدید خارج از باند است یا خیر. علت این بررسی آن است که معمولاً در یک بازه زمانی کوتاه، چندین سگمنت TCP با فلگ URG تنظیم می‌شوند که همگی به یک داده خارج از باند اشاره می‌کنند. هدف از این کار این است که تنها اولین سگمنت به عنوان رویداد داده جدید خارج از باند تشخیص داده شود.
  2. سیگنال SIGURG به مالک سوکت ارسال می‌شود (با این فرض که مالک سوکت قبلاً از طریق fcntl یا ioctl مشخص شده باشد). همچنین اگر پردازه‌ای با استفاده از select در انتظار شرایط استثنایی (exception condition) باشد، این تابع بلافاصله برمی‌گردد. این دو اعلان، با دریافت اشاره‌گر اضطراری جدید اجرا می‌شوند؛ صرف‌نظر از اینکه داده واقعی خارج از باند رسیده باشد یا خیر. نشانگر OOB منحصر‌به‌فرد است؛ بنابراین اگر قبل از خوانده شدن بایت قبلی OOB، یک بایت جدید OOB دریافت شود، بایت قبلی نادیده گرفته شده و از بین می‌رود.
  3. پس از دریافت بایت واقعی OOB، این داده می‌تواند به‌صورت درون‌خطی (inline) ذخیره شود یا به عنوان داده خارج از باند استخراج گردد. به‌طور پیش‌فرض، گزینه سوکت SO_OOBINLINE غیرفعال است؛ در نتیجه داده OOB درون بافر دریافتِ معمولی سوکت قرار نمی‌گیرد، بلکه در یک بافر مجزا و تک‌بایتی مخصوص داده‌های خارج از باند جای می‌گیرد. تنها روش دریافت این داده خارج از باند، فراخوانی توابع recv ،recvfrom یا recvmsg با تعیین فلگ MSG_OOB است. اگر قبل از خوانده شدن بایت قبلی OOB، بایت جدیدی برسد، داده قبلی حذف می‌شود. در مقابل، با فعال کردن گزینه SO_OOBINLINE، داده OOB در بافر دریافت معمولی سوکت باقی می‌ماند و پردازه می‌تواند با بررسی نشانگر خارج از باند (out-of-band mark)، متوجه دریافت آن شود.

یک مثال ساده با استفاده از SIGURG

هدف از دستور pause این است که هر فراخوانی write یا send بتواند یک سگمنت مجزای TCP را ارسال کرده و دریافت نماید.

Kotlin
C
C++

دلیل ثبت هندلر سیگنال SIGURG پس از فراخوانی Accept این است که متغیر connfd مورد استفاده در sig_urg باید پیش از آن مقداردهی شده باشد.

در تابع sig_urg خواندن ۱۰۰ بایت درخواست می‌شود، اما در عمل تنها ۱ بایت خوانده خواهد شد.

یک مثال ساده با استفاده از select

پیاده‌سازی سمت گیرنده با استفاده از تابع select به‌جای استفاده از هندلر سیگنال SIGURG

Lua
C++

نسخه اصلاح‌شده select

C++

تابع sockatmark

هنگام دریافت داده خارج از باند، یک نشانگر خارج از باند (out-of-band mark) ثبت می‌شود که موقعیت داده را مشخص می‌کند. بررسی این موقعیت از طریق فراخوانی تابع sockatmark امکان‌پذیر است.

C++
  1. در صورت فعال بودن گزینه SO_OOBINLINE: اگر بایت بعدی برای خواندن، داده OOB باشد، خروجی sockatmark برابر با true خواهد بود. در صورت غیرفعال بودن گزینه SO_OOBINLINE: اگر بایت بعدی برای خواندن، دقیقاً بایت بعد از داده OOB باشد، مقدار true برگردانده می‌شود.
  2. عملیات خواندن همواره در محل نشانگر خارج از باند متوقف می‌شود. به عنوان مثال، اگر ۱۰۰ بایت در بافر دریافت سوکت وجود داشته باشد اما تنها ۵ بایت پیش از نشانگر قرار گرفته باشد، درخواست خواندن ۱۰۰ بایت فقط ۵ بایت را برمی‌گرداند. این سازوکار به پردازه گیرنده امکان می‌دهد با فراخوانی sockatmark بررسی کند که آیا اشاره‌گر بافر به محل نشانگر رسیده است یا خیر.

مثالی از خواندن داده‌های OOB با استفاده از sockatmark

C++
C++
Terminal / Bash

مثالی از ارسال اعلان اضطراری در شرایطی که انتقال داده به دلیل کنترل جریان متوقف شده است

C++
Kotlin
Go
C++

مثالی از ارسال سریع دو بایت خارج از باند

تنظیم ارسال سریع داده‌ها به گونه‌ای که نشانگر OOB قبلی بازنویسی شده و از بین برود

C++
Terminal / Bash

در هر اتصال TCP، در هر لحظه حداکثر یک نشانگر OOB می‌تواند وجود داشته باشد.

جمع‌بندی

پروتکل TCP با استفاده از اشاره‌گر اضطراری (urgent pointer) در هدر خود، ورود فرستنده به حالت اضطراری را به گیرنده اطلاع می‌دهد. این وضعیت به پردازه مربوطه اعلان شده و اشاره‌گر به بایت داده اضطراری اشاره می‌کند؛ با این حال، تمامی داده‌ها همچنان پیرو سازوکار عمومی کنترل جریان در TCP هستند.

در Socket API، حالت اضطراری پروتکل TCP به عنوان داده‌های خارج از باند (Out-of-Band Data) نگاشت و مدیریت می‌شود.

امروزه داده‌های خارج از باند کاربرد گسترده‌ای ندارند و بیشتر در برنامه‌های سنتی‌تر مانند telnet ،rlogin و FTP دیده می‌شوند.

مطالعه بیشتر