برنامه‌نویسی شبکه در ویندوز/لینوکس: libcpnet

کارفرمای کاری که الان دارم انجام میدم اصرار داره که برنامه‌ش علاوه‌بر لینوکس روی ویندوز هم به‌خوبی اجرا بشه. مدیر من کاملاً این محدودیت رو پذیرفته و به کارفرما گفته که با سخت‌افزاری که مشخصاتش رو اعلام می‌کنیم و با مشخصات کارکردی که اعلام می‌کنیم، روی ویندوز هم می‌تونید برنامه رو اجرا کنید. خوب این تصمیم مشکلات بسیار بزرگی برای برنامه‌نویس به‌وجود میاره. ازجمله برنامه‌نویسی شبکه… این پست به بررسی این مشکلات و ارائهٔ یک راه حل خوب خواهد پرداخت (: اگر قصد دارید کدی بنویسید که هم روی ویندوز و هم روی سیستم‌عامل‌های واقعی بتونه از شبکه استفاده کنه حتماً ادامهٔ مطلب رو بخونید.

در لینوکس API ساده و بسیار کاربردی برای برنامه‌نویسی شبکه وجود داره که تقریباً تمام این API استاندارد شده. توابع زبان سی که خیلی ساده کارهایی رو با فلسفهٔ یونیکس (≈ هرچیزی یک فایل هست برای نوشتن و خواندن) انجام میده. به راحتی و با استفاده از این API سطح پایین میشه برنامه‌های شبکه با کارایی بالا نوشت.

مشکل از جایی شروع میشه که این برنامه‌ها بخوان سمت ویندوز برن. متأسفانه سیستم‌عامل ویندوز از نظر طراحی هیچ استاندارد خاصی رو رعایت نکرده و تنها قسمت‌هایی از مشخصات POSIX رو به‌صورت دست و پا شکسته و ناقص پیاده‌سازی کرده. از طرف دیگه API خوبی هم برای کار کردن با سوکت‌ها نداره. نصف توابعی که فراهم شده POSIX هستند و نصف دیگه کاملاً من‌درآوردی.

معمولاً برنامه‌های بزرگ برای حل این مشکلات از کتابخانه‌هایی که پیاده‌‌سازی‌های چندسکویی (cross-platform) فراهم کردند استفاده می‌کنند. معروف‌ترین این پیاده‌سازی‌ها poco, ACE, asio و ØMQ هستند. مشکلی که استفاده از این کتابخانه‌ها داره اینه که:

به همین دلیل جای خالی یک کتابخانهٔ ساده و بسیار سبک که با کمترین هزینه؛ برای ویندوز و لینوکس رابط برنامه‌نویسی سطح سوکت فراهم کنه احساس می‌شد. متأسفانه من همچین کدی پیدا نکردم. و شروع کردم به نوشتن یک کتابخانهٔ آزاد و خیلی خیلی ساده به زبان سی که امکان برنامه‌نویسی شبکه در سطح سوکت رو برای هر دو سیستم‌عامل فراهم می‌کنه. نتیجه چیزی شد که خودم خیلی ازش راضی بودم و تصمیم گرفتم منتشرش کنم.

این کتابخانه از اینجا قابل دسترسی هست: https://github.com/soroush/libcpnet

ویژگی‌ها

محدودیت‌ها

با وجود این محدودیت‌ها کتابخانه کاملاً کاربردی و قابل استفاده هست و مشکلات زیادی رو حل می‌کنه.

کد

هیچ چیز بهتر از کد نمی‌تونه توضیح بده (: کد سمت کلاینت برای اتصال از نوع TCP یه همچین چیزی میشه:

/* Client-side sample code */
/* Initialize networking API (Only needed in Windows) */
net_init();
/* Start a client */
socket_t socket = net_socket(SOCK_STREAM);
/* Connect to port 50001 */
if(net_connect(socket, "127.0.0.1", 50001) != 0) 
{
    fprintf(stderr, "Unable to connect: %s", net_last_error();
    exit(-1);
}
char buffer[1024];
/* Write to socket */
ssizet io_size = net_write(socket, buffer, 1024);
if(io_size < 0)
{
    fprintf(stderr, "Unable to write: %s", net_last_error();
    exit(-1);
}
io_size = net_read(socket, buffer, 1024);
if(read_size <= 0) {
    fprintf(stderr, "Unable to read: %s", net_last_error();
    exit(-1);
}
/* ... */
/* Cleanup (Only needed in Windows) */
net_clean();

و کد سمت سرور برای باز کردن یک پورت و جواب دادن به اتصالات مختلف:

/* Initialize networking API (if any needed) */
net_init();
/* Start a server */
socket_t socket = net_socket(SOCK_STREAM);
/* Bind on port 50001 */
uint16_t port = 50001;
if(net_bind(socket, NULL, &port) != 0) {
    fprintf(stderr, "Unable to bind: %s", net_last_error());
    exit(-1);
}
/* Listen for incomming connections (backlog=10) */
net_listen(socket, 10);
/* Accept clients */
char address[46];
socket_t client = net_accept(socket, address, &port);
fprintf(stderr, "Client connected. Remote Address:`%s' Assigned port number: %d\n", address, port);
char buffer[1024];
ssize_t r = net_read(client, buffer, 1024);
if(r < 0) {
    fprintf(stderr, "Unable to read from socket: ", net_last_error());
    exit(-1);
}
if(r == 0) {
    printf("Remote client stopped.");
    net_close(client);
    exit(-1);
}
ssize_t w = net_write(client, buffer, r);
if(w <= 0) {
    fprintf(stderr, "Unable to write to socket: ", net_last_error());
    exit(-1);
}

چند تا نکته:

Comments

comments powered by Disqus