Back to site
Since 2004, our University project has become the Internet's most widespread web hosting directory. Here we like to talk a lot about web servers, web development, networking and security services. It is, after all, our expertise. To make things better we've launched this science section with the free access to educational resources and important scientific material translated to different languages.

Program za podučavanje Johnnie Winsock-a

Ako ste namerno stigli na moj Winsock program za podučavanje, najvjerojatnije ste našli ideju vlastite programe komunicira putem interneta, kao fascinantan prospect kao imam. Ili, možda neko drugi smatra verovatnoću jednako zanimljivom, a vama je bilo povereno donošenje ove vizije u stvarnost. U svakom slučaju, usluga Winsock mrežnog servisa i ovaj program za podučavanje pomoći će vam u postizanju svojih ciljeva u komercjalnom preduzecu, jednostavno istražujući područje mrežnog programiranja za ličnu upotrebu upotrebu, ili nešto između.

Evo šta ćemo pokrivati:

  • Stvaranje priključka za slušanje: S obzirom na malu vojsku funkcija mreže, možemo li izgraditi program koji strpljivo čeka dolazne veze?(Da, možemo.)

  • Izrada vaše vlastite veze:S obzirom na još nekoliko funkcija, možemo li stvoriti program koji uspešno povezuje sa serverima za slušanje?(Da,možemo)

  • Slanje i primanje:Kada postignemo aktivnu vezu, kako ćemo je koristiti za razmenu podataka između dva programa?(Pogodili ste---send() and recv().)

  • Neblokirajući i asinkroni priključci:Kako možemo povećati efikasnost našeg koda inplementiranjem šema umrežavanja?(Traćimo malo vreme sa našom procedurom prozora.)

  • Više Programa za podučavanje i Linkovi:Koji resursi postoje iznad i izvan ovog programa za podučavanje? Ističem 3 koja će vas zaokupiti neko vreme (nakon što ste svarili moj program za podučavanje naravno :-) .)

  • Komentari i kritike:Evo vam prilike da ocenite program za podučavanje, postavljate pitanja ili šaljete komentare .

Iako ćete možda biti nestrpljivi da dosegnete tačku strahopoštovanja ri kojoj vaša aplikacija uspešno pravi svoju ​​prvu vezu, čuvajte se koncepata iza koda. Pokušajte izbeći jednostavno manipulisanje zadanim kodom da bi odgovarao vašim neposrednim potrebama i umesto toga identifikovao zahteve vaše aplikacije i tek onda implementirao ono što izgleda kao najbolje rešenje. To je dovoljno za sada od mog Zen za razvoj softvera saveta, hajde da uradimo neko mrežno programiranje ...

Slobodno preuzmite kompletnu listu kodova programa za podučavanje .Setite se da bilo koji kod prikazan u ovom vodiču treba biti povezan sa Winsock bibliotekom,obično wsock32.lib ili nešto slično nazvano. Takođe, kada se koristi kod tačno kao što je prikazan u programu za podučavanje u vašem vlastitom ​​IDE (Dev-C + +, Microsoft VC + +, C + + Builder, itd.), izaberite da izgradite Windows projekat sa WinMain () kako bi se izbegle greške.

Stvaranje Priključka za Slušanje

