Падручнік Джоні па Winsock

2015-11-11 13:38:48 / author: sharkov views 650Total views: 650 / 13Views for 7 days: 13
Thanks to Best cliparts
source article: http://johnnie.jerrata.com/winsocktutorial/

By Johnnie Rose, Jr.

Калі вы калі знарок прыбыў у мой winsock на ўроку, Вы ўжо хутчэй за ўсе знайшлі ідэю вашых уласных прыкладанняў зносін праз Інтэрнэт як захапляльная перспектыва, як я. Або, магчыма, хто-то ўжо знайшоў перспектыва аднолькава цікавая і вам было даручана даставіць гэта бачанне ў рэальнасць. У любым выпадку, праграма winsock сеткавы сэрвіс і гэты падручнік дапаможа вам у дасягненні Вашых мэтаў гандлевага прадпрыемства, проста даследуючы сферу сеткавае праграмаванне для асабістага выкарыстання, або што-то ў паміж.

Вось што мы будзем асвятляць:

  • Стварэнне слухае сокета: улічваючы невялікую армію сеткавыя функцыі, мы можам пабудаваць праграму, якая цярпліва чакае ўваходных злучэнняў? (Так, мы можам.)
  • Робячы свае ўласныя сувязі: даецца некалькі больш функцый, мы можам стварыць праграму, якая паспяхова звязвае з прослушивающий сервер? (Так, мы можам.)
  • Адпраўкі і атрымання: пасля таго, як мы дасягнулі актыўны злучэнне, як мы выкарыстоўваем яго для абмену дадзенымі паміж двума праграмамі? (Як вы ўжо здагадаліся---Send() і recv " (змяненне хуткасці().)
  • Неблокирующий і асінхронныя сокеты: як мы можам павысіць эфектыўнасць нашага кода шляхам ўкаранення розных сеткавых схеме? (Мы важдаемся з нашай працэдуры вокны няшмат.)
  • Больш Туториалов і спасылак: якія рэсурсы там вышэй і па-за межамі гэтага падручніка? Я магу вылучыць 3, якія павінны трымаць вас заняты нейкі час (пасля таго як вы переварили мой падручнік, вядома :-).
  • Каментары і крытыка: вось ваш шанец, каб ацаніць падручнік, задаваць пытанні або пакідаць каментары.

Хоць вы, можа быць, імкнуцца дасягнуць уражлівых кропка, у якой ваша прыкладанне паспяхова робіць свае першыя сувязі, быць у курсе канцэпцыі, якія ляжаць у код. Старайцеся пазбягаць простага маніпулявання дадзены код, каб задаволіць вашыя надзенныя патрэбы, і замест таго, каб вызначыць патрабаванні вашага прыкладання і толькі потым ажыццявіць тое, што здаецца лепшым рашэннем. Хопіць майго Дзэн распрацоўкі праграмнага забеспячэння кансультацыі для цяпер, давайце рабіць некаторыя сеткавае праграмаванне...

Не саромейцеся, каб загрузіць увесь падручнік лістынг. Памятаеце, што любы код, прадстаўлены ў гэтым падручніку павінны быць звязаныя з winsock на бібліятэку, звычайна wsock32.lib або чым-то аналагічным назвай. Таксама, пры выкарыстанні код у дакладнасці так, як прадстаўлена ў падручніку ў патрэбную IDE (Dev-C++, Microsoft VC++ і c++ Builder і інш.), вырашылі пабудаваць праект Windows з функцыі winmain (), каб пазбегнуць памылак.

Стварэнне слухае сокета

Заяўкі на абслугоўванне па-за машыны называюцца серверамі. Сервер прыкладанняў праслухоўваць кліентаў шляхам ініцыялізацыі аднаго або больш сокетамі. Калі кліент падключаецца да аднаго з гэтых слухачы сокеты, сервер атрымлівае паведамлення ад winsock, прымае падключэнне, і пачынае адпраўляць і перахопліваць паведамленні да і ад новага кліента. Бадай, найбольш просты метад, з дапамогай якога сервера апрацоўваць некалькі кліентаў, каб спарадзіць новы струмень для кожнага кліенцкага злучэння. Гэтая мадэль сервера часцей за ўсе выкарыстоўвае блакавальныя сокеты, якія часова прыпыніць чакаць ўваходных дадзеных, новае злучэнне, і іншых сеткавых мерапрыемствах. Па-першае, давайце вызначым некаторыя мадэлі, якія мы будзем павінны ініцыялізаваць блакавальны сокет:

  • WSADATA: гэтая структура выкарыстоўваецца для запыту аперацыйнай сістэмы версія winsock наш код патрабуе. Дадатак выклікае WSAStartup() для ініцыялізацыі правільныя параметры winsock dll файлы.
  • SOCKET:: аб'ект (па сутнасці, гэта вызначаецца як u_int беззнаковое цэлы лік, у winsock.span---карысна ведаць для smalltalk на вечарынках) выкарыстоўваецца прыкладаннямі для захоўвання дэскрыптар сокета.
  • 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

};
 

