Johnnie on winsock juhendaja

link: http://johnnie.jerrata.com/winsocktutorial/

Mida Johnnie Rose, Jr

Kui te olete sihilikult saabus minu Winsock juhendaja, olete tõenäoliselt leida mõte oma rakendused suhtlemine Interneti kaudu, kui põnev väljavaade, nagu ma olen. Või, võib-olla keegi on leidnud väljavaade võrdselt huvitav ja teile on usaldatud, tuues selle visiooni reaalsuseks. Kas juhul, kui Winsock võrguteenus ja selle juhendaja aitab teil saavutada oma eesmärke äriline ettevõte, lihtsalt leida valdkonda, võrgu kavandamise isiklikuks kasutamiseks, või midagi vahel.

Siin on, mida me hõlmab:

  • Luua kuulamine pesa: Kuna väikese armee võrgustike funktsioonide, saame luua programm, mis kannatlikult ootab sissetulevad ühendused? (Jah, saame )
  • Muutes oma ühendused: Esitatud mõned rohkem funktsioone, saame luua programm, et edukalt sidemeid kuulamise server? ( Jah, saame .)
  • Mitte-blokeerimine ja asünkroonne-pistikupesad: Kuidas me saame suurendada tõhusust meie koodi, rakendades erinevate võrgustike loomise kava? (Me muuta meie akna korras vähe.)
  • Rohkem Õpetused ja Lingid: Millised ressursid on seal suurem ning ulatub kaugemale see õpetus? ma rõhutada 3, et tuleks hoida sind kinni, samal ajal (kui olete seeditav minu juhendaja, muidugi :-).

Kuigi teil võib olla innukas, et jõuda, et aukartust äratav koht, kus oma taotluse edukalt teeb oma esimese ühendus, olema teadlik mõistete taga koodiga. Püüa vältida lihtsalt manipuleerides antud koodi vastavalt oma esmavajadusi ja selle asemel, et teha kindlaks nõuded oma taotluse ja ainult siis rakendada, mis tundub olevat parim lahendus. See on piisav, minu Zen Tarkvara Arendamise nõuanded nüüd; teeme mõned võrgu kavandamise…

julgelt lae kogu õpetus koodi lisamise. Pea meeles, et mingi koodi, mis on esitatud selles juhendaja peaks olema seotud sellega, et Winsock library, tavaliselt wsock32.lib või midagi sarnase nimega. Ka siis, kui kasutatakse koodi täpselt nii, nagu esitatud juhendaja oma IDE (Dev-C++ Microsoft VC++, C++ Builder, jne.), valida, kas ehitada Windows projekti WinMain(), et vältida vigu.
Luua kuulamine pesa

Rakendused teenindamine väljaspool masinad on kutsutud serverid. Server rakendused kuulata klientide poolt initializing üks või mitu kuulamise pistikupesad. Kui klient ühendab üks neist kuulamine pistikupesad, server saab teate Winsock, nõustub ühendus, ja hakkab lähetamise ja pealt kuulata sõnumeid ja mõni uus klient. Võib-olla kõige lihtsustatud meetod, mille abil serverid hakkama palju kliente on kudema uue lõnga iga kliendi ühendus. See server mudeli kõige sagedamini kasutab blokeerimine pistikupesad, mille peatada ajutiselt ootama sissetulevad andmed, uue ühenduse, ja muud võrgu sündmused. Esiteks olgem määratleda mõned struktuurid meil tuleb initsialiseerida blokeerimine pesa:

  • WSADATA : seda struktuuri kasutatakse operatsioonisüsteemi päringute jaoks Winsocki versiooni jaoks, mida meie kood nõuab. Rakendus nõuab õiget Winsock DLL-i õigeks initsialiseerimiseks WSAStartup ().
  • SOCKET : objekt (tegelikult on see defineeritud kui u_int , allkirjastamata täisarv, winsock.h-is — see on hea, et teada saada väiketalongidel ), mida rakendused kasutavad pistikupesa hoidmiseks.
  • SOCKADDR_IN : Rakendus kasutab seda struktuuri, et määrata, kuidas pistik peab toimima. SOCKADDR_IN sisaldab välju IP-aadressile ja pordi numbrile:
struct sockaddr_in 

{ 

  lühike sin_family; // protokolli tüüp 

  u_short sin_port; // pistiku 

  struktuuri port number in_addr sin_addr; // IP-aadressi 

  char sin_zero [8]; // kasutamata 

};

Esimene väli on protokolli tüüp, mis on tavaliselt AF_INET (TCP / IP). Kuna kuulamispesas ei käsitleta selle masina võrgupartnerit, kuhu ta elab, määrab Winsock automaatselt IP-aadressi ja pordi numbri luukide kuulamiseks loomisel.

Me ehitame oma esimese kuulamisserveri eespool nimetatud struktuuridega ja väikese võrgufunktsioonide armeega:

#include <windows.h> 

#include <winsock.h> 

#include <stdio.h> 



#define NETWORK_ERROR -1 

#define NETWORK_OK 0 



tühine 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); // Soovime Winsocki versiooni 1.1 





	// Alustame Winsock 

	WSAStartupi (sockVersion, & wsaData) initsialiseerimisega ; 





	// Järgmine, looge kuulamispesa 

	SOCKET listeningSocket; 



	kuulamineSocket = pesa (AF_INET, // üle minna TCP / IP 

			         SOCK_STREAM, // see on oja orienteeritud pistikupesa

				 IPPROTO_TCP); // Kasuta TCP kui UDP, 



	kui (listeningSocket == INVALID_SOCKET) 

	{ 

		nret = WSAGetLastError (); / / Võtke üksikasjalikumat viga 

		ReportError (nret, "socket ()"); // Teata veale meie kohandatud funktsioonist 



		WSACleanup (); // shutdown Winsock 

		tagasi NETWORK_ERROR; // tagastab vea väärtuse 

	} 





	// kasuta SOCKADDR_IN struktuuri, et täita aadressi andmed 

	SOCKADDR_IN serverInfo; 



	serverInfo.sin_family = AF_INET; 

	serverInfo.sin_addr.s_addr = INADDR_ANY; // Kuna see pistik kuulab ühendust, 

							// teeb mis tahes kohaliku aadressi 

	serverInfo.sin_port = htons (8888); // teisendab täisarv 8888 võrguparameetrite järjekorda

							// ja sisesta 
	portväljale 





	// Ühendage socket kohaliku serveri aadressiga 
nret = bind (listeningSocket, (LPSOCKADDR) & serverInfo, sizeof (struct sockaddr)); 



	kui (nret == SOCKET_ERROR) 

	{ 

		nret = WSAGetLastError (); 

		ReportError (nret, "siduda ()"); 



		WSACleanup (); 

		tagasi NETWORK_ERROR; 

	} 





	// Tee pesa kuulata 

	nret = listen (listeningSocket, 10); // Kuni 10 ühendust võib oodata igal 

							// korraga, et aktsepteerida () 'ed 



	kui (nret == SOCKET_ERROR) 

	{ 

		nret = WSAGetLastError (); 

		ReportError (nret, "listen ()"); 



		WSACleanup (); 

		tagasi NETWORK_ERROR; 

	}





	// oodake, kui klient 

	sulge klient ; 



	theClient = aktsepteerib (listeningSocket, 

			   NULL, // valikuliselt SOCKADDR_IN struktuuri 

			   NULL aadressi); // Valikuline aadress, mis sisaldab 

							// sizeof (struct SOCKADDR_IN) 



	if (theClient == INVALID_SOCKET) 

	{ 

		nret = WSAGetLastError (); 

		ReportError (nret, "aktsepteeri ()"); 



		WSACleanup (); 

		tagasi NETWORK_ERROR; 

	} 





	// Saada ja saada kliendilt ja lõpuks 

	closesocket (theClient); 

	closesocket (kuulamineSocket); 





	/ / Shutdown Winsock 

	WSACleanup (); 

	tagasi NETWORK_OK; 

}