Aplikacije koje servisiraju izvan mašina nazivaju se serveri. Server aplikacije slušaju za klijente inicijalizacijom jednog ili više priključaka za slušanje. Kada se klijent spaja na jedau od tih priključaka za slušanje, server primi obaveštenje iz Winsock, prihvati povezivanje, i počinje da šalje i presreće poruke ka i od novog klijenta. Možda najjednostavnija metoda kojom server rukuje sa više klijenata je da umnoži novu programsku nit za svaku vezu klijenta. Ovaj model servera najčešće koristi blokiranje priključaka, koji privremeno pauzira čekanje ulaznih podataka, novu vezu i druge mrežne događaje. Prvo, hajde da identifikujemo neke strukture koje će nam trebati da inicijalizujemo blokirajuću priključke:

  • WSADATA:Ova struktura se koristi za ispitivanje operativnog sistema za verziju Winsock koju naš kod zahteva. Aplikacija poziva WSAStartup() inicijalizirajući ispravno Winsock DLL.
  • SOCKET:Objekat (u stvari,definisan je kao u_int, nepotpisan integer, u winsock.h---good to know for smalltalk at parties) koji koriste aplikacije za čuvanje regulatora priključaka
  • SOCKADDR_IN:Aplikacija koristi ovu strukturu da bi odredila kako bi priključak trebao da radi. SOCKADDR_IN sadrži polja za IP adresu i broj porta:


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

};

Prvo polje je tip protokola, što je obično AF_INET (TCP / IP). Pošto priključak za slušanje nije podrazumevan adresom mreže mašine na kojoj se nalazi, Winsock automatski dodeljuje IP adresu i broj porta za priključke za slušanje prilikom stvaranja.

Izgradićemo naš prvi server za slušanje sa navedenim strukturama i malom armijom mrežnih funkcija:


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

}

Jedna stvar koju može odmah primetiti vezana za kod je količina napora uložena u provere grešaka. Kada dođe do greške, kod dobije određenu šifru greške sa WSAGetLastError() i čuva rezultat u nret. Kod greške se onda šalje zajedno sa nizom pokazujući ime propale funkcije uobičajenoj funkciji pod nazivom ReportError(). Tu, izgrađena je poruka i prikazana korisniku pozivom na MessageBox(), koji je deo standardne WinAPI. Na primer, sa propalom listen() sa kodom greške 10.093 (definisanim kao WSANOTINITIALISED), gotov niz greške će biti "Call to listen() returned error 10093!". Vi, pametan programer, onda ćete pogledati kod i otkriti da je došlo do pogreške, jer uspešan poziv na WSAStartup() još nije napravljen.

Aleksandar Pavlov proširio je ovaj ReportError() da bi uključio opise za oko desetak zajedničkih grešaka priključaka . Koristeći njegovu nadograđenu verziju ,više nećete morati da tažite značenje koda, i vaš program postaje više naklonjen korisnicima sa vrlo malo truda sa Vaše strane.

Takođe su uključene definicije za NETWORK_ERROR i NETWORK_OK. To bi moglo biti korisno kod provere povratnih vrednosti vaših mrežnih funkcijakcijama. Ako su vaše funkcije vratile jednu od tih vrednosti, pozivajuća funkcija može izvesti jednostavan test kako bi se otkrila bilo koja greška pogreške: ako (myNetworkingFunction () == NETWORK_ERROR) {...}. Funkcija koja se poziva tada bi mogla dobiti određeni kod sa WSAGetLastError () i obraditi grešku usklađeno. Na kraju, sprovođenjem dobre šeme rukovanja greškom sada će vam uštedeti mnogo dana ili nedelja u vremenu razvoja poštno ćete odmah znati zbog čega vaš program nije uspeo.

Osim povratka veze novog klijenta, accept() omogućava serveru da izdvoji informacije o klijentu a ne kroz metode koje zahtevaju dodatne funkcijske pozive ili vreme (što može postati problem u serverima igara, gde je brzina accept petlje osobito bitna). Da biste iskoristili prednost ove funkcije, prenesite u adresi sockaddr_in struct cast na sockaddr pokazivač, tj. (LPSOCKADDR) & aSockaddrInStructure. Takođe, deklarišite celobrojne varijable, postavite vrednost int na veličinu sockaddr struct, i prenesite adresu celobrojne vrednosti kao treći parametar. Ako informacija adrese treba da se vrati nakon poziva funkcije, parametar dužine mora biti prisutan.

