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

SpeedTestClient

SpeedTestClient یک tunnel مصنوعی برای benchmark است که در ابتدای chain قرار می‌گیرد. این node line‌های معمول WaterWall را می‌سازد، ترافیک frameشده speed-test را به tunnel بعدی می‌فرستد، frame‌های برگشتی از SpeedTestServer را اعتبارسنجی می‌کند و گزارش‌های دوره‌ای و نهایی throughput را چاپ می‌کند.

این node برای اندازه‌گیری کارایی یک chain یا مسیر transport در WaterWall است. خودش socket باز نمی‌کند، application proxy نیست و packet-line tunnel هم محسوب نمی‌شود.

جایگاه رایج​

SpeedTestClient باید نخستین node chain باشد؛ line‌های آزمایش را خودش تولید می‌کند و آن‌ها را به transport stack تنظیم‌شده می‌فرستد.

SpeedTestClient -> TcpConnector
SpeedTestClient -> UdpConnector
SpeedTestClient -> TlsClient -> TcpConnector
SpeedTestClient -> EncryptionClient -> TcpConnector

سمت remote معمولاً باید به SpeedTestServer متناظر ختم شود:

TcpListener -> SpeedTestServer
UdpListener -> SpeedTestServer
TcpListener -> TlsServer -> SpeedTestServer
TcpListener -> EncryptionServer -> SpeedTestServer

نمونه TCP​

سمت client:

[
{
"name": "speedtest-client",
"type": "SpeedTestClient",
"settings": {
"mode": "tcp",
"direction": "bidirectional",
"duration-ms": 10000,
"warmup-ms": 1000,
"report-interval-ms": 1000,
"connection-count": 4,
"payload-size": 131072,
"terminate-on-complete": true
},
"next": "speedtest-out"
},
{
"name": "speedtest-out",
"type": "TcpConnector",
"settings": {
"address": "198.51.100.10",
"port": 9000,
"nodelay": true
}
}
]

سمت server:

[
{
"name": "speedtest-listener",
"type": "TcpListener",
"settings": {
"address": "0.0.0.0",
"port": 9000,
"nodelay": true
},
"next": "speedtest-server"
},
{
"name": "speedtest-server",
"type": "SpeedTestServer",
"settings": {
"report-interval-ms": 1000,
"json-summary": true
}
}
]

نمونه UDP​

سمت client:

[
{
"name": "speedtest-client",
"type": "SpeedTestClient",
"settings": {
"mode": "udp",
"direction": "upload",
"duration-ms": 10000,
"warmup-ms": 1000,
"payload-size": 3800,
"udp-target-bits-per-sec": 10000000,
"terminate-on-complete": true
},
"next": "speedtest-udp-out"
},
{
"name": "speedtest-udp-out",
"type": "UdpConnector",
"settings": {
"address": "198.51.100.10",
"port": 9001
}
}
]

سمت server:

[
{
"name": "speedtest-udp-listener",
"type": "UdpListener",
"settings": {
"address": "0.0.0.0",
"port": 9001
},
"next": "speedtest-server"
},
{
"name": "speedtest-server",
"type": "SpeedTestServer"
}
]

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

وجود next برای SpeedTestClient الزامی است. node بعدی همان مسیری است که ترافیک آزمایشی تولیدشده را دریافت می‌کند.

settings در عمل اختیاری است و در صورت حذف شدن، مقدارهای پیش‌فرض به کار می‌روند. برای انتخاب mode، مدت، جهت، اندازه payload، تعداد stream، نرخ pacing یا قالب خروجی مشخص، آن را اضافه کنید.

تنظیمات اختیاری​