void ReportError (int errorCode, const char * whichFunc) 

{ 

   char väärtusMsg [92]; // 
   Tühista puhver, mis hoiab 

							// loodud veateadet 

   
ZeroMemory (errorMsg, 92); // Automaatselt NULL-stringi 



   lõpetage // Järgmises reas kopeeritakse fraas, milline Funci string ja täisarv errorCode puhvrisse 

   sprintf (errorMsg, "Kutsu tagasiside% s tagasi% d!", (Char *), misFunc, errorCode) ; 



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

}

Üks asi, mida võite koodi koheselt märkida, on vigade kontrollimiseks tehtud jõupingutuste arv. Kui ilmneb tõrge, saab kood koodi WSAGetLastError () ja salvestab tulemuse nret. Seejärel saadetakse veakood koos stringiga, mis näitab ebaõnnestunud funktsiooni nime kohandatud funktsioonile ReportError (). Seal konstrueeritakse ja kuvatakse veateade kasutajale CallBox () kutsumiseks, mis on standardse WinAPI osa. Näiteks kuulas () ebaõnnestunud veateade 10093 (defineeritud kui WSANOTINITIALISED), valmis viga string oleks „Helista, et kuulata () tagastab viga 10093!“. Teie, mõistlik arendaja, uuriks seejärel koodi ja avastaks, et viga tekkis, kuna edukat WSAStartupi () kutset ei olnud veel tehtud.

Aleksandar Pavlov laiendas seda ReportError (), et lisada kirjeldused ligikaudu tosinatesse lepatuti vigadest. Kasutades oma uuendatud versiooni , ei pea te enam seda, mis koodi tähendab, otsida ja teie programm muutub palju kasutajasõbralikumaks, tehes teie jaoks väga vähe jõupingutusi.

Siia kuuluvad ka NETWORK_ERROR ja NETWORK_OK-i määratlused. Need võivad olla kasulikud, kui kontrollida oma võrgufunktsioonide tagastatavat väärtust. Kui teie funktsioonid tagastavad ühe nendest väärtustest, võib helistamisfunktsioon sooritada lihtsa võrdsuse testi võimalike vigade ilmnemiseks: if (myNetworkingFunction () == NETWORK_ERROR) {…}. Seejärel võis helistamisfunktsioon saada WSAGetLastError () jaoks spetsiifilise koodi ja vea vastavalt sellele. Lõppkokkuvõttes aitab hea veakäsitsemise skeemi rakendamine säästa palju päevi või nädalaid arendusajast, kuna saate teada, miks teie programm on ebaõnnestunud.

Lisaks uue kliendiühenduse tagasisaadavale vastuvõtmisele lubab server () serveril saada teavet kliendi kohta, mitte selliste meetodite abil, mis nõuavad täiendavaid funktsionaalseid kõnesid või kellaaega (mis võib muutuda mängu serverite probleemiks, kus vastuvõtmise loopi kiirus on eriti kriitiline). Selle funktsiooni ära kasutamiseks edastage sockaddr_in struktureeritud aadress sockaddr-osutile, st (LPSOCKADDR) ja aSockaddrInStructure. Samuti deklareerige täisarvuline muutuja, määrake int väärtus sockaddr struktuuri suurusele ja edastage täisarvu aadress kolmanda parameetrina. Kui aadressi andmed tuleb pärast funktsiooni kõne saamist tagastada, peab pikkuse parameeter olema kohal.

jdarnold hoiatab meid mitte uskuma selle kolmanda parameetri MSDN-dokumente: „MSDN-i dokumendid tähendavad seda, et te ei pea lisama addllen, et see on lihtsalt valikuline väljundparameeter, kuid need on valed. Sissetulevast sõnast saab öelda, kui palju baite sockaddr puhvris ja väljaminev [Winsock] täidab, kui palju [Winsocki] kasutatakse. Kui te lähete nulliks nagu len, [Winsock] ei puuduta puhvrit. „

