سوکت‌های برکلی

سوکت‌های برکلی (به انگلیسی: Berkeley sockets) یا سوکت‌های بی‌اس‌دی (به انگلیسی: BSD sockets) کتاب‌خانه‌ای شامل رابط‌های برنامه‌نویسی نرم‌افزار برای کار با سوکت‌های اینترنتی و سوکت‌های دامنه یونیکس است، که از این سوکت‌ها برای ارتباطات بین پردازشی استفاده می‌شوند. سوکت‌های برکلی به عنوان رابط برنامه‌نویسی نرم‌افزار از سیستم‌عامل ۴/۲بی‌اس‌دی سرچشمه گرفتند که این سیستم‌عامل در سال ۱۹۸۳ منتشر شد. امروزه تمام سیستم‌عامل‌های مدرن یک پیاده‌سازی از سوکت‌های برکلی را به همراه دارند چون این سوکت‌ها روش استاندارد برای دسترسی به اینترنت هستند. این رابط‌ها در اصل به زبان سی نوشته شدند اما بیشتر زبان‌های برنامه‌نویسی رابط‌های مشابهی را در دسترس کاربر قرار می‌دهند و می‌توان از آن‌ها در اکثر زبان‌های برنامه‌نویسی مدرن استفاده کرد.

ساختارها

ساختارهای مختلفی برای کار با سوکت‌ها وجود دارد که یکی از ساده‌ترین آن‌ها ساختار sockaddr_n است که در فایل netinet/in.h تعریف شده است:
struct sockaddr_in {
        uint8_t sin_len;
        sa_family_t     sin_family;
        in_port_t       sin_port;
        struct  in_addr sin_addr;
        char    sin_zero[8];};
در ساختار sockaddr_in، فیلدهای sin_port و sin_addr باید به صورت Network byte order باشند. چرا که این اطلاعات قرار است بر روی شبکه ارسال شوند و هر ماشینی هم روش مخصوص به خود را برای پردازش داده‌های موجود در این فیلدها را دارد و بنابراین این فیلدها باید به صورت استاندارد بر روی شبکه ارسال شوند تا از تداخل جلوگیری شود. به کمک توابع htons()‎ و htonl()‎ می‌توان تبدیل مورد نظر را انجام داد. همین‌طور دیگر فیلدها هم در صورتی‌که استفاده نمی‌شوند، باید با مقادیر ۰ پر شوند. این کار با استفاده از تابع memset()‎ قابل انجام است. این تابع در فایل سرآیند string.h تعریف شده‌است. به مثال زیر توجه کنید:
#include <string.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.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 می‌باشد:
     struct addrinfo {
     int ai_flags;      /* input flags */
     int ai_family;      /* protocol family for socket */
     int ai_socktype;      /* socket type */
     int ai_protocol;      /* protocol for socket */
     socklen_t ai_addrlen;   /* length of socket-address */
     struct sockaddr *ai_addr; /* socket-address for socket */
     char *ai_canonname;     /* canonical name for service location */
     struct addrinfo *ai_next; /* pointer to next in list */
     };

accept()‎

int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
برنامه‌هایی که بر روی سوکت خاصی در حال گوش فرا دادن برای دریافت اتصالات کلاینت‌ها هستند (با استفاده از listen()‎)، می‌توانند با استفاده از فراخوان accept()‎ درخواست‌های کلاینت‌ها را پذیرفته و اتصال جدیدی را با آن‌ها برقرار کنند. فراخوان accept()‎ یکی از درخواست‌های موجود در صف listen()‎ را پذیرفته و یک توصیف‌گر پرونده جدید برمی‌گرداند که این توصیف‌گر پرونده، علاوه بر توصیف‌گری است که برنامه در حال گوش فرا دادن به آن است. به این ترتیب، برنامه می‌تواند هم‌زمان از طریق یک توصیف‌گر درخواست‌های جدید را بپذیرد و برای ارتباط با هر کلاینت هم یک توصیف‌گر مجزا خواهد داشت. این فراخوان سه پارامتر دارد:
sockfd: توصیف‌گر سوکتی که برنامه از طریق آن مشغول گوش فرا دادن به درخواست‌ها است.
cliaddr: اشاره‌گری به ساختار sockaddr به منظور دریافت کردن اطلاعات کلاینت
addrlen: اشاره‌گری به یک نوع socklen_t که اندازه ساختاری که برای دریافت اطلاعت کلاینت ارسال شده (دومین پارامتر) را مشخص می‌کند. پس از اینکه تابع accept برگشت، این پارامتر تغییر می‌کند و مشخص می‌کند ک عملاً چند بایت از ساختار مورد نظر استفاده شده‌است.
مقدار برگشتی تابع accept در صورت موفقیت، یک توصیف‌گر پرونده است که برنامه از طریق آن می‌تواند با کلاینت ارتباط برقرار کند. تمامی ارتباطات با کلاینت از طریق این توصیف‌گر صورت خواهد گرفت. در صورت شکست، مقدار -۱ برمی‌گردد و متغیر سراسری errno هم با کد خطای مورد نظر مقداردهی می‌شود.

connect()‎

int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
یک کلاینت با استفاده از این فراخوان می‌تواند به یک سرور متصل شود. این تابع در صورت موفقیت مقدار ۰ و در صورت شکست مقدار -۱ را برمی‌گرداند و متغیر errno را کد خطای مناسب مقداردهی می‌کند. اگر اجرای این تابع با شکست مواجه شود، برنامه‌های پورتابل می‌بایست بلافاصله توصیف‌گر پروندهٔ مورد استفاده را ببندند. چون این تضمین وجود ندارد که توصیف‌گر مورد نظر پس از شکست خوردن connect قابل استفادهٔ مجدد باشد. در استاندارد SUS وضعیت توصیف‌گر پرونده پس از شکست خوردن connect «تعریف‌نشده» توصیف شده‌است.