فیلدپیش‌فرضتوضیح
modetcpmode framing. مقدارهای مجاز: tcp, TCP, udp, UDP.
directionuploadجهت تست. مقدارهای مجاز: upload, UPLOAD, send, download, DOWNLOAD, receive, bidirectional, both, BOTH.
duration-ms10000مدت اندازه‌گیری پس از warmup؛ باید مثبت باشد.
warmup-ms0مدت warmup؛ باید منفی نباشد. frame‌های warmup ارسال و در صورت فعال بودن گزینه اعتبارسنجی می‌شوند، اما در مجموع بایت‌های اندازه‌گیری‌شده حساب نمی‌شوند.
report-interval-ms1000فاصله میان گزارش‌های دوره‌ای؛ باید مثبت باشد.
start-delay-ms50تأخیر آغاز هر stream پس از startup؛ باید منفی نباشد.
timeout-mswarmup-ms + duration-ms + 30000watchdog هر stream. باید در بازهٔ 1..INT_MAX میلی‌ثانیه و بزرگ‌تر از warmup-ms + duration-ms باشد. اگر مقدار پیش‌فرض از این بازه بیشتر باشد، timeout معتبری تعیین کنید یا مدت تست را کاهش دهید.
connection-count1تعداد line‌های معمول WaterWall که به‌صورت موازی ساخته می‌شوند؛ باید مثبت باشد.
payload-sizeTCP: 131072, UDP: 3800تعداد byte داخل payload فریم DATA، بدون هدر 48 بایتی speed-test.
target-bits-per-secTCP: 0pacing اختیاری بر حسب bit/sec. مقدار 0 یعنی تا جایی که backpressure اجازه دهد ارسال شود.
udp-target-bits-per-secUDP: 10000000pacing مخصوص UDP بر حسب bit/sec.
target-megabits-per-secتنظیم نشدهفرم جایگزین pacing بر حسب megabit/sec.
verify-payloadfalseتولید و بررسی الگوی داده در هر دو سمت؛ برای تست صحت فعال شود.
json-summaryfalseعلاوه بر log‌های معمول، یک خلاصه فشرده شبیه JSON چاپ می‌کند.
terminate-on-completetrueپس از پایان همه stream‌ها WaterWall را متوقف می‌کند؛ exit code در حالت موفق 0 و در صورت خطا 1 است.

تنها یکی از target-bits-per-sec، udp-target-bits-per-sec و target-megabits-per-sec را می‌توان هم‌زمان تنظیم کرد. نرخ پس از تبدیل به bit/sec باید متناهی، غیرمنفی و کمتر از 2^64 باشد.

payload-size باید بین 1 و 16777216 باشد. در UDP حداکثر 64952 است، چون هر UDP payload یک فریم کامل speed-test با هدر 48 بایتی دارد.

جریان protocol​

Frameجهتکاربرد
HELLOclient به servermode، direction، duration، warmup، report interval، payload size، pacing، سیاست اعتبارسنجی، تعداد stream و stream id را اعلام می‌کند.
ACKserver به clientتایید HELLO. در UDP client قبل از ارسال data منتظر آن می‌ماند.
DATAsender به receiverبدنه صفرشده یا الگوی اعتبارسنجی برای upload یا download.
ENDsender به receiverپایان یک جهت sender.
REPORTserver به clientآمار final sender/receiver.
ERRORserver به clientخطای protocol.

header هر frame برابر ۴۸ بایت است. payload-size فقط اندازه payload در frame نوع DATA را مشخص می‌کند و header را شامل نمی‌شود.

startup و مالکیت line​

هنگام شروع tunnel، client یک task جدا برای هر stream زمان‌بندی می‌کند. برای هر stream:

  1. worker را با stream_id % workers_count انتخاب می‌کند
  2. یک line_t عادی می‌سازد
  3. line state مربوط به SpeedTestClient را مقداردهی اولیه می‌کند
  4. Init را به سمت node بعدی می‌فرستد
  5. watchdog مربوط به timeout-ms را arm می‌کند

چون client این line‌ها را می‌سازد، از بین بردن آن‌ها نیز بر عهده خودش است. با کامل شدن یا شکست هر stream، در صورت لزوم upstream Finish را می‌فرستد و سپس line خودش را از بین می‌برد.

رفتار TCP​

در حالت TCP، بایت‌های دریافتی در stream buffer جمع و به frame‌های speed-test decode می‌شوند. TCP به‌طور پیش‌فرض pacing ندارد و تا جایی که chain و backpressure اجازه دهند ارسال می‌کند، مگر آن‌که target bandwidth تنظیم شده باشد.

client پس از دریافت downstream Est از tunnel بعدی شروع به ارسال می‌کند. با رسیدن callback‌های downstream Pause و Resume نیز task ارسال خودش را متوقف یا از سر می‌گیرد.

