آشنایی با سوکت های شبکه در سی و لینوکس - بخش پنجم

در این نوشته می خواهیم برخی نکته ها و تابع هایی که در برنامه نویسی سوکت نیاز هستند را به شما بگویم تا در نوشته های دیگر از پیش با آنها آشنا باشید. جدول زیر فهرستی از گونه های کاربردی در برنامه نویسی سی و سوکت نویسی را نشان می دهد.

https://virgool.io/linux-internals/%D8%A2%D8%B4%D9%86%D8%A7%DB%8C%DB%8C-%D8%A8%D8%A7-%D8%B3%D9%88%DA%A9%D8%AA-%D9%87%D8%A7%DB%8C-%D8%B4%D8%A8%DA%A9%D9%87-%D8%AF%D8%B1-%D8%B3%DB%8C-%D9%88-%D9%84%DB%8C%D9%86%D9%88%DA%A9%D8%B3-%D8%A8%D8%AE%D8%B4-%DB%8C%DA%A9%D9%85-wbohvfgswvmm
https://virgool.io/linux-internals/%D8%A2%D8%B4%D9%86%D8%A7%DB%8C%DB%8C-%D8%A8%D8%A7-%D8%B3%D9%88%DA%A9%D8%AA-%D9%87%D8%A7%DB%8C-%D8%B4%D8%A8%DA%A9%D9%87-%D8%AF%D8%B1-%D8%B3%DB%8C-%D9%88-%D9%84%DB%8C%D9%86%D9%88%DA%A9%D8%B3-%D8%A8%D8%AE%D8%B4-%D8%AF%D9%88%D9%85-e4ku3bqi7tsj
https://virgool.io/linux-internals/%D8%A2%D8%B4%D9%86%D8%A7%DB%8C%DB%8C-%D8%A8%D8%A7-%D8%B3%D9%88%DA%A9%D8%AA-%D9%87%D8%A7%DB%8C-%D8%B4%D8%A8%DA%A9%D9%87-%D8%AF%D8%B1-%D8%B3%DB%8C-%D9%88-%D9%84%DB%8C%D9%86%D9%88%DA%A9%D8%B3-%D8%A8%D8%AE%D8%B4-%D8%B3%D9%88%D9%85-lb8d7ce5sqig
https://virgool.io/linux-internals/%D8%A2%D8%B4%D9%86%D8%A7%DB%8C%DB%8C-%D8%A8%D8%A7-%D8%B3%D9%88%DA%A9%D8%AA-%D9%87%D8%A7%DB%8C-%D8%B4%D8%A8%DA%A9%D9%87-%D8%AF%D8%B1-%D8%B3%DB%8C-%D9%88-%D9%84%DB%8C%D9%86%D9%88%DA%A9%D8%B3-%D8%A8%D8%AE%D8%B4-%DA%86%D9%87%D8%A7%D8%B1%D9%85-fi6jlgdjlwjt
Data Types
Data Types

اندازه ساختار سوکت

برای نمونه sa_family_t آدرس فامیلی سوکت مانند AF_INET یا AF_INET6 را نگهداری می کند که در واقع شماره های صحیحی هستند که به ریخت ماکرو شناسانده شده اند. socklen_t نیز یک گونه است که اندازه یک سوکت یا اندازه یک ساختار سوکت را نگهداری می کند. اندازه هر گونه داده مانند int یا struct ها را به کمک تابع ()sizeof بدست می آوریم.

ساختار struct sockaddr_in دارای عضوهای گوناگونی از گونه ها است که هر یک اندازه ویژه خودش را دارد. برای نمونه عضو sin_addr که از گونه in_port_t است، اندازه اش ۱۶ بیت یا دو بایت است، زیرا in_port_t گونه ای صحیح بی علامت ۱۶ بیتی است.

