التعريف ببرنامج التسلسل في جيم

بناء طلب للحديث من الصفر هو أحد أفضل الطرق لبرمجة الشبكات الأساسية، واستخدام الجوارب سيعطيك التحكم المباشر في كل تفاعل شبكي، من إنشاء شبكة اتصال إلى نقل البيانات، وهذا المشروع يسير من خلال إنشاء نظام دردشة متعدد العملاء وبرنامج عمل يتواصل في الوقت الحقيقي، إلى جانب تعلم كيفية إنشاء الجوارب، وإرغامها على تقديم الخدمات، والاستماع إلى العملاء، وتلقي الرسائل الحقيقية،

ونفترض المعرفة الأساسية بالبرمجة في إطار البرنامج (الأصناف المتاحة، والثوابت، والوظائف، والمرشدين) وبيئة العمل مع نظام تشغيل مجمّع (C) ونظام التشغيل المتوافق مع نظام POSIX (Linux, macOS, or WSL on Windows) وجميع الأمثلة الرمزية تتبع نظام تقييم الأداء في مجال التنمية المستدامة، وهو معيار برمجة الشبكات في النظم شبه الثابتة.

فهم جيم

(ب) إنَّ الترسيم هو نقطة نهاية للتواصل بين آلتين على شبكة، وفي قائمة المقصورة الخاصة بمحطة POSIX، يمثل أحد مجندي الملفات المُستبقّية، وأكثر الأنواع شيوعاً هي [(FLT:0]) مواسير السلاسل ] (SOC STREAM) باستخدام TCP و

وتتمثل المهام الأساسية التي تشكل جدول الأعمال المؤقت في ما يلي:

  • - إنشاء جورب جديد وإعادة مجند ملف.
  • - يربط جوارب مع عنوان محدد من IP ورقم ميناء.
  • - يُعلّقُ a جورب كسلّي، مستعدّ للقَبْل وصلات واردة.
  • - القفل حتى يتواصل العميل، ثم يعيد جدول جديد لذلك العميل المحدد.
  • - استعمله زبون لبدء اتصال لخادم.
  • and ] — Transmit and receive data over a connected socket.

وجميع هذه المهام مُعلنة في .() كما أن هناك حاجة إلى رؤساء إضافيين مثل (بالنسبة ) و [للإصلاح] [للإصلاح]، ولفهم هؤلاء الرؤساء والهياكل التي يحددونها، فإن الهيكل يُحتفظ دائماً بالأسرة العنوانية، والموانئ (الشبكة الداخلية حسب الترتيب).

لإشارة أعمق، إستشار دليل بيج لبرمجة الشبكة - مورد كلاسيكي يشرح كل وظيفة بأمثلة واضحة.

وضعوا على الخادم

والخدم هو المركز المركزي لطلب الدردشة، ويستمع إلى اتصالات العملاء ويتلقى رسائل من أي زبون، ويبث تلك الرسائل إلى جميع العملاء الآخرين المرتبطين بها، ويستلزم بناء حاسوب خدمة قوي التعامل بعناية مع كل نداء من المكالمات التي يقدمها النظام والأخطاء المحتملة.

خلق التزلج

الخطوة الأولى هي إنشاء جورب مع وظيفة :

int server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
 perror("socket failed");
 exit(EXIT_FAILURE);
}
  • AF INET] - Address family for IPv4.
  • SOCK STREAM] - Reliable, two‐way, connection —
  • 0] - البروتوكول؛ التقصيرات في البارافينات المكلورة قصيرة السلسلة عندما تستخدم SOCK STREAM.

دائماً تفقد قيمة العودة، يجب التعامل مع فشل خلق الجوارب بشكل جيد، و وظيفة تطبع وصف الخطأ إلى stderr.

تُلزِمُ الجوائز

بعد ذلك، ربطنا الجورب إلى عنوان محلي وميناء، العملاء سيتصلون بهذا العنوان

struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY; // Bind to all local interfaces
server_addr.sin_port = htons(8080); // Port number

if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
 perror("bind failed");
 close(server_fd);
 exit(EXIT_FAILURE);
}