У першым поле паказваецца Тып пратаколу, які звычайна заснаваных на BSD (ТСР/IP). Як слухае сокета не хвалюе сеткавы адрас машыны, на якой ен пражывае, у winsock аўтаматычна прызначае IP-адрас і нумар порта для праслухоўвання сокетаў да стварэння.

Мы пабудуем нашу першую праслухоўвання серверы з вышэйазначанымі структурамі і невялікую армію сеткавых функцый:

#include 

#include 

#include 



#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. Код памылкі адпраўляецца разам са радком з указаннем ПРОЗВІШЧА, імя які адмовіў функцыі ў карыстацкую функцыю з імем ReportError(). Няма, паведамленне пра памылку ствараецца і паказваецца карыстальніку з дапамогай выкліку функцыі messagebox(), якая з'яўляецца часткай стандартнага WinAPI пакуль. Напрыклад, прыйшлося слухаць() завяршылася памылкай код 10093 (вызначаецца як WSANOTINITIALISED), скончаная радок памылкі будзе "заклік слухаць() вярнуў памылку 10093!". Вы, разумнага распрацоўніка, затым знайсці код і выявіць, што адбылася памылка, таму што паспяховы выклік WSAStartup() яшчэ не быў зроблены.

Аляксандар Паўлаў пашырыў гэтую ReportError() і ўключае апісання каля дзясятка распаўсюджаных памылак сокета. Выкарыстоўваючы яго мадэрнізаванай версіі, вам больш не спатрэбіцца пошук што азначае код, і твая праграма становіцца значна больш зручнай для карыстальнікаў з вельмі мала намаганняў з вашага боку.

Акрамя таго, уключаны вызначае для NETWORK_ERROR і NETWORK_OK. Гэта можа быць карысна пры праверцы вяртаецца значэння ўласных сеткавых функцый. Калі ваш функцыі вяртаецца адно з наступных значэнняў, якая выклікае функцыя можа выканаць просты тэст роўнасці выявіць любыя памылкі: калі (myNetworkingFunction() == NETWORK_ERROR) {...}. Якая выклікае функцыя можа затым атрымаць спецыяльны код з дапамогай WSAGetLastError() і апрацаваць памылку адпаведна. У канчатковым рахунку, рэалізацыі добрай апрацоўкі памылак схема цяпер вы зэканоміце шмат дзен ці тыдняў развіцця час як вы будзеце імгненна ведаць, чаму ваша праграма не ўдалася.

Акрамя вяртаючы новае кліенцкае злучэнне, то Accept() дазваляе серверу для здабывання інфармацыі аб кліенце, а не праз выклікі функцый або метадаў, якія патрабуюць дадатковага часу (што можа стаць праблемай у гульні сервера, дзе хуткасць прымаем завесы-гэта асабліва крытычна). Каб скарыстацца перавагамі гэтай функцыі, перадайце ў адрас структуры struct sockaddr_in літой структуры ў паказальнік, г. зн. (LPSOCKADDR)&aSockaddrInStructure. Таксама, аб'яўляем целочисленную зменную, усталюйце значэнне int на памер структуры sockaddr struct, і перадаваць адрас цэлалікавага якасці трэцяга параметру. Калі адрасная інфармацыя вяртаецца пасля выкліку функцыі, параметр даўжыні павінен прысутнічаць.

jdarnold папярэджвае нас не верыць у дакументацыі MSDN па нагоды гэтага трэцяга параметру: "у MSDN дакументы мяркуюць, што Вы не павінны прайсці ў addrlen, што гэта проста дадатковы выхадны параметр, але яны памыляюцца. Якія ўваходзяць яна кажа колькі байт у буферы структуры sockaddr, і выходны [winsock з] запаўняе колькі [winsock з] выкарыстоўваецца. Калі перадаць нуль, як лен, [канфігурацыя winsock] не чапаць буфер."