See ei ole palju serverit, sest see ootab ainult ühe ühenduse loomist ja siis kohe katkestab, kuid see on kõige elementaarsem disain. Ülesannete selgeks muutmiseks sisaldab WSAStartup () kõne WORD-i, mis määrab, millist versiooni soovite laadida (antud juhul see on 1.1) ja WSADATA-struktuuri aadressi. Järgmisena kirjeldame, kuidas teiste arvutitega ühenduse luua.

Oma ühenduste loomine

Sõlmimise loomine kellegi teisega ühenduse loomiseks kasutab enamus samu funktsioone, välja arvatud HOSTENT struktuur:

  • HOSTENT : struktuur, mida kasutatakse pistikupesastamiseks, kuhu arvuti ja port ühendada. Need struktuurid leiavad tavaliselt LPHOSTENTi muutujaid, mis on vaid HOSTENTi struktuuride osutiks. Windowsi koodi korral leiad üldiselt, et mis tahes andmetüüp, millele eelneb LP, tähendab seda, et tüüp on tegelikult „baasi“ tüüpi näitaja (näiteks LPCSTR on C-stringi osuti, mida tuntakse ka kui char * )

Nii et lähme koodi õigeks:

#include <windows.h> 

#include <winsock.h> 

#include <stdio.h> 



#define NETWORK_ERROR -1 

#define NETWORK_OK 0 



tühine 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); 





	// Winsock enne 

	WSAStartupi alustamist (sockVersion, & wsaData); 





	// Salvestab serveri kohta teabe 

	LPHOSTENT hostEntry; 



	hostEntry = gethostbyname ("www.yahoo.com"); // serveri määramine selle nime järgi; 

							// teine võimalus: gethostbyaddr () 



	if (! hostEntry) 

	{

		nret = WSAGetLastError (); 

		ReportError (nret, "gethostbyname ()"); // Teata veale enne 



		WSACleanup (); 

		tagasi NETWORK_ERROR; 

	} 





	/ / Loo pistik 

	SOCKET theSocket; 



	theSocket = pesa (AF_INET, // üle minna TCP / IP 

			   SOCK_STREAM, // see on stream-orienteeritud socket 

			   IPPROTO_TCP); // Kasuta TCP-d kui UDP-d, 



	kui (socket == INVALID_SOCKET) 

	{ 

		nret = WSAGetLastError (); 

		ReportError (nret, "socket ()"); 



		WSACleanup (); 

		tagasi NETWORK_ERROR; 

	} 





	// Täida SOCKADDR_IN struktuur aadressi andmetega 

	SOCKADDR_IN serverInfo; 



	serverInfo.sin_family = AF_INET;



	/ / Siinkohal oleme edukalt leidnud serveri olulist teavet, 

	// sealhulgas selle hostinime, varjunimed ja IP-aadressid. Oota; kuidas võiks üks 

	arvuti / arvuti omada mitu aadressi ja täpselt, mida järgnev joon teeb? 

	// Vaata alljärgnevat selgitust. 



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



	serverInfo.sin_port = htons (80); // 
	võrgubaidijärjekorra muutmine ja 

							// sisesta portväljale 





	// serveriga 
ühendamine nret = connect (theSocket, 

		       (LPSOCKADDR) & serverInfo, 

		       sizeof (struct sockaddr)); 



	kui (nret == SOCKET_ERROR) 

	{ 

		nret = WSAGetLastError (); 

		ReportError (nret, "connect ()");



		WSACleanup (); 

		tagasi NETWORK_ERROR; 

	} 





	// Edukalt ühendatud! 





	// saatke / võta vastu ja siis puhastage: 

	closesocket (theSocket); 

	WSACleanup (); 

} 





void ReportError (int errorCode, const char * thatFunc) 

{ 

   char väärtusMsg [92]; // 
   Tühista puhver, mis hoiab 

							// loodud veateadet 

   
ZeroMemory (errorMsg, 92); // Automaatselt NULL-stringi 



   lõpetage // Järgmises reas kopeeritakse fraas, milline Funci string ja täisarv errorCode puhvrisse 

   sprintf (errorMsg, "Kutsu tagasiside% s tagasi% d!", (Char *), misFunc, errorCode) ; 



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

}