INADDR ANY] allows the server to accept connections on any IP address assigned to the host. The port number (8080 in this example) is chosen to avoid well-known services; values above 1024 are typical for userlevel programs. Notice that ensures the port is in network byte order (bigendian).

الاستماع إلى الارتباطات

وبعد إلزام الخادم، يجب أن يستمع إلى الاتصالات الواردة.

if (listen(server_fd, 5) < 0) {
 perror("listen");
 close(server_fd);
 exit(EXIT_FAILURE);
}

أما الحجة الثانية (5) فهي تراكم العمل - وهي المدة القصوى لطرح الاستفسارات المعلقة - ويمكن استخدام قيمة أعلى بالنسبة للخادمات المشغولة، ولكن النظام يفرض حدا أعلى (في كثير من الأحيان ).

قبول مواقف العملاء

وتقبل عميلاً، نتصل بـ في حلقة، ويعيد مُدون جديد لملف الجورب إلى ذلك العميل المحدد، بينما يتواصل جهاز التصفيق الأصلي.

struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
if (client_fd < 0) {
 perror("accept");
 // In a real server, handle the error without exiting
 continue;
}

char client_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
printf("New connection from %s:%d\n", client_ip, ntohs(client_addr.sin_port));

لكل عميل مقبول، سنحصل على آي بي و ميناء العميل هذه المعلومات مفيدة لقطع الأشجار و التحكم المحتمل في الدخول

معالجة البيانات من العملاء

وعندما يتصل عميل، يجب أن يكون الخادم قادراً على قراءة الرسائل وبثها، وبالنسبة لخادم بسيط يتعامل مع عميل واحد في وقت ما، فإن النمط المثالي هو الاتصال على جورب العملاء، ولكن، من أجل دعم العملاء المتعددين حقاً، تحتاج إلى تطابق.

تطوير العميل

والزبون أبسط: فهو يخلق جوارب، ويربط الخادم، ثم يدخل حلقة حيث يقرأ مدخلات المستخدمين ويرسلها إلى الخادم، في الوقت نفسه يتلقى رسائل إذاعية من الخادم.

الإبداع والتواصل

أولا، تم إنشاء جورب العميل بالضبط كما فعل الخادم، ثم يتصل باستخدام عنوان الحاسوب المحمول وميناء الخادم:

int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
 perror("socket creation failed");
 exit(EXIT_FAILURE);
}

struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8080);

// Convert IP address from text to binary
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
 perror("invalid address / address not supported");
 close(sock);
 exit(EXIT_FAILURE);
}

if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
 perror("connection failed");
 close(sock);
 exit(EXIT_FAILURE);
}

هنا ] حوّل سلسلة عنوان عنوان IP البشرية القابلة للقراءة إلى شكل ثنائي، وبالنسبة لرمز الإنتاج، قد تحلّين أسماء مضيفة مع .

سوق الأوراق

وبعد الاتصال، يحتاج العميل إلى إرسال رسائل يطبعها المستخدم ويتلقى رسائل من الخادم في وقت واحد، ويتمثل النهج المشترك في استخدام خيطين: أحدهما للقراءة من الستيرين والإرسال، والآخر لتلقي الرسائل من التابوت، وكبديل لذلك، يمكن استخدام نظام غير مقفل أو نظام ، وزبون ضئيل يرسل رسائل (ويتجاهل الحاجة الحقيقية) سهل.

The following skeleton uses to create a child process that handles incoming messages while the parent handles outgoing. For threading, prefer for portability.

// Fork to handle send and receive separately
if (fork() == 0) {
 char buffer[1024];
 while (1) {
 memset(buffer, 0, sizeof(buffer));
 int n = recv(sock, buffer, sizeof(buffer) - 1, 0);
 if (n <= 0) {
 printf("Server disconnected\n");
 break;
 }
 printf("%s", buffer);
 }
 close(sock);
 exit(0);
} else {
 char message[1024];
 while (fgets(message, sizeof(message), stdin) != NULL) {
 send(sock, message, strlen(message), 0);
 memset(message, 0, sizeof(message));
 }
 close(sock);
}