Гэта не сервер, паколькі ен чакае толькі аднаго карыстальніка для падлучэння і затым адразу адключае, але гэта самае простае рашэнне. Проста каб растлумачыць сітуацыю, выклік WSAStartup() ўключае словы, паказваючы, якую версію вы хочаце загрузіць (у дадзеным выпадку гэта 1,1) і адрас WSADATA структура. Далей мы пагаворым аб тым, як падключыцца з іншых кампутараў.

Зрабіць Свой Уласны Злучэнняў

Стварыўшы сокет, каб злучыцца з кім-то яшчэ выкарыстоўвае большасць тых жа функцый, за выключэннем структуру struct HOSTENT:

  • HOSTENT: структура, якая выкарыстоўваецца, каб распавесці сокет, да якога кампутар і порт для падлучэння. Гэтыя мадэлі звычайна з'яўляюцца як LPHOSTENT зменныя, якія з'яўляюцца проста паказальнікамі на структуры HOSTENT. Як вы код для Windows, Вы часцей за ўсе выявіце, што любы тып дадзеных, якому папярэднічае ЫЯ азначае, што дадзены тып з'яўляецца на самай справе паказальнікам на "базу" тыпу (напрыклад, LPCSTR-паказальнік на радок C, таксама вядомы як сімвал *).

Такім чынам, пяройдзем адразу да коду:

#include 

#include 

#include 



#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);

бо ен выконвае адразу некалькі аперацый---адзін з іх адносна схаванага---адразу. Давайце разбіраць яго крок за крокам:

У h_addr_list членам структуры struct HOSTENT у асноўным вызначана як char **h_addr_list, якое ўяўляе сабой масіў радкоў, або радкоў char*. gethostbyname() выяўлены і скапіраваны ўсе вядомыя адрасы сервера ў гэты спіс. Тым не менш, робіць канцэпцыю некалькі адрасоў прынцыпова мае сэнсу? На самай справе, гэта не так. Ваш кампутар, па сутнасці, есць масіў агульных сеткавых адрасоў. Адрасы ў Інтэрнэце могуць быць 205.182.67.96, лакальнай сеткі адрас можа быць 10.0.0.2, і ўсе кампутары, на якіх устаноўлена сістэма Windows, натуральна, маюць "шлейф" адрас 127.0.0.1, які выкарыстоўваецца кампутарам для абазначэння сябе ў лакальнай сеткі. Тая ж канцэпцыя ўжываецца ў вобласці інтэрнэт-адрасоў або IP-адрасоў, таму спіс патрэбен, а не дыскавая прастора для аднаго адрасы. Звярніце ўвагу, што упадабаны адрас, то есць, самы даступны адрас, заўседы капіюецца ў першы элемент спісу, затым другі пераважны або іншыя адрасы.

Што *hostEntry->h_addr_list робіш? Няцяжка здагадацца, што павагі аператара (*) выкарыстоўваецца для доступу да аднаго адрасе ў спісе. Аднак, не забяспечыўшы пэўны індэкс, аперацыя разнаймення аўтаматычна адкрывае першы, пераважны адрас. Гэты канкрэтны раздзел эквівалентна *hostEntry->h_addr_list[0], які гарантавана існуе, паколькі сервер павінен мець па крайняй меры адзін адрас.

Далей, выпадковая праца *, вернутыя аперацыя разнаймення кінуты ў in_addr * ці LPIN_ADDR. Нарэшце, яшчэ адзін знак павагі аперацыя выконваецца вярнуць структуру struct in_addr, на якія спасылаецца паказальнік, які можа ўтрымліваць толькі адзін адрас. Атрыманую структуру struct in_addr затым прысвойваецца файла serverinfo.sin_addr. Наступныя падлучэння() прымае адзін адрас у якасці параметру пры фарміраванні злучэння з серверам.

Калі IP-адрас сервера вядомы, сапраўдны 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() выкарыстоўваецца для капіявання радкі, якія абазначаюць IP-адрас непасрэдна ў структуру struct in_addr. Пасля, адрас struct адліваецца ў аргумент const сімвал*, так як гэтага патрабуе gethostbyaddr(). Абодва метаду называюцца урэгуляванні адрас сервера з інтэрфейсу winsock вяртае поўны адрас запісу з частковай інфармацыі.

Некаторыя дадатковыя Заўвагі: порт 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

}

Што цікава адзначыць, што там есць кнопка на панэлі інструментаў у Microsoft Outlook з надпісам "Адправіць/усил." Гэта "атрымліваць" скарочана "усил" проста каб пераканацца кнопка выглядае так, або праграміст у звычку ўводзіць метад recv() столькі разоў? Сфармуйце сваю ўласную тэорыю змовы (зноў жа, добры для smalltalk на вечарынках).