اندازه یک ساختار در زبان سی برابر با مجموع اندازه همه عضوهای آن است. توجه کنید خود عضو sin_addr نمونه ای از یک ساختار دیگر به نام struct in_addr است، پس اندازه خود sin_addr برابر با اندازه همه عضوهای ساختار struct in_addr است که با اندازه دیگر عضوها جمع می شود.

دیگر تابع های تبدیل در سوکت نویسی

در نوشته های پیشین درباره چهار تابع برای تبدیل آدرس و شماره پورت به بایت شبکه گفته ام و در اینجا یک دسته دیگر از تابع هایی که برای تبدیل آدرس های IP (نسخه ۴ یا ۶) به بایت های شبکه به کار می روند. این سه تابع در arpa/in.h شناسانده شده اند.

تابع ()inet_aton یک رشته از کاراکترهای سی که یک آدرس IP را نشان می دهد را به بایت های دودویی شبکه تبدیل می کند. اگر آدرس معتبر و درست باشد، این تابع مقداری غیر صفر را برگشت می دهد وگرنه صفر برگشت داده خواهد شد.

int inet_aton(const char *cp, struct in_addr *inp)

در کد بالا که از رهنمای man inet_aton برداشته شده است، نمونه ای از کاربرد آن نشان داده شده است. نخست یک نمونه از ساختار struct addr_in ساخته ایم، زیرا دومین ورودی ()inet_aton یک گونه از struct in_addr است. در خط ۱۵ می بینید که آدرس متغیر addr دمین ورودی تابع است.

نخستین ورودی تابع یک رشته است که آدرس IP (نسخه ۴) را مشخص می کند که باید به بایت های شبکه تبدیل شده و در متغیری که آدرس آن به دومین ورودی فرستاده شد، ذخیره شود. بنابراین دومین ورودی، فراخوانی یک متغیر با ارجاع (Call by Reference) است.

در کد بالا نخست از خط فرمان یک ورودی خوانده می شود و سپس این ورودی به عنوان نخستین عنصر با اندیس صفر در آرایه رشته []argv نگهداری می شود. سپس این ورودی که یک رشته برابر با IP مانند 192.168.1.100 است به ورودی یکم فرستاده می شود.

بنابراین رشته ای مانند 192.168.100 که قابل خواندن برای کاربران است به بایت های دودویی قابل استفاده در ماشین و شبکه تبدیل می شوند. توجه کنید که اگر آدرس IP معتبر باشد، برآیند تابع شماره ای غیر صفر است وگرنه پیغام خط ۱۷ نشان داده می شود و برنامه با نادرستی و بدست تابع ()exit پایان می یابد.

تابع دیگر ()inet_ntoa است که در خط ۲۱ فراخوانی شده است و کاربردی وارونه ()inet_aton دارد. توجه کنید که ()inet_aton برای تبدیل رشته اسکی a به بایت شبکه n دارد ولی ()inet_ntoa برای تبدیل بایت شبکه n به رشته اسکی a قابل خوانده شدن بدست کاربران به کار می رود.

char *inet_ntoa(struct in_addr in)

توجه کنید ()inet_ntoa تنها یک ورودی دارد که آن نام متغیری از struct in_addr است و فراخوانی در ()inet_ntoa فراخوانی با مقدار (Call by Value) است. همچنین تابع ()inet_ntoa یک رشته از کاراکترها (* char) را برگشت می دهد که همان نشانی IP قابل خوانده شدن توسط کاربران است. تابع دیگر ()inet_addr است که یک ورودی رشته از کاراکترها دریافت کرده و سپس یک گونه از in_addr_t را برگشت می دهد.

in_addr_t inet_addr(const char *cp);

تابع دیگر ()inet_pton است که یک نشانی IPv4 یا IPv6 را از رشته ای به کد دودویی تبدیل می کند. این تابع سه ورودی دریافت می کند. نخستین ورودی یک آدرس فامیلی AF_INET یا AF_INET6 است. دومین ورودی یک رشته است که آدرس مبدا مانند 192.168.1.100 در IPv4 را مشخص می کند. سومین ورودی یک اشاره گر void است که آدرس دومین ورودی تبدیل شده در فامیلی آدرس نخستین ورودی در آن نگهداری می شود.