Kõige keerukam loend on järgmine:

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

sest see täidab mitu toimingut – üks neist suhteliselt peidetud — korraga. Vaatame samm-sammult lahku:

HOSTENTi struktuuri h_addr_list liige on põhimõtteliselt määratletud kui char ** h_addr_list, mis on strings või char *. gethostbyname () identifitseeris ja kopeeris kõik selle serveri tuntud aadressid sellesse loendisse. Kuid kas mitu aadressi kontseptsioon on põhimõtteliselt mõttekas? Tegelikult on see nii. Tegelikult on teie arvutis üldine võrgundusaadress. Teie Interneti-aadress võib olla 205.182.67.96, teie LAN-aadress võib olla 10.0.0.2 ja kõik arvutid, millel Windows on installitud, peavad loomuliku „loopback“ -aadressiga 127.0.0.1, mida arvuti kasutab, et viidata ennast kohalikule võrgule . Sama kontseptsioon kehtib ka Interneti-aadresside või IP-de valdkonnas, mistõttu on vaja ühe loendi asemel ühe aadressi salvestusruumi. Pange tähele, et see on eelistatudaadress, st kõige hõlpsam aadress, kopeeritakse alati loendi esimesele elemendile, millele järgneb teine eelistatud või muu aadress.

Mis on * hostEntry-> h_addr_list teeb? Te võite arvata, et loendis on üksainus aadress juurdepääsuks virtuaalserver (*). Siiski, kui ei esitata konkreetset indeksit, näitab dereferencetegevus automaatselt esimest, eelistatud aadressi. See konkreetne jaotis on samaväärne * hostEntry-> h_addr_list [0], mis on garanteeritud, sest serveril peab olema vähemalt üks aadress.

Järgmine dereferencing’iga tagastatud char * tagastatakse in_addr * või LPIN_ADDR. Lõpuks tehakse ka teine harjutusoperatsioon, et suunata kursoriga viidatud in_addr struct, millel on ainult üks aadress. Saadud in_addr struktuur määratakse seejärel serverInfo.sin_addr. Järgmine ühendus () võtab serverina ühenduse loomiseks parameetrina parameetri.

Kui serveri IP-aadress on teada, saab saada HOSTENTi kasutades gethostbyaddr () (erinevalt eelmises loendis kasutatud 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); 



kui (! hostEntry) 

{ 

	// käsitsema vastavalt 

}

Sellisel juhul kasutatakse inet_addr (), et kopeerida string, mis tähistab IP-aadressi otse in_addr struct. Seejärel lisatakse struktuuri aadress const char *, nagu seda nõuab gethostbyaddr (). Mõlemaid meetodeid nimetatakse serveri aadressi lahendamiseks, kuna Winsock tagastab osalised andmed täielike aadresside kirjed.

Mõned täiendavad märkused: porti 80 kasutati lihtsalt sellepärast, et selle porti jõuavad Interneti-lehtede ülekanded. Kui soovite saata stringi veebiserverile konkreetse faili taotlemiseks ja proovige midagi tagasi saada, siis on teil väga lihtne veebibrauser. Muidugi peab see string sisaldama täielikku HTTP-käsklust. On suurepärane, et saame kuulda ja ühendada teiste arvutitega, kuid kommunikatsioon hõlmab ka saatmist ja vastuvõtmist.

Saatmine ja vastuvõtmine

Saatmist käideldakse mugavalt mugavalt funktsiooniga send ():

int saatmine ( 

  SOCKET s, 

  const char * FAR buf, 

  int len, 

  int lipud 

);

Põhimõtteliselt kopeerite kõik, mida soovisite puhvrisse, ja kasutage ühendatud pistikupessa saatmise () funktsiooni, et andmed läheksid teise otsa:

char puhvriga [256]; // puhvri deklareerimine virnast 

char * puhver = uus char [256]; // või hunnik 



