Winsock Tutorial – ونساک ۔سبق

اگر آپ جان بوجھ کر میرے ونساک ٹیوٹوریل پر پہنچے ہیں تو ، آپ کو انٹرنیٹ کے ذریعے اپنی اپنی ایپلی کیشنز کے بارے میں سوچنے کا امکان بہت زیادہ ملے گا جیسا کہ میرے پاس ہے۔ یا ، شاید کسی اور نے امکان کو اتنا ہی دلچسپ پایا ہو اور آپ کو اس وژن کو حقیقت میں لانے کی ذمہ داری سونپی گئی ہو۔ دونوں صورتوں میں ، ونساک نیٹ ورک سروس اور یہ ٹیوٹوریل آپ کو تجارتی انٹرپرائز کے اپنے اہداف کے حصول میں مدد فراہم کرے گا ، صرف ذاتی استعمال کے لیے نیٹ ورک پروگرامنگ کے دائرے کو دریافت کرے گا یا اس کے درمیان کچھ۔

یہاں ہم کیا احاطہ کریں گے:

اگرچہ آپ اس خوفناک مقام پر پہنچنے کے خواہشمند ہو سکتے ہیں جہاں آپ کی درخواست کامیابی سے اپنا پہلا کنکشن بناتی ہے ، کوڈ کے پیچھے تصورات سے آگاہ رہیں۔ اپنی فوری ضروریات کو پورا کرنے کے لیے دیے گئے کوڈ میں صرف ہیرا پھیری سے بچنے کی کوشش کریں اور اس کے بجائے اپنی درخواست کی ضروریات کو پہچانیں اور تب ہی عمل کریں جو بہترین حل لگتا ہے۔ ابھی میرے سافٹ ویئر ڈویلپمنٹ کے مشورے کے لیے یہی کافی ہے آئیے کچھ نیٹ ورک پروگرامنگ کرتے ہیں …پورے ٹیوٹوریل کوڈ لسٹنگ کو بلا جھجھک ڈاؤن لوڈ کریں ۔ یاد رکھیں کہ اس ٹیوٹوریل میں پیش کیا گیا کوئی بھی کوڈ ونساک لائبریری سے منسلک ہونا چاہیے ، عام طور پر wsock32.lib یا اسی طرح کا کوئی نام۔ نیز ، جب آپ کے اپنے IDE (دیو-سی ++ ، مائیکروسافٹ وی سی ++ ، سی ++ بلڈر ، وغیرہ) میں ٹیوٹوریل میں پیش کردہ کوڈ کا استعمال کرتے ہوئے ، غلطیوں سے بچنے کے لیے ون مین () کے ساتھ ونڈوز پروجیکٹ بنانے کا انتخاب کریں ۔

سننے والا ساکٹ بنانا۔

باہر کی مشینوں کی خدمت کرنے والی ایپلی کیشنز کو سرور کہا جاتا ہے۔ سرور ایپلی کیشنز ایک یا زیادہ سننے والے ساکٹ کو شروع کرکے گاہکوں کو سنتے ہیں۔ جب کوئی کلائنٹ ان سننے والے ساکٹس میں سے کسی ایک سے جڑتا ہے تو ، سرور ونساک سے ایک اطلاع وصول کرتا ہے ، کنکشن کو قبول کرتا ہے ، اور نئے کلائنٹ کو پیغامات بھیجنا اور روکنا شروع کر دیتا ہے۔ شاید سب سے آسان طریقہ جس کے ذریعے سرور ایک سے زیادہ کلائنٹس کو سنبھالتے ہیں وہ ہر کلائنٹ کنکشن کے لیے ایک نیا دھاگہ بنانا ہے۔ یہ سرور ماڈل اکثر بلاکنگ ساکٹ استعمال کرتا ہے ، جو کہ آنے والے ڈیٹا ، نئے کنکشن اور نیٹ ورک کے دیگر ایونٹس کے انتظار میں عارضی طور پر رک جاتا ہے۔ پہلے ، آئیے کچھ ڈھانچے کی شناخت کریں جن کی ہمیں بلاکنگ ساکٹ کو شروع کرنے کی ضرورت ہوگی۔

  • WSADATA : یہ ڈھانچہ آپریٹنگ سسٹم کو Winsock کے ورژن کے لیے استفسار کرنے کے لیے استعمال کیا جاتا ہے جو ہمارے کوڈ کو درکار ہے۔ ایک ایپلی کیشن WSAStartup () کو درست Winsock DLL کو شروع کرنے کے لیے کال کرتی ہے۔
  • ساکٹ : ایک شے (درحقیقت ، اس کی تعریف یو_ نٹ ، بغیر دستخط شدہ عدد ، ونساک میں ہوتی ہے۔ h — پارٹیوں میں چھوٹی بات کے لیے جاننا اچھا ہے ) ایپلی کیشنز کے ذریعے ساکٹ ہینڈل کو ذخیرہ کرنے کے لیے استعمال کیا جاتا ہے۔
  • SOCKADDR_IN : ایک ایپلیکیشن اس ڈھانچے کو استعمال کرتی ہے تاکہ یہ بتائے کہ ساکٹ کو کیسے کام کرنا چاہیے۔ SOCKADDR_IN ایک IP ایڈریس اور پورٹ نمبر کے لیے فیلڈز پر مشتمل ہے:
struct sockaddr_in

{

  short sin_family;         // Protocol type

  u_short sin_port;         // Port number of socket

  struct in_addr sin_addr;  // IP address

  char sin_zero[8];         // Unused

};

پہلا فیلڈ پروٹوکول کی قسم ہے ، جو عام طور پر AF_INET (TCP/IP) ہوتا ہے۔ چونکہ سننے والی ساکٹ مشین کے نیٹ ورک ایڈریس سے متعلق نہیں ہے جس پر یہ رہتی ہے ، ونساک خود بخود ایک IP ایڈریس اور پورٹ نمبر تخلیق کرنے پر ساکٹ کو تفویض کرتا ہے۔

ہم اپنا پہلا سننے والا سرور مذکورہ ڈھانچے اور نیٹ ورک افعال کی ایک چھوٹی فوج کے ساتھ بنائیں گے۔

#include <windows.h>

#include <winsock.h>

#include <stdio.h>



#define NETWORK_ERROR -1

#define NETWORK_OK     0



void ReportError(int, const char *);





int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmd, int nShow)

{

	WORD sockVersion;

	WSADATA wsaData;

	int nret;



	sockVersion = MAKEWORD(1, 1);			// We'd like Winsock version 1.1





	// We begin by initializing Winsock

	WSAStartup(sockVersion, &wsaData);





	// Next, create the listening socket

	SOCKET listeningSocket;



	listeningSocket = socket(AF_INET,		// Go over TCP/IP

			         SOCK_STREAM,   	// This is a stream-oriented socket

				 IPPROTO_TCP);		// Use TCP rather than UDP



	if (listeningSocket == INVALID_SOCKET)

	{

		nret = WSAGetLastError();		// Get a more detailed error

		ReportError(nret, "socket()");		// Report the error with our custom function



		WSACleanup();				// Shutdown Winsock

		return NETWORK_ERROR;			// Return an error value

	}





	// Use a SOCKADDR_IN struct to fill in address information

	SOCKADDR_IN serverInfo;



	serverInfo.sin_family = AF_INET;

	serverInfo.sin_addr.s_addr = INADDR_ANY;	// Since this socket is listening for connections,

							// any local address will do

	serverInfo.sin_port = htons(8888);		// Convert integer 8888 to network-byte order

							// and insert into the port field





	// Bind the socket to our local server address

	nret = bind(listeningSocket, (LPSOCKADDR)&serverInfo, sizeof(struct sockaddr));



	if (nret == SOCKET_ERROR)

	{

		nret = WSAGetLastError();

		ReportError(nret, "bind()");



		WSACleanup();

		return NETWORK_ERROR;

	}





	// Make the socket listen

	nret = listen(listeningSocket, 10);		// Up to 10 connections may wait at any

							// one time to be accept()'ed



	if (nret == SOCKET_ERROR)

	{

		nret = WSAGetLastError();

		ReportError(nret, "listen()");



		WSACleanup();

		return NETWORK_ERROR;

	}





	// Wait for a client

	SOCKET theClient;



	theClient = accept(listeningSocket,

			   NULL,			// Optionally, address of a SOCKADDR_IN struct

			   NULL);			// Optionally, address of variable containing

							// sizeof ( struct SOCKADDR_IN )



	if (theClient == INVALID_SOCKET)

	{

		nret = WSAGetLastError();

		ReportError(nret, "accept()");



		WSACleanup();

		return NETWORK_ERROR;

	}





	// Send and receive from the client, and finally,

	closesocket(theClient);

	closesocket(listeningSocket);





	// Shutdown Winsock

	WSACleanup();

	return NETWORK_OK;

}





void ReportError(int errorCode, const char *whichFunc)

{

   char errorMsg[92];					// Declare a buffer to hold

							// the generated error message

   

   ZeroMemory(errorMsg, 92);				// Automatically NULL-terminate the string



   // The following line copies the phrase, whichFunc string, and integer errorCode into the buffer

   sprintf(errorMsg, "Call to %s returned error %d!", (char *)whichFunc, errorCode);



   MessageBox(NULL, errorMsg, "socketIndication", MB_OK);

}