Вось дзе я сутыкнуўся з маленькай праблемай пры напісанні маіх уласных праграм winsock. Проста выкарыстоўваючы метад recv() гэта выдатна, калі вы дакладна ведаеце, колькі дадзеных Вы будзеце атрымліваць (такі, як у гульні, дзе першы байт можа быць каманднага і наступны байт будзе параметр, і г. д.), але калі вы не ведаеце, што вы робіце? Калі дадзеныя, якія вы атрымліваеце завяршаецца сімвалам новага радка (распаўсюджаная праблема з Java-кліентамі размаўляеш з сервера), вы можаце напісаць з readline() функцыю, каб захапіць усе да гэтага сімвала. Вось што я выкарыстаў:

char * readLine()

{

   vector theVector;

   char buffer;

   int bytesReceived;



   while (true)

   {

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

      if (bytesReceived <= 0)

         return NULL;



      if (buffer == '
')

      {

         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() вяртае памылку (пазначаецца bytesReceived быць менш за нуль), то вяртаецца NULL. Так як гэта магчымасць, выклік функцыі павінен пераканацца, што радок, якая вяртаецца з з readline() сапраўдны да выкарыстання. Ўнутры цыклу, аднаго чара атрымана з разеткі і, калі не сімвал новай радкі, дадаецца да вектару. Калі гэта сімвал новай радкі, змесціва вектара капіююцца ў радок C і вярнуўся. Радок аб'яўляецца адзін сімвал больш, чым вектар і функцыі memset()'Тэд да нуля, так што радок будзе вернутая аўтаматычна завяршаецца нулем. Канцоўка радкі з NULL прадухіляе незвычайныя памылкі і наогул добрай практыкай праграмавання.

Не прадстаўлена гэтая хітра палепшаная версія з падтрымкай backspaces і магчымасць змяняць сімвал новай радкі легка:

// 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 ''					// To disable backspaces, #define backKey NULL

#define newLine '
'

#define endStr  ''



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;

		}

	}

}

Неблокирующий і асінхронны разеткі

Да гэтага моманту мы казалі аб блакаванні разетак, дзе выклік функцыі, такія як Accept() чакае некаторы час, каб карыстальнік мог падключыцца. Неблокирующий сокет адразу вяртаецца кожны раз, калі гаворыцца што-то рабіць, альбо з паспяховым вынікам, памылка, або нічога (гэта азначае, што там будзе што-то атрымліваць пазней). Недахопам гэтага тыпу з'яўляецца тое, што вам прыйдзецца ўручную запыту сокет, каб убачыць, калі вынік прыйшоў на кожным выкліку функцыі. Вы можаце прайсці набор разетак у функцыю Select (), каб убачыць, якія з іх гатовыя да чытання, ліста, або вярнуліся памылкі.

Функцый, выкарыстоўваючы асінхронныя сокеты таксама вяртаюцца неадкладна, але вы можаце пазначыць паведамленне для адпраўкі вашага працэдуры вокны, калі згаданая падзея мела месца. Напрыклад, вы можаце мець сокет адправіць SOCKET_GOTMSG паведамленне кожны раз, калі ен атрымлівае што-то. Звычайна гэта разумна, каб праверыць на наяўнасць памылак (грувасткія, але неабходныя), калі вы атрымліваеце паведамленне разетку, каб не выклікаць непатрэбных праблем у далейшым. Па-першае, давайце вызначым некаторыя функцыі, якія мы будзем выкарыстоўваць, каб стварыць асінхронны сокет:

  • Тып int WSAAsyncSelect ( сокет ы, ад hwnd hwnd, які, без знака int х сусветных інтэлектуальных гульняў, доўга левент ) Гэтая функцыя выкарыстоўваецца для ідэнтыфікацыі сокета як асінхронны і звязаць паведамленне з ім. S-гэта сокет, з якім вы працуеце. hwnd-гэта дэскрыптар акна, якое атрымае паведамленне, калі сокет ствараецца падзея. х сусветных інтэлектуальных гульняў з'яўляецца паведамленне, якое вы хочаце адправіць вашай аконнай працэдуры (прыкладам можа служыць SOCKET_GOTMSG паведамленне зверху). Параметрам levent прымае адзін або больш сцягоў, якія распавядаюць гняздо на якія падзеі вы хочаце адправіць паведамленне. Некаторыя з гэтых сцягоў:
    • FD_READ: сокет гатовы да прыему дадзеных
    • FD_WRITE: сокет гатовы да перадачы дадзеных
    • FD_ACCEPT: выкарыстоўваецца ў серверах, гэта паведамленне паказвае карыстальнік падключыўся
    • FD_CONNECT: выкарыстоўваецца ў кліенцкіх прыкладаннях, гэта паведамленне кажа вам, сокет падлучаны
    • FD_CLOSE: сокет быў зачынены
  • WSAGETSELECTERROR ( параметр lparam lparam павінен )

Вызначае, ці будзе сокет вярнуў памылку. Тэхнічна, гэта не функцыя, а макрас (вы сапраўды можаце генераваць плеткі на вечарынках з гэты маленькі фактоид).

  • WSAGETSELECTEVENT ( параметр lparam lparam павінен )

Яшчэ адзін карысны макрас, пэўны ў winsock2.span WSAGETSELECTEVENT(), які выкарыстоўваецца, каб дакладна даведацца, што гняздо было зроблена.

Такім чынам, прыступім да наладзе асінхроннага сокета:

// We begin by creating a flag that Windows will use to contact us when something happens

#define THERE_WAS_A_SOCKET_EVENT	WM_USER + 100	// WM_USER is a base for custom messages
// 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



	}

}