ZeroMemory (puhver, 256); 

strcpy (puhver, "teeselda, et see on olulised andmed"); 



nret = send (theSocket, 

	    buffer, 

	    strlen (buffer), // Pange tähele, et see määrab stringi pikkuse, mitte 

				// kogu puhvri suuruse 

	    0); // Enamasti on null, kuid vaata teiste valikute MSDN-i, 



kustuta [] puhvrit; // Kui ja ainult siis, kui kasutatakse hunnikut, 



kui (nret == SOCKET_ERROR) 

{ 

	// Saa kindlat koodi 

	// Käidelda vastavalt 

	tagasi NETWORK_ERROR; 

} else { 

	// nret sisaldab saatnud baitide arvu 

}

Vastuvõtmine on sama protsess, tagurpidi:

char puhvriga [256]; // stack 

char * puhver = uus char [256]; // või hunnik 



nret = recv (theSocket, 

	    buffer, 

	    256, // puhver 

	    0 täissuuruses ); 



kustuta [] puhvrit; // Manipuleerige puhvrit, seejärel kustutage siis ja ainult siis, kui 

				// puhvrile eraldati hunnikule, 



kui (nret == SOCKET_ERROR) 

{ 

	// Saa kindlat koodi 

	// Käidelda vastavalt 

	tagasi NETWORK_ERROR; 

} else { 

	// nret sisaldab saadud baitide arvu 

}

Huvitav on märkida, et Microsoft Outlooki tööriistaribal on nupp „Saada / Jugeda“. Kas „Receive“ lühendatakse „Recv“ lihtsalt, et tagada nupu otsimine õigesti, või on see programmeerija harjumus kirjutama recv () nii mitu korda? Vormelge oma vandenõu teooriad ( jällegi hea väiketalongide jaoks pidudel ).

See on koht, kus ma oma Winsocki programmide kirjutamisel tekitas väikese probleemi. Lihtsalt recv () kasutamine on suurepärane, kui täpselt teada, kui palju andmeid saadate (näiteks mängus, kus esimene bait võib olla käsk ja järgmine bait on parameeter jne), kuid kui sa ei tead, mida sa teed? Kui teie saadud andmed lõpevad uue reastusega (tavaline probleem Java-klientidega, kes räägivad C-serveritega), saate kirjutada readLine () funktsiooni, et salvestada kõike kuni selle märkini. Siin ma kasutasin:

char * readLine () 

{ 

   vektortheVector; 

   char puhvriga; 

   int bytesReceived; 



   samas (true) 

   { 

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

      kui (bytesReceived <= 0) 

         tagastab NULL; 



      kui (puhver == '\ n') 

      { 

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

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



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

            pChar [f] = theector [f]; 



         tagasi pChar; 

      } else { 

         theVector.push_back (puhver); 

      } 

   } 

}

Massiivi asemel kasutatakse vektorit, sest selle salvestusruumi võib automaatselt suurendada, et see vastaks liini pikkusele. Kui recv () tagastab vea (näidatud baitidega, mis on saadud vähem kui nullist), tagastatakse NULL. Kuna see on võimalus, peaks funktsiooni kutsumine tagama, et readLine () tagastatud string on kehtiv enne kasutamist. Loopis võetakse pikslist ühe haru ja vektorile lisandub, kui mitte uue reana. Kui see on uue rea tähis, kopeeritakse vektori sisu C-stringi ja tagastatakse. String deklareeritakse ühe haru võrra suuremaks kui vektor ja nimega memset () ‚, et tagastatud rida oleks automaatselt NULL lõpetatud. Lõpp stringide NULL takistab ebatavalisi vigu ja on üldiselt hea programmitöö praktika.

Samuti ei toeta seda nutikalt parendatud versiooni, mis toetab backspaces’i ja võimet redigeerida kergesti järgmiselt:

// Kood, mille algselt kirjutas Nor. Muudetud veidi 

// toetab MessageBox () API-d, muudab loogika loetavamaks, 

/ joondatakse vahekaugused ja lisab kommentaare. Postitatud loaga. 