jdarnold nas upozorava da ne verujemo u MSDN dokumentaciju vezane ovaj treći parametar: MSDN dokumenti podrazumevaju da morate preći u addrlen, to je samo dodatni izlazni parametar, ali oni su pogrešni. Ulazni govori koliko je bajtova u sockaddr baferu, a odlazi [Winsock] ispunjava koliko je [Winsock] korišćen. Ako prenesete nula kao lupu, [Winsock] nemojte dirati bafer.

"To nije mnogo do servera pošto čeka samo jednog korisnika da se poveže i odmah zatim isključi, ali to je osnovni. Samo da se razjasne stvari, poziv na WSAStartup() uključuje WORD koja određuje koju verziju želite da učitate (u ovom slučaju to je 1,1) i adresu WSADATA strukture. Dalje, mi ćemo pokriti povezivanje sa drugim računarima.

Izrada Vaših Vlastitih Veza

Stvaranje priključka za spajanje na nekoga drugoga koristi većinom iste funkcije, sa izuzetkom HOSTENT strukture:

  • HOSTENT:struktura korišćena za saopštavanje priključku na koji računar i port da se spoji. Te strukture najčešće se pojavljuju kao LPHOSTENT varijable, koje su samo pokazivače na HOSTENT strukture. Kao što kodirate za Windows, obično ćete naći da je bilo koji tip podataka prethodio LP označava da je tip zapravo pokazivač do "osnovnog" tipa (na primer, LPCSTR je pokazivač na niz C, takođe poznat kao char * ).

Dakle, hajde da pristupimo pravilno kodu


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

}

Najkomplikovaniji red na listi je sledeći:

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

zato što obavlja nekoliko operacija --- jedan od njih relativno skriven --- odjednom. Idemo ga rastaviti korak po korak

The:h_addr_list član HOSTENT struct je u osnovi definisan kao char ** h_addr_list, koji je niz znakovnih nizova, ili char * 's. gethostbyname() koji identifikuje i kopira sve od poznatih adresa servera na ovom spisku. Međutim, da li koncept više adresa fundamentalno ima smisla? Zapravo, ima. Računar, u stvari, ima niz opštih mrežnih adresa. Vaša Internet adresa može biti 205.182.67.96, vaša LAN adresa može biti 10.0.0.2, a svi računari na kojem je instaliran Windows prirodno imaju "loopback" adresu 127.0.0.1, korišćenu od strane računara koje se odnose na sebe na lokalnoj mreži . Isti koncept se primenjuje u području internet adresa ili IP-a, što je razlog zašto je spisak potreban radije nego skladišni prostor za jednu adresu. Imajte na umu da željena adresa, koja je, najdostupnija adresa, uvek je kopirana u prvi element spiska, praćena drugom preferiranom ili drugim adresama.

Šta * hostEntry-> h_addr_list radi? Možete pretpostaviti da je priklonjeni operater (*) bio upotrebljen za pristup jednoj adresi na spisku. Međutim, ne uspevajući da se osigura određeni indeks,priklonjena operacija automatski otkriva prvu, željenu adresu. To posebno poglavlje je ekvivalent * hostEntry-> h_addr_list [0], što garantovano postoji jer server mora imati barem jednu adresu.

Dalje, char * vraćen od strane priklonjene operacije ubačen je u in_addr * ili LPIN_ADDR. Konačno, još jedna priklonjena operacija se izvodi da se vrati in_addr struct povezanu sa pokazivačem, koji može samo držati jednu adresu. Rezultirajuća in_addr struct onda je dodeljena serverInfo.sin_addr. Naknadno connect() uzima jednu adresu kao parametar pri formiranju veze sa serverom.

Ako je IP adresa servera poznata, validna HOSTENT može biti dobijena kroz korišćenje gethostbyaddr() (za razliku od gethostbyname() korišćenom u prethodnom spisku):


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

}

U ovom slučaju, inet_addr () se koristi za kopiranje niza označavajući IP adresu direktno u in_addr struct. Nakon toga, adresa struct je ubačena u const char * kako to zahteva gethostbyaddr(). Obe metode se smatraju onim metodama koje rešavaju adresu servera pošto Winsock vraća punom adresom zapise iz parcijalnih informacija.

