هستهٔ خالص
packet_reader، tcp_analyzer، volume_analyzer، metrics_calculator — فقط پکت به متریک؛ بدون شبکه.
تحلیل فایلهای PCAP ضبطشده با PCAPdroid برای اپهای پیامرسان (سناریوهای آپلود و دانلود)، محاسبهٔ شاخصهای کیفیت و حجم شبکه، و انتشار نتیجه به Kafka — بدون اتصال مستقیم به PostgreSQL.
این مستند معماری، فلوی اجرا، متدولوژی ضبط داده، تعریف دقیق شاخصها، و تصمیمات مهندسی زیرسیستم تحلیل ترافیک شبکه را شرح میدهد. این زیرسیستم فایلهای PCAP ضبطشده با PCAPdroid را برای اپلیکیشنهای دستهی گپوگفت (در دو سناریوی آپلود و دانلود) تحلیل میکند، شاخصهای کیفیت و حجم شبکه را محاسبه میکند، و نتیجه را به تاپیک network-metrics در Kafka منتشر میکند.
نقش آن در زنجیرهٔ داده مکمل Crawler است: یکی آمار و نقد Play Store را میآورد، دیگری کیفیت شبکهٔ واقعی روی گوشی را میسنجد. ماندگارسازی هر دو مسیر را Storage Consumer انجام میدهد.
مهمترین تصمیم طراحی این زیرسیستم است: پوشهی analysis/ فقط به dpkt، کتابخانهی استاندارد پایتون، و مدل دامنه (core/models.py) وابسته است — هیچگاه Kafka، HTTP، یا Config را import نمیکند. این تفکیک دقیقاً همان چیزی است که اجازه داد Milestoneهای خواندن پکت و محاسبهی شاخصها کاملاً بدون روشن بودن هیچ زیرساختی (نه Kafka، نه App API) ساخته و با صدها Synthetic PCAP تست شوند.
analysis/ هیچ وابستگی بیرونی ندارد، بیش از ۲۰۰ تست Unit و تستهای Oracle در کمتر از چند ثانیه اجرا میشوند، بدون نیاز به هیچ کانتینر یا بروکری. همین مرز، پایهٔ استراتژی تست سهلایه در بخش ۱۱ است.مطابق همان الگوی معماری که در storage_consumer پیادهسازی شد (جداسازی بر اساس مسئولیت و جهت وابستگی)، ساختار نهایی این زیرسیستم چنین است:
packet_reader، tcp_analyzer، volume_analyzer، metrics_calculator — فقط پکت به متریک؛ بدون شبکه.
مدلهای immutable، پارسر نام فایل، و message_mapper خالص که شکل JSON تاپیک را تعریف میکند.
دو نوع ارتباط بیرونی عمداً جدا شدهاند: Kafka برای خروجی، HTTP برای اعتبارسنجی App API.
main.py اشیاء واقعی را میسازد؛ analyzer_service use-case را اجرا میکند: validate → analyze → publish.
storage_consumer که فقط یک نوع I/O بیرونی (Kafka) داشت، این زیرسیستم دو نوع کاملاً متفاوت دارد — Kafka (خروجی نتیجه) و HTTP به App API (اعتبارسنجی اپلیکیشن) — که عمداً در دو پوشهی جدا (messaging/ و clients/) نگه داشته شدهاند، چون ماهیت و الگوی خطای این دو نوع ارتباط با هم فرق دارد.صحت اندازهگیری، پیش از هر خط کد، به نحوهی ضبط بستگی دارد. سه اصل رعایت شده است:
| ملاحظه | توضیح |
|---|---|
| چرا Force Stop لازم است | اکثر اپهای پیامرسان از اتصال TCP پایدار برای Push Notification استفاده میکنند؛ بدون Force Stop، ممکن است اصلاً Handshake جدیدی در Capture ثبت نشود و rtt_handshake برابر null بماند. |
| Trade-off اجتنابناپذیر | برای ثبت Handshake، ضبط باید از لحظهی باز شدن اپ شروع شود؛ این یعنی مقداری ترافیک راهاندازی (Login/Sync/Presence) وارد فایل میشود که در bytes_transferred_total منعکس میشود. این محدودیت بهجای حذف کامل، صادقانه در تفسیر نتایج ذکر میشود. |
| محدودیت شناختهشدهی دیگر | متریکهای کیفیت (RTT، Retransmission، Out-of-order، Spurious، Zero-Window، RST) فقط TCP هستند؛ متریکهای حجم کل ترافیک IP از جمله QUIC/UDP را شامل میشوند. یک اپ مبتنی بر QUIC میتواند حجم بالا و Retransmission نزدیک صفر نشان دهد — این رفتار طبیعی پروتکل است، نه نقص Parser. |
| قرارداد نام فایل | <package>__<upload|download>__<YYYYMMDDTHHMMSS>.pcap — جداکنندهٔ دو زیرخط چون . و _ داخل نام پکیج اندروید مجازند. timestamp فقط اطلاعاتی است و به قرارداد Kafka نمیرسد (ساعت گوشی قابل اعتماد نیست). |
| یک سناریو در هر فایل | هر Capture دقیقاً یک عملیات هدف (ارسال یا دریافت فایل) دارد. Target apps در PCAPdroid روی همان اپ تنظیم شود تا ترافیک دیگران مخلوط نشود. |
تفکیک صادقانهی اینکه کدام شاخص خواندن مستقیم یک فیلد پروتکل است و کدام یک تخمین مهندسیشده، برای اعتبار علمی نتایج ضروری است. گروه ۱ فقط TCP را میبیند؛ گروه ۲ همهی ترافیک IP را.
| شاخص | نوع | تعریف دقیق |
|---|---|---|
rtt_handshake |
قطعی، با قرارداد خاص پروژه | میانگین t(SYN-ACK) − t(SYN اولیه) بر حسب میلیثانیه. عمداً از اولین SYN حساب میشود، نه آخرین تلاش — چون تعریف سند («از بستهی اولیه») میخواهد افت و تاخیر ناشی از بستههای گمشده در عدد نهایی منعکس شود، برخلاف الگوریتم کلاسیک Karn که چنین نمونهای را کنار میگذارد. این متریک صرفاً سطح TCP را میسنجد؛ نه TLS Handshake و نه لحظهی Established کامل (ACK نهایی) را. اگر هیچ Handshake کاملی در فایل نباشد → null. |
zero_window_count |
قطعی | بستههای TCP با window == 0، بهاستثنای SYN (که Window اولیه هنوز معنا ندارد) و RST (که فیلد Window در آن بیمعناست). |
tcp_reset_count |
قطعی | شمارش مستقیم بستههای دارای پرچم RST. |
retransmission_count |
Heuristic (صریحاً مستندشده) | به ازای هر جهت جریان، map بازههای sequence پوششدادهشده نگه داشته میشود. بستهای که بایتهایش قبلاً پوشش داده شده و peer هنوز ACK نکرده، Retransmission شمرده میشود. SYN/FIN تکراری هم شمرده میشوند. Out-of-order و Spurious جدا گزارش میشوند تا سیگنال بازیابی از دسترفتگی باد نکند. |
out_of_order_count |
Heuristic | سگمنت payloadدار که فقط بایتهای جدید میآورد ولی از جلوی frontier پیوسته جلوتر شروع میشود (حفرهٔ sequence باقی میگذارد) — بهجای retransmission اینجا شمرده میشود. |
spurious_retransmission_count |
Heuristic | سگمنت کاملاً پوششدادهشدهای که peer قبلاً تا انتهای آن را ACK کرده. از retransmission_count جداست تا resendهای غیرضروری، سیگنال loss-recovery را متورم نکنند. |
| شاخص | نوع | تعریف دقیق |
|---|---|---|
bytes_transferred_total |
قطعی، با تصمیم طراحی مهم | مجموع فیلد ip.len (نه طول فریم Pcap)، چون PCAPdroid با «PCAPdroid extensions» یک هدر Ethernet جعلی + Trailer ۲۸ بایتی اضافه میکند؛ جمع بر مبنای ip.len این Overhead مصنوعی را حذف و بین دو حالت DLT سازگار میکند. |
bytes_payload_total |
قطعی | TCP: ip.len − ip_hdr_len − tcp_hdr_len (با احتساب Options)؛ UDP: udp.ulen − 8؛ سایر: صفر. |
overhead_ratio |
مشتق | (total − payload) / total، با محافظت در برابر تقسیم بر صفر (فایل خالی → 0.0). در دامنه بهصورت property محاسبه میشود تا هرگز با دو عدد حجمی تناقض نداشته باشد؛ روی سیم برای گزارشگیری materialize میشود. |
پیادهسازی اولیهی RTT Handshake، هنگام گمشدن و بازارسال یک SYN، زمان را از روی آخرین تلاش موفق حساب میکرد (نتیجه: ۲۰ میلیثانیهی گمراهکننده بهجای واقعیت). تست Oracle، با مقایسهی مستقیم خروجی در برابر فیلد tcp.analysis.ack_rtt در Wireshark روی همان فایل، این ناهمخوانی را آشکار کرد: Wireshark مقدار ۳۰۲۰ میلیثانیه گزارش میداد — دقیقاً بازهی واقعی که اتصال طول کشیده بود. اصلاح شد تا از اولین SYN حساب شود، همانطور که هدف متریک (تشخیص افت اتصال) اقتضا میکند.
اسکریپت بچ هر فایل inbox را به main.py میسپارد؛ سرویس Compositionشده مسیر validate → analyze → publish را طی میکند.
App API چک میکند اپ وجود دارد، فعال است، و is_messaging_app است. نام پکیج غلط در چند ثانیه روی ترمینال میمیرد، نه هفتهها بعد در لاگ Consumer.
AppRegistryClient + فلگ --skip-registry-check برای کار آفلاین
Analyzer هرگز به Postgres وصل نمیشود. سند صراحتاً ماندگاری را به Storage واگذار کرده؛ یک کامپوننت مالک اعتبارنامه و migration میماند.
مکانیزم: تاپیکnetwork-metrics، کلید = package_name
این بخش اینترفیسی است که Storage Consumer روی آن پیادهسازی میکند. سبک پیام با Crawler یکی است: کلید پکیج، مقدار JSON تخت و snake_case، بدون envelope از نوع {type, payload}.
| فیلد | یادداشت |
|---|---|
analysis_id | UUID4 یکتا بهازای هر تحلیل — کلید Idempotency برای ON CONFLICT DO NOTHING در Consumer. Kafka At-Least-Once است؛ بدون این فیلد، Replay فایل ردیف تکراری میساخت و نمودارهای روند را خراب میکرد. |
analyzed_at | در سمت Analyzer ثبت میشود، نه auto_now_add در دیتابیس — معنای «چه زمانی تحلیل واقعاً اجرا شد» را حفظ میکند، نه «چه زمانی Consumer پیام را خواند» (همان اصل crawled_at در Crawler). |
package_name | هرگز application_id ارسال نمیشود؛ resolve کلید خارجی مال مالکی اسکیما است. |
scenario | فقط UPLOAD یا DOWNLOAD. |
rtt_handshake | float یا JSON null؛ تا ۳ رقم اعشار. |
| فیلدهای داخلی CLI | packet_count و handshake_sample_count فقط برای گزارش stdout هستند و عمداً روی سیم نمیآیند تا Consumer به ستونی که در DB نیست وابسته نشود. |
ip.lenPCAPdroid فایل را در یکی از دو شکل مینویسد. Reader با dispatch روی link-type هدر فایل هر دو را پشتیبانی میکند:
| Link type | چه موقع | رفتار Reader |
|---|---|---|
101 (raw IP) | پیشفرض، از جمله root mode | رکورد از هدر IP شروع میشود. |
1 (Ethernet) | فعال بودن «PCAPdroid extensions» | هدر Ethernet جعلی + trailer حاوی UID و نام پکیج؛ trailer قبل از Parse IP جدا میشود. |
DLT_* کتابخانه، چون Reader.datalink() مقدار هدر فایل را برمیگرداند در حالی که dpkt.pcap.DLT_RAW ثابت سبک BSD پلتفرم است (12، نه 101).جمع بر مبنای ip.len حدود ۴۶ بایت bookkeeping مصنوعی بهازای هر پکت (هدر جعلی + padding + trailer + checksum) را حذف میکند، بین دو حالت DLT یکسان میماند، و نسبت به truncation ناشی از snaplen مقاومتر است.
اسکریپت analyze_pcaps.sh بر اساس کد خروج Analyzer تصمیم میگیرد فایل کجا برود. شکاف بین کد ۵ و بقیه حیاتی است: بروکر قطعشده نباید Capture سالم را قرنطینه کند، و فایل معیوب نباید تا ابد Retry شود.
| کد | معنا | رفتار اسکریپت بچ |
|---|---|---|
| 0 | تحلیل و انتشار موفق | انتقال به processed/ |
| 1 | خطای پیکربندی یا غیرمنتظره | انتقال به failed/ + فایل .log |
| 2 | خطای usage آرگومان (argparse) | انتقال به failed/ + لاگ |
| 3 | نام فایل بد یا اپ غیرمجاز | انتقال به failed/ + لاگ |
| 4 | Capture خوانا نیست | انتقال به failed/ + لاگ |
| 5 | Kafka یا App API در دسترس نیست | فایل در inbox میماند برای تلاش مجدد |
این زیرسیستم یک Job دستهای on-demand است، نه Daemon. پشت پروفایل Compose به نام tools است تا docker compose up معمولی آن را بالا نیاورد.
docker-entrypoint.sh بر اساس آرگومان اول بین بچ کل inbox/ (پیشفرض / --batch) و تحلیل تکفایل (--file …) سوییچ میکند — همان الگوی Imageهای رسمی Postgres برای یک Image با چند حالت استفاده، بدون نیاز به دو Image جدا.
network_analyzer/.env برای Host است (localhost:9092). کانتینر از docker-compose.yml مقادیر شبکهٔ داخلی میگیرد (kafka:29092، http://app-api:8000). پوشهٔ ./data/pcap به /data/pcap bind میشود.| لایه | چه چیزی را تضمین میکند |
|---|---|
| Unit (Synthetic) | منطق داخلی با Ground Truth کنترلشده مطابقت دارد؛ سریع، قطعی، بدون فایل باینری Commitشده. تعداد دقیق retransmission / zero-window / RST تزریق و assert میشود. |
| Contract | message_mapper شکل JSON سیم را بدون نیاز به broker ثابت نگه میدارد. |
| Oracle (tshark) | خروجی با ابزار مستقل صنعتی همخوان است؛ همین لایه باگ واقعی RTT را کشف کرد. بدون tshark بهصورت خودکار skip میشود. |
| Live | فرضهای Mock دربارهٔ App API و Kafka در دنیای واقعی هم برقرارند. |
| End-to-End دستی | کل زنجیره (ضبط واقعی → تحلیل → Kafka → در نهایت Storage) با هم کار میکند. |
test)| سرویس Compose | محتوا | Image |
|---|---|---|
network-analyzer-tests | Unit + Contract (not live and not oracle) | Dockerfile معمولی |
network-analyzer-tests-live | مارکر live روی شبکهٔ Compose | همان Image |
network-analyzer-tests-oracle | مارکر oracle | Dockerfile.test با tshark |
| مسیر | ارتباط با Network Analyzer |
|---|---|
scripts/network/analyze_pcaps.sh |
بچ روی inbox؛ مسیریابی فایل بر اساس exit code. |
scripts/verify/verify_network_metrics.sh |
تأیید انتشار روی تاپیک (قبل/مکمل persist در Storage). |
scripts/run_tests_in_docker.sh |
صف تستهای unit / live / oracle این زیرسیستم. |
| Storage Consumer | مصرفکنندهٔ تاپیک network-metrics و مالک جدول همنام. |
| Database Schema | ستونهای جدول network_metrics و قید Idempotency روی analysis_id. |
سند صراحتاً ماندگاری دادهٔ شبکه را به زیرسیستم Storage واگذار کرده. نگهداشتن اعتبارنامهی دیتابیس فقط در یک کامپوننت، دو سرویس مستقل را به یک اسکیمای فیزیکی مشترک وابسته نمیکند. پیامد جانبی: لاگ Kafka خودش آرشیو نتایج است؛ باگ mapping در Consumer با reset offset درست میشود، نه با ضبط دوباره روی گوشی.
analysis/بدون Kafka/HTTP/Config در هسته، Milestoneهای M1–M3 و صدها تست Synthetic بدون زیرساخت ممکن شد و سرعت بازخورد توسعه حفظ شد.
analysis_id (UUID یکتا)Kafka At-Least-Once است. UUID بهازای هر تحلیل، ON CONFLICT DO NOTHING را در Consumer ممکن میکند بدون از بین بردن intent append-only: اجرای مجدد واقعی تست، UUID جدید و ردیف جدید میسازد.
analyzed_at در Analyzer، نه DBمعنای زمانی تحلیل باید مستقل از زمان خواندن پیام توسط Consumer بماند — همراستا با crawled_at در Crawler.
messaging/ از clients/دو I/O با الگوی خطای متفاوت (broker در برابر REST) عمداً در دو پوشه ماندند تا مرز مسئولیت شفاف بماند.
ip.len نه طول فریمحذف overhead مصنوعی PCAPdroid extensions و سازگاری بین DLT خام و Ethernet.
متریک باید افت اتصال را نشان دهد، نه فقط RTT تلاش موفق. Heuristic retransmission با interval map و peer ACK، سیگنالهای متفاوت را قاطی نمیکند.
یک Image، دو حالت (بچ / تکفایل) — الگوی رسمی Postgres — بدون دو Dockerfile موازی.
فایل سالم روی قطعی موقت زیرساخت قرنطینه نمیشود؛ فقط ورودیهای غیرقابلقبول به failed/ میروند.
retry_policyمثل Crawler و Storage Consumer: هر زیرسیستم واحد استقرار مستقل است و سورس بقیه در Imageش نیست. مجموعهی خطاهای قابل Retry مخصوص HTTP/Kafka این سرویس است.
| ابزار | نقش |
|---|---|
| dpkt | خواندن و Parse کردن فایلهای PCAP در سطح بایت |
| tshark (Wireshark CLI) | Oracle مستقل برای اعتبارسنجی دقت متریکها؛ فقط وابستگی توسعه، نه Production |
| scapy | ساخت Synthetic PCAP با Ground Truth دقیق برای تستهای واحد |
| kafka-python | انتشار پیام به Topic network-metrics |
| requests | اعتبارسنجی اپلیکیشن از طریق App API قبل از تحلیل |
| PCAPdroid | ابزار ضبط ترافیک روی گوشی اندروید (منبع ورودی) |
| pytest (+ markers) | Unit / Contract / Oracle / Live با جداسازی پیشنیازها |
Docker Compose profiles tools / test | Job on-demand و کانتینرهای تست بدون قاطی شدن با استک پیشفرض |
| python-dotenv | بارگذاری network_analyzer/.env در اجرای Host |