#define backKey '\ b' // Backspace'i keelamiseks #define backKey NULL 

# define newLine '\ n' 

#define endStr '\ 0' 



char * readLine (SOCKET s) 

{ 

	vectortheVector; 

	char puhvriga; 

	char * pChar; 

	int bytesReceived; 



	samas (true) 

	{ 

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



		kui (bytesReceived <= 0) 

		{ 

			MessageBox (NULL, 'recv () ei tagastanud midagi.', 'socketIndication', MB_OK); 

			tagasi NULL; 

		} 



		lüliti (puhver) 

		{ 

			case backKey: // käsitseda backspace 

				kui (theVector.size ()> 0) 

					theVector.pop_back (); 

				murda; 

			case endStr: // Kui stringi haru lõpp on jõudnud, siis 

			case newLine: // või kui stringi haru lõpp on jõudnud, 

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

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



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

					pChar [f] = theector [f]; 

				tagasi pChar; 

				murda; 

			vaikimisi: // mis tahes regulaarselt char 

				theVector.push_back (puhver); 

				murda; 

		} 

	} 

}

Mitteblokeerivad ja asünkroonsed pistikupesad

Kuni selle hetkeni oleme rääkinud blokeerivatest pistikupesadest, kus kutsutakse sellist funktsiooni nagu accept (), ootab ühendus lõputult. Mitteblokeeriv pistik naaseb kohe, kui talle öeldakse, et midagi teha, kas eduka tulemuse, vea või mittevajumisega (mis näitab, et hiljem saabub midagi). Selle tüübi kasutamise puuduseks on see, et peate käsitsi lukustama, et näha, kas kõik funktsioonid, millele helistate, on sisenenud. Funktsioonide select () abil saate pistikupesade komplekti edasi anda, et näha, millised neist on lugemis-, kirjutamis- või tagasisidevigadele valmis.

Asünkroonsed pistikupesad kasutavad funktsioonid tagastatakse ka kohe, kuid saate määrata konkreetse sündmuse toimumise akna protseduurile saatmise kohta sõnumi. Näiteks võite lukustuse saata SOCKET_GOTMSG sõnumi, kui see saab midagi. Tavaliselt on tark kontrollida vead (tülik, kuid vajalik), kui saate pistikupilti, et vältida tarbetute probleemide tekkimist hiljem. Esmalt määratleme mõned funktsioonid, mida kasutame asünkroonse pistiku seadistamiseks:

  • int WSAAsyncSelect (SOCKET, HWND hwnd, unsigned int wMsg, long lEvent)
    Seda funktsiooni kasutatakse pistiku tuvastamiseks asünkroonseks ja sellega seotud sõnumi seostamiseks. s on socket, millega te töötate. Hwnd on akna käepide, mis saab sõnumi, kui pistik loob sündmuse. wMsg on sõnum, mida soovite oma akna protseduurile saata (näiteks on ülaltoodud SOCKET_GOTMSG sõnum). Parameeter lEvent võtab ühe või mitu lippu, mis ütlevad pistikule, millistes sündmustes sõnum saadetakse. Mõned neist lipudest on:

    • FD_READ : pistik on valmis andmete vastuvõtmiseks
    • FD_WRITE : Socket on andmete saatmiseks valmis
    • FD_ACCEPT : kasutatakse serverites, see sõnum näitab, et kasutaja on ühendatud
    • FD_CONNECT : kasutatakse kliendirakendustes, see sõnum ütleb teile, et pistik on ühendatud
    • FD_CLOSE : pistik on äsja suletud
  • WSAGETSELECTERROR (LPARAM lparam)
    Määrab, kas pistikupesa on vea tagastanud. Tehniliselt ei ole see funktsioon, vaid makro ( võite tõeliselt luua väiketalu osapoolte juures seda vähest fakti ).
  • WSAGETSELECTEVENT (LPARAM lparam)
    Veel üks kasulik makro, mis on defineeritud winsock2.h-is, on WSAGETSELECTEVENT (), mida kasutatakse selleks, et täpselt näha, mida pistikupesa on teinud.