ایک چیز جو آپ کوڈ کے بارے میں فوری طور پر محسوس کر سکتے ہیں وہ ہے غلطی کی جانچ میں کی جانے والی کوشش کی مقدار۔ جب بھی کوئی غلطی ہوتی ہے ، کوڈ WSAGetLastError () کے ساتھ ایک مخصوص غلطی کوڈ حاصل کرتا ہے اور نتیجہ کو nret میں محفوظ کرتا ہے۔ غلطی کا کوڈ پھر اسٹرنگ کے ساتھ بھیجا جاتا ہے جس میں ناکام فنکشن کے نام کی نشاندہی کی جاتی ہے جسے رپورٹ ایرر () نامی کسٹم فنکشن میں دکھایا جاتا ہے۔ وہاں ، ایک غلطی کا پیغام بنایا گیا ہے اور صارف کو MessageBox () پر کال کے ساتھ دکھایا گیا ہے ، جو کہ معیاری WinAPI کا حصہ ہے۔ مثال کے طور پر ، اگر سن () 10093 کے غلطی کوڈ (WSANOTINITITALISED کے طور پر بیان کیا گیا) کے ساتھ ناکام ہو گیا تو ، غلطی کی ختم شدہ تار “سننے کے لیے کال () 10093 کی واپسی کی غلطی ہوگی۔” آپ ، سمجھدار ڈویلپر ، پھر کوڈ کو دیکھیں گے اور دریافت کریں گے کہ غلطی اس وجہ سے ہوئی ہے کیونکہ WSAStartup () پر ایک کامیاب کال ابھی تک نہیں کی گئی تھی۔

الیگزینڈر پاولوف نے تقریبا Report ایک درجن عام ساکٹ کی غلطیوں کی تفصیل شامل کرنے کے لیے اس رپورٹ ایرر () کو بڑھایا۔ اس کے اپ گریڈ شدہ ورژن کا استعمال کرتے ہوئے ، آپ کو کوڈ کو کیا مطلب ہے اسے تلاش کرنے کی ضرورت نہیں رہے گی ، اور آپ کا پروگرام آپ کی طرف سے بہت کم کوشش کے ساتھ بہت زیادہ صارف دوست بن جاتا ہے۔

NETWORK_ERROR اور NETWORK_OK کی وضاحتیں بھی شامل ہیں۔ جب آپ اپنے نیٹ ورکنگ افعال کی واپسی کی قیمت چیک کرتے ہیں تو یہ کارآمد ثابت ہوسکتے ہیں۔ اگر آپ کے افعال ان اقدار میں سے ایک کو واپس کرتے ہیں تو ، کالنگ فنکشن کسی بھی غلطیوں کو ظاہر کرنے کے لیے ایک سادہ مساوات کا امتحان دے سکتا ہے: اگر (myNetworkingFunction () == NETWORK_ERROR) {…}۔ کالنگ فنکشن WSAGetLastError () کے ساتھ ایک مخصوص کوڈ حاصل کرسکتا ہے اور اس کے مطابق غلطی کو سنبھال سکتا ہے۔ بالآخر ، ایک اچھی ایرر ہینڈلنگ اسکیم کو نافذ کرنے سے آپ کو کئی دن یا ہفتوں کی ترقی کا وقت بچ جائے گا کیونکہ آپ کو فوری طور پر معلوم ہو جائے گا کہ آپ کا پروگرام کیوں ناکام ہو گیا ہے۔

ایک نیا کلائنٹ کنکشن واپس کرنے کے علاوہ ، قبول کریں () سرور کو کلائنٹ کے بارے میں معلومات نکالنے کی اجازت دیتا ہے بجائے اس کے کہ اضافی فنکشن کالز یا وقت کی ضرورت ہوتی ہے نازک) اس فعالیت سے فائدہ اٹھانے کے لیے ، sockaddr_in struct cast کے ایڈریس کو sockaddr پوائنٹر ، یعنی (LPSOCKADDR) اور aSockaddrInStructure پر منتقل کریں۔ نیز ، ایک انٹیجر متغیر کا اعلان کریں ، انٹ کی قدر کو ساکڈر ڈھانچے کے سائز پر سیٹ کریں ، اور تیسرے پیرامیٹر کے طور پر انٹیجر کا پتہ پاس کریں۔ اگر فنکشن کال کے بعد ایڈریس کی معلومات واپس کرنی ہے تو لمبائی کا پیرامیٹر موجود ہونا چاہیے۔

jdarnold نے ہمیں خبردار کیا ہے کہ اس تیسرے پیرامیٹر سے متعلق MSDN دستاویزات پر یقین نہ کریں: “MSDN دستاویزات کا مطلب ہے کہ آپ کو addrlen میں پاس کرنے کی ضرورت نہیں ہے ، کہ یہ صرف ایک اختیاری آؤٹ پٹ پیرامیٹر ہے ، لیکن وہ غلط ہیں۔ sockaddr بفر میں ، اور آؤٹ باؤنڈ [Winsock] بھرتا ہے کہ کتنے [Winsock] استعمال ہوئے ہیں۔