رفتار UDP​

در حالت UDP، هر buffer payload در WaterWall دقیقاً یک frame کامل speed-test است. client تا دریافت ACK، frame نوع HELLO را دوباره می‌فرستد. فاصله retry برابر مقدار کوچک‌تر میان report-interval-ms و ۲۵۰ ms است. frame نهایی END برای upload نیز سه بار ارسال می‌شود تا از دست رفتن یک datagram مانع پایان تست نشود.

نرخ پیش‌فرض UDP برابر udp-target-bits-per-sec: 10000000 است. این حالت loss، packet‌های تکراری و خارج از ترتیب، خطاهای اعتبارسنجی و jitter را گزارش می‌کند.

آمار​

سمت receiver این آمار را نگه می‌دارد:

  • تعداد بایت‌ها
  • packets
  • valid packets
  • lost packets
  • duplicate packets
  • out-of-order packets
  • validation errors
  • jitter estimate

با verify-payload=true بدنه‌ها از الگویی قطعی بر اساس stream id، sequence number و direction استفاده می‌کنند. شمارش ترتیب بسته‌ها در حالت غیرفعال هم برقرار است.

در پایان، client گزارش‌های remote per-stream را از server چاپ می‌کند و یک summary نهایی aggregate می‌سازد. اگر json-summary: true باشد، یک خط compact JSON-style aggregate هم چاپ می‌شود.

اعتبارسنجی payload و splice​

تنظیم verify-payload در SpeedTestClient به‌صورت پیش‌فرض false است و از طریق flag پیام HELLO برای هر دو سمت اعمال می‌شود. مقدار true تولید و بررسی الگوی قطعی داده را فعال می‌کند. در حالت غیرفعال، فرستنده بدنه را با صفر مقداردهی می‌کند و گیرنده فقط تعداد بایت‌ها را حساب می‌کند. بررسی header، طول، stream id، sequence و گزارش نهایی همیشه برقرار است. در این حالت valid_packets و validation_errors مربوط به payload صفر هستند و خلاصه JSON فیلد verification_enabled دارد.

هر دو node در chain مناسب TCP با misc.splice=true از splice پشتیبانی می‌کنند. گیرنده header چهل‌وهشت‌بایتی را می‌خواند و برای frame ناقص فوراً بازمی‌گردد. در Linux بدنه DATA بدون بررسی، از pipe مستقیماً به /dev/null تخلیه می‌شود؛ در صورت نبود پشتیبانی، خواندن معمولی با حافظه موقت محدود استفاده می‌شود. بدنه پیام‌های کنترلی و DATA نیازمند اعتبارسنجی به حافظه معمولی منتقل می‌شوند. UDP همچنان هر datagram را جدا پردازش می‌کند. تولید داده خروجی از حافظه معمولی انجام می‌شود.

حد هزینه ذخیره frame ناقص برای هر line برابر 2 * (16 MiB + 48) است؛ پیش از رد ورودی، فشرده‌سازی bufferها امتحان می‌شود. این عدد حد کل حافظه process یا kernel نیست. هر task حداکثر ۳۲ frame می‌فرستد و به Pause احترام می‌گذارد؛ pacing با زمان‌بندی task انجام می‌شود و worker را به خواب نمی‌برد.

اشتباه‌های رایج​

  • این node را وسط chain ترافیک واقعی قرار ندهید؛ chain head است و خودش line می‌سازد.
  • بدون SpeedTestServer متناظر از آن استفاده نکنید.
  • انتظار باز کردن socket مستقیم از آن نداشته باشید؛ پس از آن TcpConnector، UdpConnector یا transport stack دیگری قرار دهید.
  • برای UDP payload خیلی بزرگ انتخاب نکنید مگر اینکه کل مسیر آن را بدون fragmentation/loss تحمل کند.

متادیتای node​

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

ویژگیمقدار
node flagskNodeFlagChainHead + kNodeFlagSupportsSplice
can_have_prevfalse
can_have_nexttrue
layer_groupkNodeLayer4
layer_group_prev_nodekNodeLayerNone
layer_group_next_nodekNodeLayer4
required_padding_left0 بایت