Nii et seadistage asünkroonse pistiku:

// alustame loo loomisega, mida Windows kasutab, et meiega ühendust võtta, kui midagi juhtub. 

# Define THERE_WAS_A_SOCKET_EVENT WM_USER + 100 // WM_USER on aluseks kohandatud sõnumitele

/ / Kustutades initsialiseerimiskoodi pärast CreateWindow () kutsume WSAAsyncSelect () 

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



// See tähendab: Windows, võtke minuga ühendust, kasutades selleks lippu THERE_WAS_A_SOCKET_EVENT, mis I 

// varem määratlesin, kui on andmeid, mida lugeda (FD_READ) või kui mul on õigus andmete saatmiseks 

// (FD_WRITE) või kui ma olen edukalt kellega on ühendatud (FD_CONNECT) või siis, kui ... jne.

// Meie akna protseduur (funktsioon, mis käitleb kõiki sõnumeid, mille Windows saadab teie rakendusele) 

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

{ 



	switch (msg) 

	{ 

		case THERE_WAS_A_SOCKET_EVENT: 

			if (WSAGETSELECTERROR ( lParam)) 

			{// Kui ilmnes viga, 

				closesocket (theSocket); 

				WSACleanup (); // shutdown Winsock 

				tagasi NETWORK_ERROR; 

			} 

			lüliti (WSAGETSELECTEVENT (lParam)) 

			{// Mis juhtus täpselt? 

				juhul FD_READ: 

					/ / saada andmete 

					katkestamine; 

				juhul FD_WRITE: 

					// kirjutada andmete 

					katkemist;

				juhul FD_CONNECT: 

					// on lihtsalt ühendatud serveriga 

					pausi; 

				juhtum ... / / Sama seadistus teiste lipud 

					murda; 

			} 

			murda 



		// teiste juhtumite aruanne koos loogikaga, mis käitleb teisi Windowsi sõnumeid 



	} 

}

Pange tähele, et te ei saa iga sündmuse jaoks määrata üht sõnumit, näiteks FD_READ SOCKET_GOTMSG ja FD_CONNECT SOCKET_CONNECTED. Seda seetõttu, et iga lipu seadistamiseks korduvad WSAAsyncSelect () kutsed tühistavad viimase kõne mõjud WSAAsyncSelect ().

Veel juhendeid ja lingid

Ma kirjutasin selle õpetuse 2000. Aasta detsembris ja sellest alates on seitse aastat võiganud külastanud pidevalt külastajaid ja parandusi. Ma loodan, et sulle meeldib lugeda nii palju kui mulle meeldis kirjutamine: tänan teid Johnnie Winsocki õpetuse kasutamise eest . Ülaltoodud on vaid lühike ülevaade võimalustest, mida saate Winsocki kaudu jõuda, ja teised on selle teema eripära uurimisel palju paremini tegutsenud:

(Lisateabe saamiseks liigutage kursorit raamatu katele.)
 

effective_tcpip_programming tcpip_sockets_in_c programming_windows maxim_1_yr

Ma nõustun Thomas Bleekeriga (MadWizard), et „võrguprogramm tundub lihtsam kui see on.“ Ma ei saa mulle muljet avaldada, kui tähtis on kasutada neid funktsioone koos silumisega, et saaksite näha, mis toimub. Lõppkokkuvõttes on teil palju paremini mõista, kuidas asjad töötavad, kui teete valesti, uurige, miks sa seda valesti tegid, ja siis tunnete rõõmu, et see on õige. Teisisõnu, eksimuste tegemine on see, kuidas me õpime.

Kuidas ma saaksin parandada?

Kas on midagi, mis vajab selgitamist? Kas juhendaja ei suuda Winsockiga seotud teemat katta, mida soovite õppida? Kas juhendaja täitis teie vajadused tarkvaraarendajatena? Kas see on lõbus? nutikalt kirjutatud? liiga lihtsustatud? või lihtsalt?