في هذه الجلسة السريعة سأتحدث عن نوعين من أنواع ال Sockets , وبالطبع أغلبكم يعرف ماهي Winsock وتحدثنا عن برمجتها سابقا في هذا الرابط :
المهم ماتجب معرفته أن في البرامج الكبيرة التي تحتاج لتعدد تخاطبات أو Multy sockets , كبرامج chat وبرامج مشاركة الملفات وأي برنامج يحتاج فيه لتكوين أكثر من إتصال , يجب وقتها التفكير مليا قبل الشروع في كتابة الكود حيث يمكن أن تكون Blocking Sockets هي الحل الأمثل وقد تكون Non Blocking Sockets ,
الآن دعونا من الثرثرة ولنعرف ماذا نعني بالمصطلحين Blocking and nono blocking Sockets ,
الأول Blocking Sockets وهي الطريقة العادية التي كنا نتعامل فيها مع برمجة Winsock حيث هي الوضع الإفتراضي دائما عند البدء بمكتبات winsock , وسميت Blocking Socket لأنها تثبت مجرى البرنامج حتى تحصل على إستجابة خارجية External Response ,
مثلا عندما نقوم بعمل Accept Connecton , سيتوقف برنامجنا تماما عند هذه النقطة حتى نحصل في النهاية على Connection خارجي من بريمج في جهاز يحاول الإتصال بجهازنا عن بعد , عندها سيتم الحصول على مقبض ل Socket صالح للتخاطب مع الجهاز البعيد , بالطبع أيضا نفس المشكلة مع الدالة recv وحتى نحصل تماما على مانريد من بيانات من الجهاز أو البريمج المتصل , لن يتحرك البرنامج سطار واحدا حتى ينفذ الدالة recv أو accept ليحصل عل كل مايريد من البرنامج الخارجي ,
الآن في وضع كهذا لو كان لدي مثلا بريمج chat بسيط , وكنت أتحدث مع أكثر من مستخدم , فالبتأكيد لن أستطيع أن أستقبل البيانات إلا من شخص واحد والسبب أن المعالجة Thread الحالية ستتوقف تماما وحتى الحصول على respond بعيد ,
إذا مالحل ؟
الحل كما توقعتم يكون في بعض الأحيان بإستخدام Multy threading حيث يكون لكل socket أو إتصال Thread منفصل تماما عن غيره , يتوقف كما يريد أن يتوقف حتى يحصل على recv وبالطبع سيكون لدينا آخر لعمل send لكل جلسة مع مستخدم بعيد , وبالتأكيد thread منفصل جديد ينتظر طلبات التخاطب الجديدة لينشيء بدوره لها حسابات Socket جديدة في thread جديد هو الآخر !!
يمكن تكوين هذه الشبكة ببساطة , ولاتأسى ولاتقلق على عدد threads القصوى التي يمكن إنشاءها , فبعض البريمجات تأخذ حوالي 100 معالجة thread مستقل ويمكن أن تلاحظ لذلك برؤية عدد threads في البرامج المقيمة في الذاكرة قيد العمل من Task manger بالضغط على ctrl+alt+delete , ثم التبويب View - > select colums->thread count , وسترى في الشاشة Process عدد threads لكل بريمج موجود ,
المهم يجب أن يحظى برنامجك بتخطيط لابأس به يوضح سريان العمل وبدون أخطاء مع تعاطي كبير في تعدد المعالجات وهي من التقنيات المعقدة في عملية Debugging , بالطبع تزامن المسالك يجب أن يكون في قائمة أولوياتك لأن حتى Socket الذي ستستخدمه في الإستقبال والإرسال لمستخدم معين سيكون مشترك بين معالجيتين , ودعك من بقية المتغيرات في الكود .
الآن هذا كود مبسط يوضح عملية إستقبال أكثر من أكثر من Socket بدون تعدد معالجات :
// Server_Final.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <winsock.h>
#include <process.h>
#pragma comment(lib,"wsock32.lib")
using namespace std;
#define SEARCH_PORT 2222
#define DOWN_PORT 2223
#define COM_PORT 2224
#define MAXCONNECTION 10
//---Global Variabules ------------
SOCKET server_socket;
sockaddr_in soc;
//--------------------------------
struct Client
{
SOCKET soc;
};
void Accept()
{
}
void StartServer()
{
WSADATA wsa;
WSAStartup(MAKEWORD(1,1),&wsa);
server_socket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
soc.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
soc.sin_family = AF_INET;
soc.sin_port = htons(SEARCH_PORT);
//memcpy ( &server_socket_address, &server_address, sizeof ( SOCKADDR_IN ) );
bind(server_socket,(SOCKADDR*)&soc,sizeof(soc));
listen(server_socket,MAXCONNECTION);
}
int x = 0;
void Acception()
{
int size = sizeof(soc);
char buf[1000];
for(int q=0;q<1000;q++)
{
//Sleep(100);
SOCKET to_client = accept(server_socket,(SOCKADDR*)&soc,&size);
cout << x++;
if (to_client==0)
{
}
else if (to_client == SOCKET_ERROR)
{
}
else
{
cout << "Connection Established ... "<<" : ";
ZeroMemory(buf,sizeof(buf));
int ret = recv(to_client,buf,1000,0);
if(ret)
{
cout << ret;
}
cout << buf << endl;
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
StartServer();
Acception();
return 0;
}لاحظ للدالة :
Acception , والكود :
int size = sizeof(soc);
char buf[1000];
for(int q=0;q<1000;q++)
{
//Sleep(100);
SOCKET to_client = accept(server_socket,(SOCKADDR*)&soc,&size);
cout << x++;
if (to_client==0)
{
}
else if (to_client == SOCKET_ERROR)
{
}هنا أضفت حلقة تكرار بعدد كبير 1000 إتصال منفصل , ولن يتم التحرك من الدالة Acception مالم يحدث إتصال من البريمج البعيد , وسكون على الشكل :
// client.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <winsock.h>
#include <windows.h>
using namespace std;
#pragma comment(lib,"wsock32.lib")
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsa;
WSAStartup(MAKEWORD(1,1),&wsa);
SOCKET soc;
SOCKADDR_IN addr;
soc = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
addr.sin_port=htons(2222);
addr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
addr.sin_family = AF_INET;
connect(soc,(sockaddr*)&addr,sizeof(addr));
char* buf=new char[1000];
ZeroMemory(buf,1000);
strcpy(buf,"Hello");
cout << buf;
send(soc,buf,10,0);
cin.get();
return 0;
}المثال السابق واضح يوضح عيب Blocking Sockets دون إستخدام تعدد المعالجات , سنعدل عليه لكي لايوقف سير البرنامج نهائيا حيث ربما نحصل على recv بيانات من كونيكشن متصل سابقا ولن نستطيع ذلك مالم يحدث إتصال جديد ل Accept ,
لحل الإشكالية يجب وضع الدالة recv والدالة send والدالة accept كل منها في معالجة منفصلة لكي لاتؤثر على سير البرنامج .
non Blocking Sockets : وهي من إسمها لاتثبت البرنامج ككل في حال الإنتظار في دالة ك Accept , حيث سيمر فيها البرنامج حقيقة وكأنها غير موجودة , لكنه يعود لها فور حصول إتصال مع جهاز آخر , وهي كما ترون حل جذري لبرنامجنا السابق , حيث سيمر التنفيذ في الدالة Accept حتى يصل للدالة recv بشكل طبيعي , وسيظل يدور فيها حتى يحدث شيء إما بالحصول على recv أو حدوث connection مع accept .
يتم التحول لوضع non blocking Sockets بإستخدام ioctlsocket كالتالي :
unsigned long b = 1; ioctlsocket ( SocCom, FIONBIO, &b );
حيث SocCom هو Socket الذي نريد جعله non Blocking , ونريد جعل الدوال التي تتعامل به non blocking ,
كل الدوال مابعد السطر :
ioctlsocket ( SocCom, FIONBIO, &b );
تعامل معاملة Non blocking Socket ,
عند الرغبة للعودة مرة أخرى لنظام blocking Socket فقط يكفي تغيير القيمة :
unsigned long b = 1;
إلى صفر مرة أخرى وإستدعاء :
ioctlsocket ( SocCom, FIONBIO, &b );
قد يكون الكلام مربك , لكن عموما هذا الكود سيعمل مباشرة مع كود client الموجود في الأعلى ويجب عليك القيام بالكثير من التجارب للتتقن هذه العمليات :