اپنے رابطے بنانا۔

کسی اور سے جڑنے کے لیے ساکٹ بنانا HOSTENT ڈھانچے کو چھوڑ کر ، زیادہ تر ایک جیسے افعال استعمال کرتا ہے:

  • ہوسٹنٹ : ایک ڈھانچہ جو ساکٹ کو بتاتا تھا کہ کس کمپیوٹر اور بندرگاہ کو جوڑنا ہے۔ یہ ڈھانچے عام طور پر LPHOSTENT متغیرات کے طور پر ظاہر ہوتے ہیں ، جو محض HOSTENT ڈھانچے کی طرف اشارہ کرتے ہیں۔ جیسا کہ آپ ونڈوز کے لیے کوڈ کرتے ہیں ، آپ عام طور پر دیکھیں گے کہ ایل پی سے پہلے کا کوئی بھی ڈیٹا ٹائپ اس بات کی نشاندہی کرتا ہے کہ یہ ٹائپ دراصل ایک “بیس” ٹائپ کی طرف اشارہ ہے (مثال کے طور پر ، ایل پی سی ایس ٹی آر ایک سی سٹرنگ کا اشارہ ہے ، جسے چار بھی کہا جاتا ہے * ).

تو ، آئیے کوڈ پر جائیں:

So, let’s get right to the code:

#include <windows.h>

#include <winsock.h>

#include <stdio.h>



#define NETWORK_ERROR -1

#define NETWORK_OK     0



void ReportError(int, const char *);





int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmd, int nShow)

{

	WORD sockVersion;

	WSADATA wsaData;

	int nret;



	sockVersion = MAKEWORD(1, 1);





	// Initialize Winsock as before

	WSAStartup(sockVersion, &wsaData);





	// Store information about the server

	LPHOSTENT hostEntry;



	hostEntry = gethostbyname("www.yahoo.com");	// Specifying the server by its name;

							// another option: gethostbyaddr()



	if (!hostEntry)

	{

		nret = WSAGetLastError();

		ReportError(nret, "gethostbyname()");	// Report the error as before



		WSACleanup();

		return NETWORK_ERROR;

	}





	// Create the socket

	SOCKET theSocket;



	theSocket = socket(AF_INET,			// Go over TCP/IP

			   SOCK_STREAM,			// This is a stream-oriented socket

			   IPPROTO_TCP);		// Use TCP rather than UDP



	if (theSocket == INVALID_SOCKET)

	{

		nret = WSAGetLastError();

		ReportError(nret, "socket()");



		WSACleanup();

		return NETWORK_ERROR;

	}





	// Fill a SOCKADDR_IN struct with address information

	SOCKADDR_IN serverInfo;



	serverInfo.sin_family = AF_INET;



	// At this point, we've successfully retrieved vital information about the server,

	// including its hostname, aliases, and IP addresses.  Wait; how could a single

	// computer have multiple addresses, and exactly what is the following line doing?

	// See the explanation below.



	serverInfo.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list);



	serverInfo.sin_port = htons(80);		// Change to network-byte order and

							// insert into port field





	// Connect to the server

	nret = connect(theSocket,

		       (LPSOCKADDR)&serverInfo,

		       sizeof(struct sockaddr));



	if (nret == SOCKET_ERROR)

	{

		nret = WSAGetLastError();

		ReportError(nret, "connect()");



		WSACleanup();

		return NETWORK_ERROR;

	}





	// Successfully connected!





	// Send/receive, then cleanup:

	closesocket(theSocket);

	WSACleanup();

}





void ReportError(int errorCode, const char *whichFunc)

{

   char errorMsg[92];					// Declare a buffer to hold

							// the generated error message

   

   ZeroMemory(errorMsg, 92);				// Automatically NULL-terminate the string



   // The following line copies the phrase, whichFunc string, and integer errorCode into the buffer

   sprintf(errorMsg, "Call to %s returned error %d!", (char *)whichFunc, errorCode);



   MessageBox(NULL, errorMsg, "socketIndication", MB_OK);

}

فہرست میں سب سے پیچیدہ لائن درج ذیل ہے۔ :serverInfo.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list);


کیونکہ یہ کئی آپریشن کرتا ہے — ان میں سے ایک نسبتا hidden پوشیدہ — ایک ساتھ۔ آئیے اسے مرحلہ وار الگ کریں:

HOSTENT ڈھانچے کے h_addr_list ممبر کو بنیادی طور پر چار ** h_addr_list کے طور پر بیان کیا گیا ہے ، جو ڈور کی ایک صف ہے ، یا char *’s۔ gethostbyname () نے اس فہرست میں سرور کے تمام معلوم پتوں کی شناخت اور کاپی کی۔ تاہم ، کیا متعدد پتوں کا تصور بنیادی طور پر معنی رکھتا ہے؟ دراصل ، یہ کرتا ہے۔ آپ کے کمپیوٹر میں ، حقیقت میں ، نیٹ ورکنگ کے عام پتے ہیں۔ آپ کا انٹرنیٹ ایڈریس 205.182.67.96 ہوسکتا ہے ، آپ کا LAN ایڈریس 10.0.0.2 ہوسکتا ہے ، اور تمام کمپیوٹر جن پر ونڈوز انسٹال ہے قدرتی طور پر 127.0.0.1 کا “لوپ بیک” ایڈریس ہے ، جسے کمپیوٹر مقامی نیٹ ورک پر خود سے رجوع کرتا ہے۔ . یہی تصور انٹرنیٹ ایڈریس یا آئی پی کے دائرے میں لاگو ہوتا ہے ، یہی وجہ ہے کہ کسی ایک ایڈریس کے لیے اسٹوریج کی جگہ کے بجائے ایک فہرست کی ضرورت ہوتی ہے۔ نوٹ کریں کہ ترجیحی۔ ایڈریس ، جو کہ سب سے زیادہ قابل رسائی ایڈریس ہے ، ہمیشہ فہرست کے پہلے عنصر میں نقل کیا جاتا ہے ، اس کے بعد دوسرا ترجیحی یا دوسرے پتے ہوتے ہیں۔

*hostEntry-> h_addr_list کیا کر رہا ہے؟ آپ اندازہ لگا سکتے ہیں کہ ڈیفریشن آپریٹر (*) فہرست میں کسی ایک پتے تک رسائی کے لیے استعمال ہو رہا ہے۔ تاہم ، ایک مخصوص انڈیکس فراہم کرنے میں ناکامی کی وجہ سے ، ڈیفیرنس آپریشن خود بخود پہلا ، ترجیحی پتہ ظاہر کرتا ہے ۔ یہ خاص حصہ *hostEntry-> h_addr_list [0] کے برابر ہے ، جس کے وجود کی ضمانت ہے کیونکہ سرور کا کم از کم ایک پتہ ہونا ضروری ہے۔

اگلا ، dereferencing آپریشن کے ذریعے لوٹا گیا چار * in_addr * یا LPIN_ADDR میں ڈال دیا جاتا ہے۔ آخر میں ، ایک اور ڈیفریشن آپریشن کیا جاتا ہے جس میں پوائنٹر کے ذریعہ حوالہ دیا گیا in_addr ڈھانچہ واپس آتا ہے ، جو صرف ایک ہی پتہ رکھ سکتا ہے۔ نتیجے میں_ addr ساخت پھر serverInfo.sin_addr کو تفویض کی جاتی ہے۔ بعد کا کنیکٹ () سرور سے کنکشن بناتے وقت ایک ایڈریس کو پیرامیٹر کے طور پر لیتا ہے۔

اگر سرور کا آئی پی ایڈریس معلوم ہے تو ، ایک درست HOSTENT حاصل کیا جا سکتا ہے gethostbyaddr () کے استعمال سے (جیسا کہ gethostbyname () پچھلی لسٹنگ میں استعمال کیا گیا ہے):

LPHOSTENT hostEntry;

in_addr iaHost;



iaHost.s_addr = inet_addr("204.52.135.52");



hostEntry = gethostbyaddr((const char *)&iaHost, sizeof(struct in_addr), AF_INET);



if (!hostEntry)

{

	// Handle accordingly

}

اس معاملے میں ، inet_addr () آئی پی ایڈریس کی نشاندہی کرنے والی ایک تار کو براہ راست in_addr ڈھانچے میں کاپی کرنے کے لیے استعمال کیا جاتا ہے۔ اس کے بعد ، ڈھانچے کا پتہ ایک const char * میں ڈال دیا جاتا ہے جیسا کہ gethostbyaddr ()۔ دونوں طریقوں کو سرور ایڈریس کو حل کرنے کے طور پر کہا جاتا ہے کیونکہ ونساک جزوی معلومات سے مکمل ایڈریس ریکارڈ لوٹاتا ہے۔

کچھ اضافی نوٹ: پورٹ 80 کو صرف اس لیے استعمال کیا گیا کہ انٹرنیٹ ویب پیج کی منتقلی اسی بندرگاہ پر ہوتی ہے۔ اگر آپ کسی مخصوص فائل کی درخواست کرنے والے ویب سرور کو سٹرنگ بھیجنا چاہتے ہیں اور کچھ واپس لینے کی کوشش کرتے ہیں تو آپ کے پاس بہت آسان ویب براؤزر ہوگا۔ یقینا ، اس تار میں مکمل HTTP کمانڈ شامل ہونا ضروری ہے۔ یہ بہت اچھا ہے کہ ہم دوسرے کمپیوٹرز کو سن سکتے ہیں اور رابطہ قائم کرسکتے ہیں ، لیکن مواصلات میں بھیجنا اور وصول کرنا بھی شامل ہے۔