int inet_pton(int af, const char *src, void *dst)

در کد زیر نمونه ای از تبدیل آدرس های IPv4 و IPv6 به بایت های شبکه نشان داده شده است. نخست یک متغیر به نام buf و به اندازه یک ساختار struct in6_addr تعریف کرده ایم. توجه کنید ساختارهای in_addr و in6_addr به ترتیب IPv4 و IPv6 را نشان می دهند.

در دنباله دو متغیر domain و s از گونه int و یک آرایه از کاراکترها به اندازه INET6_ADDRSTRLEN را ساخته ایم. سپس در شرط خط های ۱۲ تا ۱۶ بررسی می شود که آیا ورودی پارامترهای ورودی کمتر از سه تا نباشد. توجه کنید زمانی که می خواهیم با argc و argv ورودی از خط فرمان دریافت کنیم، argc به تعداد پارامترهای ورودی اشاره دارد که نخستین آنها نام برنامه و دیگر ورودی ها پارامترهایی هستند که در برنامه استفاده می شوند یا بر روند اجرای برنامه تاثیر می گذارند. بنابراین [0]argv به نخستین پارامتر ورودی۷ یعنی نام برنامه اجرایی اشاره دارد.

برنامه بدین صورت است که باید یکی از آرگومان های i4 یا i6 که در ورودی و پس از نام برنامه مشخص شده باشد که از طریق [1]argv مشخص می شود. اگر پارامتر دوم i4 باشد، پس آدرس فامیلی AF_INET و اگر i6 باشد پس AF_INET6 خواهد بود.

از تابع ()strcmp برای سنجش اندیس دوم argv با یکی از دو مقدار رشته i4 یا i6 استفاده شده است. به هر حال اگر دو رشته فرستاده شده به ()strcmp یکسان باشند، پس مقدار صفر برگشت داده می شود. توجه کنید نتیجه خط ۱۸ که یا AF_INET یا AF_INET6 است، در متغیر domain نگهداری می شود که سپس این متغیر نخستین ورودی تابع ()inet_pton خواهد بود.

دومین ورودی تابع، سومین پارامتر ورودی از خط فرمان است که در [2]argv نگهداری می شود. سومین پارامتر ورودی نیز که نتیجه اجرای تابع در آن ریخته می شود، متغیر buf است. متغیر s برآیند تابع ()inet_pton است. تابع ()inet_pton هم برای IPv4 و هم برای IPv6 به کار می رود. تابع دیگر ()inet_ntop است که کاربردی وارونه ()inet_pton دارد.

const char *inet_ntop (int family, cost void *addrptr, char *strptr, size_t len)

در فایل netinet/in.h دو کاکرو به نام های INET_ADDRSTRLEN با مقدار ۱۶ و INET_ADDRSTRLEN با مقدار ۶۴ شناسانده شده اند که به ترتیب اندازه رشته (STRLEN) برای آدرس (ADDR) نسخه ۴ و ۶ را نشان می دهند. در واقع با رشته ای ۱۶ بیتی می توانیم IPv4 و با رشته ای ۴۶ بیتی می توانیم IPv6 را نشان دهیم.

این اندازه ها با حساب یک بیت برای کاراکتر پایانی رشته ها در سی، یعنی 0\ یا Terminate Character حساب شده اند. به هر رو، اگر اندازه تعیین شده (در اینجا برای متغیر buf) کم باشد، یک اشاره گر تهی (Null Pointer) برگشت داده می شود، از این رو در زمان تعریف آرایه buf بیشترین مقدار را صرف نظر از IPv4 یا IPv6 در نظر گرفته شد است.

شاد و پیروز و تندرست باشید.