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 یا قالب
خروجی مشخص، آن را اضافه کنید.
تنظیمات اختیاری
| فیلد | پیشفرض | توضیح |
|---|---|---|
mode | tcp | mode framing. مقدارهای مجاز: tcp, TCP, udp, UDP. |
direction | upload | جهت تست. مقدارهای مجاز: upload, UPLOAD, send, download, DOWNLOAD, receive, bidirectional, both, BOTH. |
duration-ms | 10000 | مدت اندازهگیری پس از warmup؛ باید مثبت باشد. |
warmup-ms | 0 | مدت warmup؛ باید منفی نباشد. frameهای warmup ارسال و در صورت فعال بودن گزینه اعتبارسنجی میشوند، اما در مجموع بایتهای اندازهگیریشده حساب نمیشوند. |
report-interval-ms | 1000 | فاصله میان گزارشهای دورهای؛ باید مثبت باشد. |
start-delay-ms | 50 | تأخیر آغاز هر stream پس از startup؛ باید منفی نباشد. |
timeout-ms | warmup-ms + duration-ms + 30000 | watchdog هر stream. باید در بازهٔ 1..INT_MAX میلیثانیه و بزرگتر از warmup-ms + duration-ms باشد. اگر مقدار پیشفرض از این بازه بیشتر باشد، timeout معتبری تعیین کنید یا مدت تست را کاهش دهید. |
connection-count | 1 | تعداد lineهای معمول WaterWall که بهصورت موازی ساخته میشوند؛ باید مثبت باشد. |
payload-size | TCP: 131072, UDP: 3800 | تعداد byte داخل payload فریم DATA، بدون هدر 48 بایتی speed-test. |
target-bits-per-sec | TCP: 0 | pacing اختیاری بر حسب bit/sec. مقدار 0 یعنی تا جایی که backpressure اجازه دهد ارسال شود. |
udp-target-bits-per-sec | UDP: 10000000 | pacing مخصوص UDP بر حسب bit/sec. |
target-megabits-per-sec | تنظیم نشده | فرم جایگزین pacing بر حسب megabit/sec. |
verify-payload | false | تولید و بررسی الگوی داده در هر دو سمت؛ برای تست صحت فعال شود. |
json-summary | false | علاوه بر logهای معمول، یک خلاصه فشرده شبیه JSON چاپ میکند. |
terminate-on-complete | true | پس از پایان همه 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 | جهت | کاربرد |
|---|---|---|
HELLO | client به server | mode، direction، duration، warmup، report interval، payload size، pacing، سیاست اعتبارسنجی، تعداد stream و stream id را اعلام میکند. |
ACK | server به client | تایید HELLO. در UDP client قبل از ارسال data منتظر آن میماند. |
DATA | sender به receiver | بدنه صفرشده یا الگوی اعتبارسنجی برای upload یا download. |
END | sender به receiver | پایان یک جهت sender. |
REPORT | server به client | آمار final sender/receiver. |
ERROR | server به client | خطای protocol. |
header هر frame برابر ۴۸ بایت است. payload-size فقط اندازه payload در frame نوع DATA را مشخص میکند و header را شامل نمیشود.
startup و مالکیت line
هنگام شروع tunnel، client یک task جدا برای هر stream زمانبندی میکند. برای هر stream:
- worker را با
stream_id % workers_countانتخاب میکند - یک
line_tعادی میسازد - line state مربوط به
SpeedTestClientرا مقداردهی اولیه میکند Initرا به سمت node بعدی میفرستد- 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 flags | kNodeFlagChainHead + kNodeFlagSupportsSplice |
can_have_prev | false |
can_have_next | true |
layer_group | kNodeLayer4 |
layer_group_prev_node | kNodeLayerNone |
layer_group_next_node | kNodeLayer4 |
required_padding_left | 0 بایت |