شاید انتظار داشتید که در این مرحله با پروتکل TCP شروع کنیم؛ اما از اونجایی که پیادهسازی پروتکل UDP بسیار سادهتر از TCP هست؛ بعلاوه این دو پروتکل مفاهیم مشترک زیادی دارند؛ تصمیم داریم با UDP کار رو جلو ببریم.
همونطور که دیدیم؛ پروتکل ICMP هم مثل پروتکل ARP دریافتکننده دادهای از لایههای بالاتر نبود؛ ولی دو پروتکل UDP و TCP عموماً بستههایی از لایههای بالاتر دریافت کرده، هدر خودشون رو اضافه میکنند؛ سپس این دادهها رو که دیگه اینجا بهش میگیم سگمنت (segment) تحویل لایه سه میدن. در هنگام دریافت هم از لایه سه سگمنت رو میگیرند؛ هدر خودشون رو برمیدارن؛ داده رو تفسیر میکنند و بعد میدن به لایه چهارم و…
لایههای 5 و بالاتر یا L5+ or L5~7 | داده اصلی Main data |
(Transport Layer) لایه 4 | TCP or UDP segments |
(Network Layer) لایه 3 | IP Packets |
(Data Link Layer) لایه 2 | Ethernet ii frames |
(Physical Layer) لایه 1 | ‘0’ , ‘1’ Stream |
یادمون هست که در لایه دو ما آدرسهای فیزیکی یا همون MAC Addressها رو داشتیم و بعد دلیل به وجود اومدن آدرسهای منطقی یا همون IP Address رو گفتیم که در لایه سه مورداستفاده قرار میگرفتند.
خب تا اینجا ما تونستیم ارتباط هاست به هاست رو در شبکه داشته باشیم. اما اگر در داخل یک هاست، سرویسها یا نرمافزارهای مختلفی در حال کار باشند؛ چطور باید مشخص کنیم که هر بسته باید به کدام سرویس تحویل بشود؟ اینجاست که یک نوع آدرسدهی جدید بنام شماره port رو داریم.
شماره پورتها مثل شمارههای داخلی تلفن بر روی یک شماره اصلی هستند و عملاً آدرسدهی سرویس به سرویس رو پوشش میدن. توجه داشته باشید که این شماره پورت رو با پورتهای سختافزاری مثل SPI یا UART اشتباه نگیرید.
شماره پورت یه عدد 16 بیتی است؛ درنتیجه میشه باهاش 65536 سرویس مجزا رو روی یک هاست داشته باشیم. البته تعدادی از این پورت ها توسط IANA برای پروتکلهای لایههای بالاتر در نظر گرفته شدهاند و اختصاصی هستند. تعدادی دیگه هم رزرو شدهاند؛ اما قسمت عمده پورتها در اختیار کاربر هست. از اونجاییکه پورتهای اختصاصی عموماً در شمارههای کوچکتر از 1023 قرار دارند؛ پیشنهاد ما اینه که برای سخت افزارتون از پورتهای با شماره بزرگتر از 1023 و کوچکتر از 49151 استفاده کنید. البته این فقط یک پیشنهاده و الزامی نیست.
در جدول زیر تعدادی از پورتهای اختصاصی برای پروتکلهای لایههای بالاتر رو مشاهده میکنید:
پورت | پروتکل |
7 | Echo protocol |
11 | Day Time protocol |
15 | Netstat service |
20,21 | FTP (File Transfer Protocol) |
22 | SSH (Secure SHell) |
23 | Telnet Protocol (Telecomunication network) |
25 | SMTP (Simple Mail Transfer Protocol) |
37 | TIME protocol |
53 | DNS (Domain Name Service) |
67,68 | DHCP (Dynamic Host Configuration Protocol) And BOOTP (Boostarp Protocol) |
69 | TFTP (Trivial File Transfer Protocol) |
80 | HTTP (HyperText Transfer Protocol) |
115 | SFTP (Simple File Transfer Protocol) |
161 | SNMP (Simple Network Management Protocol) |
همونطور که دیده میشه؛ ممکنه برای یک عملیات خاص مثل انتقال فایل؛ پروتکلهای مختلفی بر روی پورتهای مختلف داشته باشیم (FTP, TFTP) و یا یک هنگام بهروزرسانی یک پروتکل قدیمی از همان شماره پورت برای پروتکل جدیدتر استفاده شده باشد؛ مثل پورتهای 67,68 که در ابتدا برای سرویسهای درخواست/پاسخ BOOTP در نظر گرفته شده بودند؛ اما با ظهور پروتکل DHCP که برای اعمال تنظیمات یک هاست توسط یک سرور بکار میرود؛ مجدداً از همین پورتها استفاده شده است. تذکر بدیم که در ادامه این سند، پروتکل DHCP و یکی از اصلیترین کارکردهای آن یعنی تخصیص IP Address به هاستهای کلاینت را خواهیم داشت.
برگردیم سراغ UDP. پروتکل UDP (Use Datagram Protocol) برای ارسال و دریافت دادههایی استفاده میشوند که بین دو مقوله “زمان” یا “سلامت” انتقال داده؛ برای زمان اهمیت بیشتری قایل هستند. این پروتکل اصطلاحاً یک پروتکل connectionless یا بدون اتصال است، بدین معنی که اولاً فرستنده و گیرنده قبل از شروع انتقال داده، با هم مذاکره یا هماهنگی خاصی ندارند! بعلاوه فرستنده، بعد از ارسال پیام؛ بررسی نمیکند که آیا دادهها به سلامت رسیدهاند یا خیر! البته این موضوع، رفتار تعریف شده در استاندارد این پروتکل است؛ ولی اگر برنامهنویسی هر دو سمت فرستنده و گیرنده دست شما باشه؛ خودتون میتونید این بررسی رو انجام بدید.
بخش هدر در یک سگمنت UDP بهصورت زیر هست:
Octet Offset | 0 | 1 | 2 | 3 |
0 | Source Port | Destination Port | ||
4 | Length | CheckSum |
همونطور که در شکل میبینید؛ شبه هدر شامل آدرس IP فرستنده و گیرنده بعلاوه شماره پروتکل UDP (عدد 17 یا همون 0x11) و طول سگمنت UDP هست. قسمت zero هم صفر هست (برای اینکه اندازه بخش protocol؛ 16 بیتی باشه).
پس قبل از انجام چک سام، ابتدا بخش چک سام در UDP رو صفر میکنیم؛ سپس IP آدرسهای فرستنده و گیرنده رو بهصورت 16 بیتی جمع میکنیم. عدد حاصل رو ابتدا با 0x0011 (شماره پروتکلUDP و بخش Zero) و بعد هم با اندازه طول سگمنت UDP جمع میکنیم. حالا این نتیجه رو بهعنوان مقدار اولیه در تابعی که برای چک سام نوشتیم، استفاده میکنیم.
حالا نوبت اینه که تابع IP_Process() رو طوری تغییر بدیم که بتونه سگمنتهای UDP رو هم دریافت کنه. از اونجاییکه ما داریم با مفاهیم اولیه و پروتکلهای اصلی آشنا میشیم؛ اینجا کد رو طوری مینویسیم که بعد از دریافت هر سگمنت UDP ، به تکتک بایتهای دریافتی، یک عدد اضافه کنه و به فرستنده برگردونه. قاعدتاً شما میتونید از UDP برای ارسال هر نوع داده یا پردازشی استفاده بکنید. بهعنوانمثال ارسال دادههای تعدادی سنسور از یک برد الکترونیکی به کامپیوتر؛ پردازش دادهها در کامپیوتر و سپس ارسال دستور از کامپیوتر به برد الکترونیکی جهت راهاندازی تعداد عملگر مثل رله؛ شیر برقی و…
تابع IP_Process() اینگونه تغییر خواهد کرد:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | if (ipFrame->protocol == IP_FRAME_PROTOCOL_ICMP) //==0x01 { newDataLen = ICMP_Process((ICMP_EchoFrame*)ipFrame->data, dataLen); } else if (ipFrame->protocol == IP_FRAME_PROTOCOL_UDP) //==0x11 { newDataLen = UDP_Process((UDP_Frame*)ipFrame->data, dataLen); } |
ساختار UDP_Frame مثل ساختارهای قبلی، طوری تعریف شده که بتونیم هدر و دادههای سگمنت رو از هم جدا کنیم. همچنین یک پورت برای ارتباط UDP معرفی میکنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | #define UDP_DEMO_PORT 2020 typedef struct UDP_Frame { uint16_t srcPort; uint16_t destPort; uint16_t len; uint16_t checkSum; uint8_t data[]; } UDP_Frame; و تابع UDP_Process() هم اینگونه تعریف میشه: uint16_t UDP_Process(UDP_Frame* udpFrame, uint16_t frameLen) { uint16_t destPort = ntohs(udpFrame->destPort); uint16_t len = ntohs(udpFrame->len); uint16_t dataLen = len - sizeof(UDP_Frame); if (destPort == UDP_DEMO_PORT) { for(uint16_t i = 0 ; i < dataLen - 1; i++) { udpFrame->data[i]++; // به بایتهای دریافتی یک عدد اضافه میکنیم } } uint16_t swapPort = udpFrame->destPort; udpFrame->destPort = udpFrame->srcPort; udpFrame->srcPort = swapPort; udpFrame->checkSum = UDP_checkSum(); // calculate new checksum for UDP return len; } |
همونطور که در کد دیده میشه؛ بعد از دریافت یک سگمنت ابتدا بررسی میشه که آیا پورتی که ما در انتظارش هستیم آدرسدهی شده یا نه؟ (مقدار چک سام رو بررسی نکردیم! اما بهتره که بررسی بشه؛ استاندارد هم تاکید میکنه که انجام بشه) سپس به مقدار بایتهای دریافتی، یک عدد اضافه کردیم (با عملگر ++) بعد جای پورت فرستنده/گیرنده رو در درون سگمنت تغییر دادیم؛ چک سام جدید رو حساب کردیم و سگمنت خودمون رو برگردوندیم به لایه IP تا ارسال کنه.
برای تست پروتکل UDP میتونید خودتون برنامه سادهای بنویسید یا از برنامههای آماده و رایگانی مثل echotool استفاده کنید. فایل اجرایی این نرمافزار رو میتوانید از https://github.com/PavelBansky/EchoTool/tree/master دانلود کنید (این نرمافزار در command prompt ویندوز اجرا میشه). همچنین بسیاری از شرکتهای تولیدکننده ماژولهای اترنت مثل USR نیز نرمافزارهایی برای ارتباط در اختیار کاربران قرار میدهند. نرمافزار تست این شرکت رو از آدرس https://www.pusr.com/support/download/PC-Test-Software-USR-TCP232-Test-V1-3.html دانلود کنید.
کار ما با UDP در اینجا تموم میشه. قبل از اینکه بخواهیم پروتکل TCP رو توضیح بدیم، ترجیحمون اینه که پروتکل DHCP رو معرفی کنیم و کمی هم از DNS بگیم. علت این کار اینه که پروتکل TCP پروتکل پیچیده ایه (قورباغه رو الان قورت نميديم!) از طرفی DHCP و DNS روی UDP کار میکنند.
نویسنده شو !
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.