سوکت‌های برکلی
سوکت‌های برکلی یا سوکت‌های بی‌اس‌دی کتاب‌خانه‌ای شامل رابط‌های برنامه‌نویسی نرم‌افزار برای کار با سوکت‌های اینترنتی و سوکت‌های دامنه یونیکس است، که از این سوکت‌ها برای ارتباطات بین پردازشی استفاده می‌شوند. سوکت‌های برکلی به عنوان رابط برنامه‌نویسی نرم‌افزار از سیستم‌عامل ۴/۲بی‌اس‌دی سرچشمه گرفتند که این سیستم‌عامل در سال ۱۹۸۳ منتشر شد. امروزه تمام سیستم‌عامل‌های مدرن یک پیاده‌سازی از سوکت‌های برکلی را به همراه دارند چون این سوکت‌ها روش استاندارد برای دسترسی به اینترنت هستند. این رابط‌ها در اصل به زبان سی نوشته شدند اما بیشتر زبان‌های برنامه‌نویسی رابط‌های مشابهی را در دسترس کاربر قرار می‌دهند و می‌توان از آن‌ها در اکثر زبان‌های برنامه‌نویسی مدرن استفاده کرد.
رابط‌های سوکت برکلی در چند فایل سرآیند تعریف شده‌اند. البته در پیاده‌سازی‌های مختلف، این فایل‌ها کمی در نام و محتوا با هم متفاوت هستند. اما به‌طور کلی این فایل‌های سرآیند به شرح زیر هستند:
توابع و ساختمان داده‌های اصلی مرتبط با سوکت‌ها در این فایل تعریف شده‌است.
دربرگیرنده AF_INET و AF_INET6 و پروتکل‌های متناظر با آن‌ها PF_INET و PF_INET6 است. این پروتکل‌ها به‌طور گسترده در اینترنت استفاده می‌شوند. این فایل هم دربرگیرنده آدرس‌های IP و هم شماره پورت‌های TCP و UDP است.
این فایل حاوی خانواده آدرس‌های PF_UNIX/PF_LOCAL است. این دسته از آدرس‌ها برای برقرای ارتباط میان برنامه‌های موجود بر روی یک سیستم استفاده می‌شوند و از آن‌ها در شبکه استفاده نمی‌شوند.
حاوی توابعی برای دستکاری آدرس‌های IP عددی است.
حاوی توابعی برای ترجمه کردن اسامی پروتکل‌ها و میزبان‌ها به معادل عددی آنهاست.
ساختارهای مختلفی برای کار با سوکت‌ها وجود دارد که یکی از ساده‌ترین آن‌ها ساختار sockaddr_n است که در فایل netinet/in.h تعریف شده است: struct sockaddr_in {
در ساختار sockaddr_in، فیلدهای sin_port و sin_addr باید به صورت Network byte order باشند. چرا که این اطلاعات قرار است بر روی شبکه ارسال شوند و هر ماشینی هم روش مخصوص به خود را برای پردازش داده‌های موجود در این فیلدها را دارد و بنابراین این فیلدها باید به صورت استاندارد بر روی شبکه ارسال شوند تا از تداخل جلوگیری شود. به کمک توابع htons() و htonl() می‌توان تبدیل مورد نظر را انجام داد. همین‌طور دیگر فیلدها هم در صورتی‌که استفاده نمی‌شوند، باید با مقادیر ۰ پر شوند. این کار با استفاده از تابع memset() قابل انجام است. این تابع در فایل سرآیند string.h تعریف شده‌است. به مثال زیر توجه کنید:
/* pseudo code */
struct sockaddr_in mystruct;
mystruct.sin_family = AF_INET; mystruct.sin_port = htons(1234); mystruct.sin_addr.s_addr = htonl(INADDR_ANY);
memset( &(mystruct.sinzero), '\0', 8 ); ساختار معروف دیگر ساختار addrinfo می‌باشد:
برخی از مهمترین توابع عبارتند از:
سوکت‌ها نقاط پایانی اتصالات هستند. این فراخوان سیستمی یک سوکت جدید ایجاد کرده و یک توصیف‌گر فایل به آن سوکت برمی‌گرداند. socket() سه آرگومان دریافت می‌کند:
این تابع در هنگام شکست مقدار -۱ را برمی‌گرداند و در صورت موفقیت هم یک توصیف‌گر فایل به سوکت مورد نظر برمی‌گرداند که از نوع عدد صحیح است.
این تابع به شکل زیر تعریف شده است:
int socket(int domain, int type, int protocol); این فراخوان سیستمی سوکت مورد نظر را به یک آدرس دلخواه مرتبط می‌کند. وقتی که یک سوکت جدید با socket() ایجاد می‌شود، تنها نوع آن سوکت مشخص می‌شود. (IPv4 یا IPv6) و socket() هیچ آدرسی به آن اختصاص داده نمی‌شود. قبل از اینکه سوکت بتواند برای پذیرش درخواست‌های سرویس‌گیرنده‌ها استفاده شود، باید با استفاده از bind() یک آدرس دلخواه را به آن اتصال داد. bind() سه آرگومان دریافت می‌کند.
اگر سوکت با موفقیت به آدرس مورد نظر مرتبط شد، عدد ۰ و در غیر این صورت عدد -۱ برمی‌گردد.
این تابع به شکل زیر اعلان شده است:
int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen); بعد از اینکه یک آدرس دلخواه به سوکت اختصاص یافت (از طریق bind())، فراخوان سیستمی listen() سوکت مورد نظر را برای دریافت اتصالات ورودی آماده می‌کند تا کلاینتها بتوانند به سیستم متصل شوند. اتصالات ورودی به ترتیب در یک صف قرار می‌گیرند. listen() به دو آرگومان احتیاج دارد:
وقتی که یک اتصال مورد پذیرش قرار گرفت، از صف حذف می‌شود تا فضای کافی برای اتصالات جدید به وجود آید. این تابع در هنگام موفقیت مقدار ۰ و در هنگام شکست مقدار -۱ را برمی‌گرداند. این تابع به شکل زیر اعلان شده است:
int listen(int sockfd, int backlog);
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
برنامه‌هایی که بر روی سوکت خاصی در حال گوش فرادادن برای دریافت اتصالات کلاینت‌ها هستند (با استفاده از listen())، می‌توانند با استفاده از فراخوان accept() درخواست‌های کلاینت‌ها را پذیرفته و اتصال جدیدی را با آن‌ها برقرار کنند. فراخوان accept() یکی از درخواست‌های موجود در صف listen() را پذیرفته و یک توصیف‌گر پرونده جدید برمی‌گرداند که این توصیف‌گر پرونده، علاوه بر توصیف‌گری است که برنامه در حال گوش فرادادن به آن است. به این ترتیب، برنامه می‌تواند هم‌زمان از طریق یک توصیف‌گر درخواست‌های جدید را بپذیرد و برای ارتباط با هر کلاینت هم یک توصیف‌گر مجزا خواهد داشت. این فراخوان سه پارامتر دارد:
sockfd: توصیف‌گر سوکتی که برنامه از طریق آن مشغول گوش فرادادن به درخواست‌ها است. cliaddr: اشاره‌گری به ساختار sockaddr به منظور دریافت کردن اطلاعات کلاینت addrlen: اشاره‌گری به یک نوع socklen_t که اندازه ساختاری که برای دریافت اطلاعت کلاینت ارسال شده (دومین پارامتر) را مشخص می‌کند. پس از اینکه تابع accept برگشت، این پارامتر تغییر می‌کند و مشخص می‌کند ک عملاً چند بایت از ساختار مورد نظر استفاده شده‌است.
مقدار برگشتی تابع accept در صورت موفقیت، یک توصیف‌گر پرونده است که برنامه از طریق آن می‌تواند با کلاینت ارتباط برقرار کند. تمامی ارتباطات با کلاینت از طریق این توصیف‌گر صورت خواهد گرفت. در صورت شکست، مقدار -۱ برمی‌گردد و متغیر سراسری errno هم با کد خطای مورد نظر مقداردهی می‌شود.
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
یک کلاینت با استفاده از این فراخوان می‌تواند به یک سرور متصل شود. این تابع در صورت موفقیت مقدار ۰ و در صورت شکست مقدار -۱ را برمی‌گرداند و متغیر errno را کد خطای مناسب مقداردهی می‌کند. اگر اجرای این تابع با شکست مواجه شود، برنامه‌های پورتابل می‌بایست بلافاصله توصیف‌گر پروندهٔ مورد استفاده را ببندند. چون این تضمین وجود ندارد که توصیف‌گر مورد نظر پس از شکست خوردن connect قابل استفادهٔ مجدد باشد. در استاندارد SUS وضعیت توصیف‌گر پرونده پس از شکست خوردن connect «تعریف‌نشده» توصیف شده‌است.