وهذا النمط يعمل، ولكن في التطبيق الحقيقي، ستحتاجون إلى معالجة مسألة صياغة الرسالة بشكل سليم، لأن برنامج التجارة الدولية هو بروتوكول تيار - يمكن تقسيم الرسائل أو تصنيفها، والحل البسيط هو استخدام تعيين حدودي مثل أو إرسال طول الرسالة أولا.

التعامل مع العملاء المتعددي الأطراف

ويجب أن يدير الخادم العديد من العملاء في آن واحد، وهناك طريقتان شعبيتان هما ] [القراءة الكاملة] ] (خطة واحدة لكل زبون) و]I/O multiplexing] [الاستعمال أو ).

استخدام الخيوط (الخط)

في النموذج المُخَطَّع، تستقبل الحلقة الرئيسية زبوناً وتُنشئ خيط جديد مُكرَّس لذلك الزبون، ويتلقى المُنافذ الخفية رسائل ويبثها، وهذا بسيط من الناحية المفاهيمية ويعمل جيداً لعشرات العملاء، ولكن يمكن أن يصبح الرؤوس العامة للخيط مسألة لها آلاف الصلات.

void *handle_client(void *arg) {
 int client_fd = *(int*)arg;
 free(arg); // Free the heap‑allocated int
 char buffer[1024];
 while (1) {
 memset(buffer, 0, sizeof(buffer));
 int n = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
 if (n <= 0) {
 // Client disconnected
 break;
 }
 // Broadcast to all other clients
 // (requires a mutex‑protected list of client fds)
 }
 close(client_fd);
 return NULL;
}

while (1) {
 int *pclient = malloc(sizeof(int));
 *pclient = accept(server_fd, ...);
 pthread_t tid;
 pthread_create(&tid, NULL, handle_client, pclient);
 pthread_detach(tid); // Automatically clean up on exit
}

تذكر حماية البيانات المشتركة )مثل قائمة الجوارب المرابطة( مع متحول، كما تحرص على سلامة - وهي آمنة عموما، ولكن يجب أن تتجنب الكتابات المتزامنة إلى نفس الجوارب.

استخدام اختيار

ويسمح نظام ] باستخدام خيط واحد لرصد مجندين متعددين للملفات من أجل القراء أو الصلاحية أو الأخطاء، وهذا يتفادى الرؤوس العامة للعديد من الخيوط ويحقق الكفاءة لعدد معتدل من الاتصالات، ويحتفظ الخادم بمجموعة من مجندي الملفات (سوكيت) المحاسبين وكل الجوارب العميلة) عندما يكون [مدوناً: 38] قد عادوا.

fd_set readfds;
int max_fd = server_fd;

while (1) {
 FD_ZERO(&readfds);
 FD_SET(server_fd, &readfds);
 for (int i = 0; i < client_count; i++) {
 FD_SET(clients[i], &readfds);
 if (clients[i] > max_fd) max_fd = clients[i];
 }

 int activity = select(max_fd + 1, &readfds, NULL, NULL, NULL);
 if (activity < 0) { perror("select"); break; }

 if (FD_ISSET(server_fd, &readfds)) {
 // Accept new client
 int new_socket = accept(server_fd, ...);
 clients[client_count++] = new_socket;
 }

 for (int i = 0; i < client_count; i++) {
 int sd = clients[i];
 if (FD_ISSET(sd, &readfds)) {
 char buffer[1024];
 int n = recv(sd, buffer, sizeof(buffer), 0);
 if (n <= 0) {
 // Remove client
 close(sd);
 clients[i] = clients[--client_count];
 i--;
 } else {
 // Broadcast buffer to all other clients
 }
 }
 }
}

One advantage of is portability, but it is limited by (usually 1024). For higher scalability, use or (Linuxspecific). The select(2) man page provides full details.

تنفيذ بروتوكول نموذجي بسيط