Nekoliko dodatnih napomena: Port 80 je korišćen jednostavno jer se transferi internet web odigravaju preko ovog porta. Ako ste nameravali da pošaljete znakovni niz na web server zahtevajući određenu datoteku i pokušali da dobijete nešto natrag, imali biste vrlo jednostavan web pretraživač. Naravno, taj niz mora sadržavati punu HTTP naredbu. Divno je što možemo da slušamo i da se povežemo sa drugim računarima, ali komunikacija takođe uključuje slanje i primanje.

Slanje i primanje

Slanja je regulisano, dovoljno povoljno, od strane send() Funkcije:


int send(

  SOCKET s,

  const char * FAR buf,

  int len,

  int flags

);

Uglavnom bi kopirali ono što ste hteli u bafer i koristili send() funkciju na povezanom priključku kako bi podaci otišli na drugi kraj:


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

}

Prijem je isti proces, unatrag:


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

}

" Zanimljivo je napomenuti da postoji dugme alatnoj traci u programu Microsoft Outlook označeno "Send/Recv." Je li "Receive" skraćeno "Recv" jednostavno kako bi se osiguralo da dugme izgleda pravilno, ili je to navika programera koji tipka recv() toliko puta? Oblikujte vašu vlastitu teoriju zavere (opet,dobro za ćaskanje na zabavama).

Ovo je mesto gde sam naleteo na malo problema prilikom pisanja svog vlastitog Winsock programa. Sama upotreba recv() je super kada znate tačno koliko podataka ćete primati (kao što je u igri, gde prvi bajt može biti naredba i sledeći bajt biti parametar, itd.), ali kada ne znate, šta ćete da radite? Ako su podaci koje primate završeni karakterom novog reda (čest problem sa Java klijentima koji se obraćaju C serveru), možete napisati readLine() funkciju za hvatanje sve do tog karaktera. Evo šta sam koristio:


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

      }

   }

}

vektor se koristi umesto niza jer se njegov prostor može povećati automatski da bi odgovarao dužini reda. Ako recv() vraća grešku (undicirano ako je bytesReceived manje od nula), NULL se vraća. Budući da je to mogućnost, pozivanje funkcija trebalo bi da osigura da je niz vraćen iz readLine () validan pre upotrebe. Unutar petlje, jedan karakter je primljen od priključka, a ako nije karakter novog reda, dodat je vektoru. Ako je karakter novog reda, sadržaj vektora se kopira u C nizi vraća. Niz je deklarisan da bude za jedan karakter veci od vektora i memset() 'ted na nuli, tako da će povratni red biti automatski NULL-ukinut. Završetak niza sa NULL sprečava neobične greške i opšte je dobra vežba programiranja.

Nor predstavlja ovu pametno poboljšanu verziju sa podrškom za backspaces i sposobnošću da se promeni karakter novog reda:


// 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;

		}

	}

}

Neblokirajući i Asinhroni priključci

Do ove tačke smo govorili o blokirajućim utičnicama, gde poziv funkcije kao što je accept() čeka neodređeno vreme korisnika da se poveže. Neblokirajući priključak vraća odmah bilo da mu je rečeno da nešto uradi, bilo sa uspešnim rezultatom, greškom, ili ničim (što znači da će se nešto kasnije primiti). Nedostatak korišćenja ove vrste je što ćete morati ručno da upitate priključak da bi videli da li se rezultat pojavljuje za svaku funkciju koju pozivate.Možete preneti set priključka na select() funkciju da bi videli one koji su spremni za čitanje, pisanje, ili su vratili greške.