بھیجنا اور وصول کرنا۔

بھیجنا () آسانی سے کافی ہے ، send () فنکشن کے ذریعے:

int send(

  SOCKET s,

  const char * FAR buf,

  int len,

  int flags

);

بنیادی طور پر آپ جو چاہیں اسے بفر میں کاپی کریں اور ڈیٹا کو دوسرے سرے تک پہنچانے کے لیے مربوط ساکٹ پر send () فنکشن استعمال کریں:

char buffer[256];		// Declaring a buffer on the stack

char *buffer = new char[256];	// or on the heap



ZeroMemory(buffer, 256);

strcpy(buffer, "Pretend this is important data.");



nret = send(theSocket,

	    buffer,

	    strlen(buffer),	// Note that this specifies the length of the string; not

				// the size of the entire buffer

	    0);			// Most often is zero, but see MSDN for other options



delete [] buffer;		// If and only if the heap declaration was used



if (nret == SOCKET_ERROR)

{

	// Get a specific code

	// Handle accordingly

	return NETWORK_ERROR;

} else {

	// nret contains the number of bytes sent

}

وصول کرنا ایک ہی عمل ہے ، پیچھے کی طرف:

char buffer[256];		// On the stack

char *buffer = new char[256];	// or on the heap



nret = recv(theSocket,

	    buffer,

	    256,		// Complete size of buffer

	    0);



delete [] buffer;		// Manipulate buffer, then delete if and only if

				// buffer was allocated on heap



if (nret == SOCKET_ERROR)

{

	// Get a specific code

	// Handle accordingly

	return NETWORK_ERROR;

} else {

	// nret contains the number of bytes received

}

دلچسپ بات یہ ہے کہ مائیکروسافٹ آؤٹ لک میں ٹول بار پر ایک بٹن موجود ہے جس کا لیبل لگا ہوا ہے “Send/Recv”۔ کیا “Receive” کا اختصار “Recv” ہے تاکہ یہ یقینی بنایا جاسکے کہ بٹن صحیح نظر آتا ہے ، یا یہ پروگرامر کی عادت ہے کہ recv () کو کئی بار ٹائپ کرنا؟ اپنے سازشی نظریات بنائیں ( ایک بار پھر ، پارٹیوں میں چھوٹی باتوں کے لیے اچھا )

یہ وہ جگہ ہے جہاں میں اپنے ونساک پروگرام لکھتے وقت تھوڑی پریشانی میں پڑ گیا۔ صرف recv () کا استعمال بہت اچھا ہے جب آپ کو معلوم ہو کہ آپ کتنا ڈیٹا وصول کر رہے ہوں گے (جیسے کسی گیم میں ، جہاں پہلا بائٹ کمانڈ ہو سکتا ہے اور اگلا بائٹ پیرامیٹر وغیرہ ہو سکتا ہے) ، لیکن جب آپ ڈان پتہ نہیں ، تم کیا کرتے ہو؟ اگر آپ جو ڈیٹا حاصل کر رہے ہیں اسے ایک نئی لائن کیریکٹر (جاوا کلائنٹس کے سی سرورز سے بات کرنے کا ایک عام مسئلہ) کے ذریعے ختم کر دیا گیا ہے ، آپ اس کردار تک ہر چیز پر قبضہ کرنے کے لیے ایک ریڈ لائن () فنکشن لکھ سکتے ہیں۔ یہاں میں نے استعمال کیا ہے:

char * readLine()

{

   vector theVector;

   char buffer;

   int bytesReceived;



   while (true)

   {

      bytesReceived = recv(theSocket, &buffer, 1, 0);

      if (bytesReceived <= 0)

         return NULL;



      if (buffer == '\n')

      {

         char *pChar = new char[theVector.size() + 1];

         memset(pChar, 0, theVector.size() + 1);



         for (int f = 0; f < theVector.size(); f++)

            pChar[f] = theVector[f];



         return pChar;

      } else {

         theVector.push_back(buffer);

      }

   }

}