ولجعل الدردشة متسقة، تحتاج إلى بروتوكول دنيا، وبدون بروتوكول، يمكن أن تتدخل رسائل من مختلف العملاء على السلك، على الأقل، اتخاذ قرار بشأن شكل رسالة، أي أن النهج البسيط: كل رسالة هو خط واحد ينتهي بـ .

[Alice] Hello everyone!
[Bob] Hi Alice!

وعندما يرسل العميل رسالة، يقرأ الخادم الخط (يستخدم تعييناً) ثم يرسله إلى جميع العملاء الآخرين، على نحو اختياري مع نقطة البداية، والرسالة ذات أهمية حاسمة، والطريقة القوية هي إرسال رئيس ثابت الحجم يتضمن طول الرسالة، يليه الحمولة، وهذا يحول دون المسائل التي لها قراء جزئية.

struct chat_message {
 uint32_t length; // in network byte order
 char data[1024]; // up to 1024 bytes
};

الخادم يقرأ بالضبط 4 قوارير للحصول على طول، ثم يقرأ أن الكثير من البواخر للبيانات، وهذا يضمن تلقي الرسائل بقطعة واحدة.

معالجة الأخطاء وأفضل الممارسات

وتكتنف برامج الشبكات حالات الحواف، وهناك بضع ممارسات أساسية:

  • (د) التحقق دائماً من قيمة العودة ، ، ، ، ، ، ، ورواسب مغلقة بشأن الفشل.
  • Use or ] to log meaningful error messages.
  • (ب) أن يضع خيار التسلسل [(FLT:56]) على متن قذيفة الخادم لتجنب أخطاء " الإضافة التي كانت مستخدمة بالفعل " عند إعادة التشغيل.
  • Handle signals like (which can occur when writing to a closed socket) by ignoring them or using .
  • Use non —blocking I/O or timeouts with ] to prevent the server from hanging on a dead client.
  • دائماً ما لا تُرسل أي عوازل قبل القراءة لضمان عدم إرسال بيانات عن البقايا.
  • تنظيف الموارد - جوارب مغلقة والذاكرة المجانية - لمنع التسرب.

For a deep dive into error handling and advanced techniques, read the POSIX specification for align] and related functions.

توسيع نطاق الطلب

بمجرد أن يكون لديك دردشة متعددة العملاء يمكنك إضافة سمات لجعلها أكثر فائدة

  • User authentication] - Clients provide a username on connect; the server validates and stores it.
  • Private Messaging] – Allow “/msg username text” commands that the server routes only to the target client.
  • Room / channel support] - يمكن للمكفوفين الانضمام إلى غرف مسماة؛ ولا تبث الرسائل إلا في غرفة واحدة.
  • File transfer] – Send binary files by implementing a separate data channel or by sending file metadata and raw bytes over the same socket.
  • Encryption] - Use OpenSSL or TLS to secure communication.
  • Graphical user interface - أكتب عميلاً من مجموعة جوال (يستخدم GTK, Qt, or even a web fronthinend with a backend bridge).
  • Persistent storage] – Log messages to a file or database for later retrieval.

وكل توسع يُعلّم مفاهيم قيمة: تصميم البروتوكول، والتوافق، والأمن، وخبرة المستعملين، والقاعدة التي تبنيها الآن ستدعم أي من هذه التحسينات.

خاتمة

إنشاء تطبيق بسيط للحديث باستخدام التوابيت هو مشروع جيد لكل من برامج شبكة التعلم تعلمت كيف تخلق الخواديم والزبائن وتدير صلات متعددة مع الخيوط أو وتعالج البيانات الأولية على المقطع التجاري وتطبق بروتوكولاً أساسياً للحديث، وتضع الأمثلة الرمزية نقطة انطلاق تستطيع جمعها واختبارها وتعديلها، مع توسيع رسالة المشروع، ستواجهون مشاكل حقيقية مثل

وللعمل الإضافي، النظر في تنفيذ خادم بسيط من طراز HTTP أو أداة لتقاسم الملفات بين الأقران، وهذه المهارات قابلة للنقل مباشرة، وتذكر دائما قراءة الوثائق. ] The socket(7) man page هو رفيقكم الدائم.