Funkcije koje koriste asinhrone priključke odmah vraćaju, ali možete odrediti poruku koju ćete poslati na prozor postupka kada se navedeni događaj dogodi. Na primer, možete priključku poslati poruku SOCKET_GOTMSG kad god primi nešto. Obično je pametno proveriti zbog grešaka (teško, ali je potrebno), kada dobijete poruku priključka kako bi se sprečilo uzrokovanje nepotrebnih problema kasnije. Prvo, hajde da definišemo neke funkcije koje ćemo koristit da podesimo asinhroni priključak:

  • int WSAAsyncSelect ( SOCKET s, HWND hwnd, unsigned int wMsg, long lEvent )
    Ova funkcija se koristi za identifikaciju priključka kao asinhronog i povezivanje poruke sa njim. s je priključak sa kojim radite. HWND je ručka do prozora koji će primiti poruku kada priključak generiše događaj. wMsg je poruka koju želimo da pošaljemo na prozor postupka (primer je SOCKET_GOTMSG poruka odozgo). The lEevent parametar uzima jedan ili više indikatora koji govore priključku na koje događaje da pošalje svoju poruku. Neki od tih indikatora su:
      • FD_READ:Priključak je spreman za primanje podataka
      • FD_WRITE: Priključak je spreman da šalje podatke
      • FD_ACCEPT:Korišćena u serverima, ova poruka ukazuje da je korisnik spojen
      • FD_CONNECT:Korišćena u aplikacijama klijenata, ova poruka ukazuje da je priključak spojen
      • FD_CLOSE:Priključak je upravo zatvoren

  • WSAGETSELECTERROR (LPARAM lparam)
    Određuje da li je priključak vratio grešku.Tehnički, to nije funkcija nego makro(što stvarno može genersati Smalltalk sa ovom malom činjenicom).

  • WSAGETSELECTEVENT (LPARAM lparam)
    Drugi koristan makro definisan u winsock2.h je WSAGETSELECTEVENT (), koji se koristi da se vidi tačno ono šta je priključak učinio.

Dakle, hajde da postavimo jedan asinhroni priključak:


// 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



	}

}

Imajte na umu da se ne može definisati jedna poruka za svaki događaj, kao što je SOCKET_GOTMSG za FD_READ a zatim SOCKET_CONNECTED za FD_CONNECT. To je zato što će ponovljeni pozivi WSAAsyncSelect() za podešavanje svakog indikatora poništiti učinke poslednjeg poziva WSAAsyncSelect().

Više Programa Za Podučavanje i linkovi

Napisao sam ovaj tutorial u decembru 2000, a sedam ili toliko godina od tada videli smo konstantan tok posetilaca i poboljšanja. Nadam se da ste uživali čitajući koliko sam ja uživao dok sam pisao: hvala za korišćenje Johnnie's Winsock Tutorial.Iznad je samo kratak pregled mogućnosti koje možete dosegnuti kroz Winsock, a drugi su napravili daleko bolji posao od mene pri ispitivanju specifičnosti ovog predmeta:

Slažem se sa Thomasom Bleeker (MadWizard) da "mrežno programiranje izgleda lakše nego što zapravo jeste." Ne mogu vam nametnuti značaj vežbanja upotrebe ovih funkcija zajedno sa programom za otklanjanje grešaka kako biste videli šta se zapravo dešava. Na kraju ćete imati mnogo bolji pojam o tome kako stvari funskcionišu ako pogrešite, istražite zašto ste pogrešili a zatim doživite zadovoljstvo u ispravljanju greške i odrađivanju posla kako treba. Drugim rečima, na greškama se uči.

Kako da postanem bolji?

Ima li nešto što treba pojasniti? Da li uputstvo nije uspelo da pokrije temu vezanu za Winsock o kojoj ste želeli da saznate nešto? Da li je uputstvo ispunilo vaše potrebe kao softver developera? Da li je zabavno? Pametno napisano? Suviše pojednostavljujuće? Ili baš kako treba? Kliknite ovde da postavite komentar





Published (Last edited): 12-03-2013 , source: http://johnnie.jerrata.com/winsocktutorial/