صف کی بجائے ایک ویکٹر استعمال کیا جاتا ہے کیونکہ لائن کی لمبائی کے مطابق اس کے اسٹوریج کی جگہ خود بخود بڑھ جاتی ہے۔ اگر recv () غلطی لوٹاتا ہے (بائٹس کی طرف سے اشارہ صفر سے کم ہونے کی وجہ سے) ، NULL لوٹا دیا جاتا ہے۔ چونکہ یہ ایک امکان ہے ، کالنگ افعال کو یقینی بنانا چاہیے کہ ریڈ لائن () سے لوٹا گیا تار استعمال سے پہلے درست ہے۔ لوپ کے اندر ، ساکٹ سے ایک ہی چار وصول کیا جاتا ہے اور ، اگر نیا لائن کردار نہیں ہے تو ، ویکٹر میں شامل کیا جاتا ہے۔ اگر یہ ایک نیو لائن کریکٹر ہے تو ویکٹر کے مندرجات کو C سٹرنگ میں کاپی کر کے واپس کر دیا جاتا ہے۔ تار کو ویکٹر اور میمسیٹ () ‘ٹیڈ سے صفر تک ایک چار بڑا قرار دیا گیا ہے تاکہ واپس آنے والی لائن خود بخود NULL- ختم ہوجائے گی۔ NULL کے ساتھ ڈور ختم کرنا غیر معمولی غلطیوں کو روکتا ہے اور عام طور پر پروگرامنگ کی اچھی مشق ہے۔

نہ ہی اس چالاکی سے بہتر ورژن کو بیک اسپیس کی حمایت اور نئی لائن کے کردار کو آسانی سے تبدیل کرنے کی صلاحیت کے ساتھ پیش کرتا ہے۔

// Code originally written by Nor.  Modified slightly to

// support the MessageBox() API, make logic more readable,

// align spacing, and add comments.  Posted with permission.



#define backKey '\b'					// To disable backspaces, #define backKey NULL

#define newLine '\n'

#define endStr  '\0'



char *readLine(SOCKET s)

{

	vector theVector;

	char buffer;

	char *pChar;

	int bytesReceived;



	while (true)

	{

		bytesReceived = recv(s, &buffer, 1, 0);



		if (bytesReceived <= 0)

		{

			MessageBox(NULL, "recv() returned nothing.", "socketIndication", MB_OK);

			return NULL;

		}



		switch (buffer)

		{

			case backKey:			// Handle backspace

				if (theVector.size() > 0)

					theVector.pop_back();

				break;

			case endStr:			// If end of string char reached,

			case newLine:			// or if end of line char reached,

				pChar = new char[theVector.size() + 1];

				memset(pChar, 0, theVector.size() + 1);



				for (int f = 0; f < theVector.size(); f++)

					pChar[f] = theVector[f];

				return pChar;

				break;

			default:			// Any regular char

				theVector.push_back(buffer);

				break;

		}

	}

}

غیر مسدود اور غیر متزلزل ساکٹ۔

اس مقام تک ہم ساکٹ کو مسدود کرنے کے بارے میں بات کر رہے ہیں ، جہاں ایک فنکشن کو کال کرنا جیسے قبول () صارف کے منسلک ہونے کا غیر معینہ مدت تک انتظار کرتا ہے۔ غیر بلاک کرنے والا ساکٹ جب بھی اسے کچھ کرنے کے لیے کہا جاتا ہے ، فورا returns واپس آجاتا ہے ، یا تو کامیاب نتیجہ ، غلطی ، یا کچھ نہیں (اس بات کی نشاندہی کرتا ہے کہ بعد میں کچھ حاصل ہوگا)۔ اس قسم کو استعمال کرنے کا نقصان یہ ہے کہ آپ کو دستی طور پر ساکٹ سے پوچھنا پڑے گا تاکہ یہ معلوم کیا جا سکے کہ آپ کے ہر فنکشن پر کوئی نتیجہ آیا ہے یا نہیں۔ آپ ساکٹ کا ایک مجموعہ سلیکٹ () فنکشن میں منتقل کر سکتے ہیں تاکہ دیکھ سکیں کہ کونسے پڑھنے ، لکھنے یا غلطیاں واپس کرنے کے لیے تیار ہیں۔