Звярніце ўвагу, што нельга вызначыць адно паведамленне для кожнага падзеі, як SOCKET_GOTMSG для FD_READ і тады SOCKET_CONNECTED для FD_CONNECT. Гэта адбываецца таму, што неаднаразовыя заклікі да WSAAsyncSelect () для ўстаноўкі кожнага сцяга будзе адмяніць дзеянне апошняй выклік WSAAsyncSelect ()

Больш Туториалов і спасылак

Я напісаў гэты падручнік у снежні 2000 года, і за сем гадоў з тых часоў назіраецца пастаянны паток наведвальнікаў і паляпшэнняў. Я спадзяюся, што вам спадабалася чытаць столькі, колькі я любіў пісаць: дзякуем Вас за выкарыстанне Джоні winsock на ўроку. Вышэйпададзенае з'яўляецца толькі кароткім аглядам магчымасцяў вы можаце дасягнуць праз winsock і іншыя зрабілі значна лепшую працу, чым у мяне на зандаванне спецыфіку гэтай тэме:

Johnnie'sWinsock ПадручнікJohnnie'sWinsock ПадручнікJohnnie'sWinsock ПадручнікJohnnie'sWinsock Падручнік

Я згодны з Томасам Блекер (MadWizard), што "сеткавае праграмаванне, здаецца, лягчэй, чым гэта." Я не магу пераканаць вам важнасць практыкі выкарыстання гэтых функцый разам з отладчиком, так што вы можаце бачыць, што адбываецца. Вы будзеце ў канчатковым выніку мець значна лепшае разуменне таго, як рэчы працуюць, калі вы зробіце гэта няправільна, высветліць, чаму вы атрымалі гэта няправільна і потым адчуваць задавальненне атрымаць гэта права. Рабіць памылкі, іншымі словамі, як мы вучымся.

Як Я Магу Палепшыць?

Ці есць што-тое, што мае патрэбу ва ўдакладненні? Не падручнік, не ў стане пакрыць звязаныя з winsock тэме, што вы хацелі даведацца пра? Есць падручнік сустрэлі вашыя патрэбы ў якасці распрацоўніка праграмнага забеспячэння? Гэта пацешным? Бойка напісаны? занадта спрошчана? ці проста так?

paper4pc
Add a comment:
Sign in

See also

Behaviourism

Бігейвіярызму

2015-11-11 11:58:42

Паводніцкія (або "паводніцкай" тэорыі ў псіхалогіі-гэта вельмі істотны поля: па спасылках злева ці справа для ўвядзення да некаторых з яе...

traditional idl graphics

Змяніць спосаб запісу графічных праграм назаўседы!

2015-11-10 18:01:30

Год або каля таго таму, я ўбіў сабе ў галаву, каб вярнуцца да сваіх каранеў. Я думаў, што гэта будзе...

Інтэрв'ю з Вінсэнтам Лэ Муа

2015-11-10 18:41:48

Вінцэнт-вельмі заняты чалавек. Ен з'яўляецца рухаючай сілай Agiledesigners, Minicons і Fontastic. Ен пайшоў ад фрыланс да стварэння аднаго з самых...

Programming Language

cT Programavimo Kalba Archyvas

2015-11-11 12:38:14

Trumpa istorija iš cT cT programavimo kalba buvo sukurta laikotarpį 1985-2000 Centre Projektavimo Švietimo Kompiuterija Carnegie Mellon Universiteto Pitsburge (Centre, vėliau...