غیر مطابقت پذیر ساکٹ استعمال کرنے والے افعال بھی فوری طور پر واپس آتے ہیں ، لیکن جب آپ کوئی مخصوص واقعہ پیش آئے تو آپ اپنی ونڈو کے طریقہ کار کو بھیجنے کے لیے ایک پیغام کی وضاحت کر سکتے ہیں۔ مثال کے طور پر ، آپ ساکٹ کو SOCKET_GOTMSG پیغام بھیج سکتے ہیں جب بھی اسے کچھ ملتا ہے۔ عام طور پر غلطیوں کی جانچ پڑتال کرنا مشکل ہے (بوجھل ، لیکن ضروری) جب آپ کو ساکٹ پیغام ملتا ہے تاکہ بعد میں غیر ضروری مسائل پیدا نہ ہوں۔ پہلے ، آئیے کچھ افعال کی وضاحت کرتے ہیں جو ہم ایک غیر مطابقت پذیر ساکٹ قائم کرنے کے لئے استعمال کریں گے:

  • int WSAAsyncSelect (SOCKET s، HWND hwnd، unsigned int wMsg، long lEvent)
    یہ فنکشن ایک ساکٹ کو غیر متزلزل کے طور پر شناخت کرنے اور اس کے ساتھ کسی پیغام کو جوڑنے کے لیے استعمال ہوتا ہے۔ s وہ ساکٹ ہے جس کے ساتھ آپ کام کر رہے ہیں۔ hwnd ونڈو کا ہینڈل ہے جو پیغام وصول کرے گا جب ساکٹ ایونٹ تیار کرے گا۔ wMsg وہ پیغام ہے جسے آپ اپنی ونڈو کے طریقہ کار پر بھیجنا چاہتے ہیں (ایک مثال اوپر سے SOCKET_GOTMSG پیغام ہے)۔ لیونٹ پیرامیٹر ایک یا زیادہ جھنڈے لیتا ہے جو ساکٹ کو بتاتے ہیں کہ کن ایونٹس پر آپ کا پیغام بھیجنا ہے۔ ان جھنڈوں میں سے کچھ یہ ہیں:
    • FD_READ : ساکٹ ڈیٹا حاصل کرنے کے لیے تیار ہے۔
    • FD_WRITE : ساکٹ ڈیٹا بھیجنے کے لیے تیار ہے۔
    • FD_ACCEPT : سرورز میں استعمال کیا جاتا ہے ، یہ پیغام اس بات کی نشاندہی کرتا ہے کہ صارف نے کنیکٹ کیا ہے۔
    • FD_CONNECT : کلائنٹ ایپلی کیشنز میں استعمال کیا جاتا ہے ، یہ پیغام آپ کو بتاتا ہے کہ ساکٹ جڑا ہوا ہے۔
    • FD_CLOSE : ساکٹ ابھی بند کیا گیا ہے۔
  • WSAGETSELECTERROR (LPARAM lparam) اس بات
    کا تعین کرتا ہے کہ ساکٹ نے کوئی غلطی واپس کی ہے۔ تکنیکی طور پر ، یہ ایک فنکشن نہیں ہے بلکہ ایک میکرو ہے ( آپ واقعی اس چھوٹی سی فیکٹائڈ والی پارٹیوں میں چھوٹی باتیں پیدا کرسکتے ہیں )۔
  • WSAGETSELECTEVENT (LPARAM lparam) Winsag2.h
    میں بیان کردہ ایک اور مفید میکرو WSAGETSELECTEVENT () ہے ، جو کہ یہ دیکھنے کے لیے استعمال ہوتا ہے کہ ساکٹ نے کیا کیا ہے۔

تو ، آئیے ایک غیر متزلزل ساکٹ مرتب کریں:

بناتے ہوئے 
شروع کرتے ہیں جسے ونڈوز ہم سے رابطہ کرنے کے لیے استعمال کرے گا جب کچھ ہو جائے گا 
#DERFINE THERE_WAS_A_SOCKET_EVENT WM_USER + 100 // WM_USER اپنی مرضی کے پیغامات کے لیے ایک بنیاد ہے 


// Somewhere in our initialization code after CreateWindow (), we call WSAAsyncSelect ()

WSAAsyncSelect ( theSocket, hwnd, THERE_WAS_A_SOCKET_EVENT, FD_READ | FD_WRITE | FD_CONNECT | ... );



// This translates: Windows, please contact me using the THERE_WAS_A_SOCKET_EVENT flag that I

// previously defined whenever there's data to read (FD_READ), or when I'm free to send data

// (FD_WRITE), or when I've successfully connected to someone else (FD_CONNECT), or when...etc.

// In our window procedure (the function which handles all the messages that Windows sends to your app)

LRESULT WINAPI TheWindowProcedure ( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )

{



	switch ( msg )

	{

		case THERE_WAS_A_SOCKET_EVENT:

			if ( WSAGETSELECTERROR ( lParam ) )

			{	// If an error occurred,

				closesocket ( theSocket );

				WSACleanup ();				// Shutdown Winsock

				return NETWORK_ERROR;

			}

			switch ( WSAGETSELECTEVENT ( lParam ) )

			{	// What happened, exactly?

				case FD_READ:

					// Receive data

					break;

				case FD_WRITE:

					// Write data

					break;

				case FD_CONNECT:

					// Just connected to server

					break;

				case ...				// Same setup for other flags

					break;

			}

			break;



		// other case statements with logic that handles other Windows messages



	}

}

نوٹ کریں کہ آپ ہر ایک ایونٹ کے لیے ایک پیغام کی وضاحت نہیں کر سکتے ، جیسے FOC_READ کے لیے SOCKET_GOTMSG اور پھر FOC_CONNECT کے لیے SOCKET_CONNECTED۔ اس کی وجہ یہ ہے کہ ہر پرچم کو سیٹ اپ کرنے کے لیے WSAAsyncSelect () کو بار بار کال کرنے سے WSAAsyncSelect () پر آخری کال کے اثرات منسوخ ہو جائیں گے۔