Johnnie’s Winsock Tutorial

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

Jeżeli zarejestrowałeś celowo modell moje samouczek Winsock, zostałeś najprawdopodobniej znalazł pomysł na swoje własne aplikacje komunikujące się za pośrednictwem Internetu jako fascynująca perspektywa jak to słyszałem. Lub, być może ktoś inny znalazł się perspektywą równie ciekawe i które zostały powierzone podkreślając tę wizję w rzeczywistość. W obu przypadkach sieci Winsock obsługa i ten samouczek pomoże Ci w osiągnięciu Twoich celów przedsiębiorstwa handlowego, po prostu poznawanie analytical programowanie sieci do użytku osobistego, albo coś pomiędzy.

Oto czego będziemy obejmujący:

 

  • Tworzenie gniazda odsłuchu: Biorąc pod uwagę niewielką armią funkcji sieci, możemy zbudować program, który cierpliwie czeka na połączenia przychodzące? (Tak, możemy.)
  • tworzenie własnych połączeń: podane kilka funkcji, możemy stworzyć program, który z powodzeniem łączy z serwerem odsłuchu? (Tak, możemy.)
  • wysyłanie i odbieranie: Raz osiągnęliśmy aktywnym połączeniem, w jaki sposób możemy go użyć do wymiany danych między dwoma programami? (domyślacie,—send() i recv().)
  • Non-blocking i asynchronicznego gniazda: w jaki sposób możemy zwiększyć skuteczność naszego kodu dzięki zastosowaniu różnych sieciowych programu? (możemy korzystać z naszego okna procedura trochę.)
  • Więcej samouczków i linków: jakie zasoby są tam ponad i poza ten tutorial? Ja podświetlić 3 powinna zająć Cię na chwilę (po uzyskaniu przetrawionego mój tutorial, oczywiście :-).
  • Komentarze & krytyczne mające: Oto Twoja okazja do stawki tutorial, zadawać pytania lub komentarze.

Choć możesz być chętna do reach, które wzbudzały podziw punkt, w którym aplikacja z powodzeniem podejmują swoje pierwsze połączenie, należy znać pojęcia za pomocą kodu. Starajmy się unikać po prostu manipulacja podany kod odpowiadający twoim potrzebom i zamiast określenia wymagań aplikacji, a dopiero potem wdrożyć, co wydaje się być najlepszym rozwiązaniem. Tyle mojego Zen z Software Development advice for now; zróbmy trochę programowanie sieci…

Feel free do pobrania cały samouczek kod notowaniu. Należy pamiętać, że kod podany w niniejszym samouczku powinna być powiązana z biblioteki Winsock, zwykle pliku wsock32.lib lub coś podobnie nazwane. Także, kiedy kod jest dokładnie taki, jak przedstawiony w samouczku w twoim własnym IDE (Dev-C , VC , C Builder, itd.), należy wybrać w celu utworzenia okna projektu WinMain(), aby uniknąć błędów.

Tworzenie muzyki gniazdo

 

aplikacji obsługa maszyn poza nazywane są serwery. Aplikacje serwerowe, posłuchać dla klientów poprzez inicjowanie jednego lub więcej gniazd odsłuchu. Kiedy klient łączy się jeden z tych gniazd odsłuchu, serwer otrzyma powiadomienie z Winsock, akceptuje połączenie, a następnie rozpoczyna wysyłkę i przechwycić wiadomości do i od nowego klienta. Bodaj najbardziej najprostsze metody, dzięki którym serwery obsłużyć wielu klientów jest do wychowu nowego wątku dla każdego połączenia z klientem. Serwer ten model najczęściej wykorzystuje blokowanie gniazda, które czasowo wstrzymać czekać na nadchodzące dane, a nowe połączenie, i innych zdarzeniach w sieci. Po pierwsze, przyjrzyjmy się zidentyfikować pewne załamywały się przyda, aby ustawić

parametry początkowe blokady gniazda:

 

  • WSADATA: struktura ta jest używana do tworzenia zapytań systemu operacyjnego do wersji Winsock naszego kodu wymaga. Aplikacja zwraca WSAStartup() aby zainicjować odpowiedni DLL.
  • GNIAZDA: obiektu (w rzeczywistości jest on zdefiniowany jako u_int, unsigned integer, w winsock.h—dobrze wiedzieć o smalltalk na przyjęciach) wykorzystywane przez aplikacje do przechowywania nasadki uchwyt.
  • SOCKADDR_IN: Aplikacja wykorzystuje tę strukturę, aby określić sposób gniazdo powinno działać. SOCKADDR_IN zawiera pola Adres IP i numer portu:

 

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

 

};

 

Pierwsze pole jest typu protokołu, który jest zazwyczaj AF_INET (TCP/IP). Jako gniazdo odsłuchu nie jest zainteresowana adres sieciowy wraz z maszyny, w którym rezyduje, Winsock automatycznie przypisuje adres IP i numer portu do słuchania gniazd podczas tworzenia.

Będziemy budować nasz pierwszy serwer odsłuchu z powyższymi strukturami i małej armii funkcje sieciowe:

 

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

 

}

 

Jedną rzecz, którą może niezwłocznie zawiadomienie o kod jest wysiłek oddany do sprawdzania błędów. Gdy wystąpi błąd, kod uzyskuje konkretny kod błędu przy WSAGetLastError() i zapisuje wynik w nret. Kod błędu jest następnie przesyłany wraz z ciągiem wskazując nazwę błędną funkcja niestandardowa funkcja o nazwie ReportError(). Nie, komunikat o błędzie jest zbudowany i widoczny dla użytkownika z wezwaniem, aby zrestartować aparat(), która jest częścią standardowego WinAPI. Na przykład, gdyby słuchać() nie powiodło się z powodu błędu kod 10093 (definiowane jako WSANOTINITIALIZED), zakończone error string byłoby “wezwanie do słuchania() zwraca błąd 10093!”. You, the ostrożny dewelopera, nastepnie poszukaj kodu i odkryć, że błąd wystąpił z powodu udanego połączenia do WSAStartup() nie zostały jeszcze dokonane.

Aleksandar Pawłow rozszerzona to ReportError() zawiera opisy około tuzina gniazdo wspólne błędy. Za jego uaktualnionej wersji, będziesz już potrzeby wyszukiwania informacji, co oznacza kod, a twój program staje się znacznie bardziej przyjazne dla użytkownika, przy niewielkim wysiłku z twojej strony.

Ujęte są również definiuje for NETWORK_ERROR i NETWORK_OK. Może to być przydatne podczas sprawdzania powrót wartości własnych funkcji sieciowych. Jeśli twoje funkcje wrócił jeden z tych wartości, wywołanie funkcji może wykonać prosty test płci aby ujawnić ewentualne błędy: jeśli (myNetworkingFunction() == NETWORK_ERROR) {…}. Na wywoływanie funkcji może następnie uzyskać specjalny kod z WSAGetLastError() i obsługi błędów. Ostatecznie, wykonujące dobrą błędów obecnie program oszczędzi Ci wielu dni czy tygodni od czasu opracowania jak będziesz od razu wiedzieć, dlaczego twój program zakończył się niepowodzeniem.

Oprócz powrotu nowego połączenia z klientem, accept() umożliwia serwerowi wyciąg informacji o kliencie, zamiast za pomocą metod wymagających dodatkowych wywołań funkcji lub czasu (co może stać się problemem w serwery gier, gdzie prędkość pętli accept jest szczególnie ważne). Aby skorzystać z tej funkcjonalności, przekazać na adres sockaddr_in struct wrzucił do sockaddr wskaźnik, tj. (LPSOCKADDR)&aSockaddrInStructure. Również wyłóż na integer zmienna, ustawić wartość int do sizeof do struct sockaddr, i podasz adres niedopełnieniem jako trzeci parametr. Jeżeli adres ma być zwrócone po wywołaniu funkcji parametru długość musi być obecna.

jdarnold ostrzega nas, aby nie wierzyć w witrynie MSDN dokumentacja dotycząca ten trzeci parametr: “MSDN docs implikuje nie musisz przechodzić w addrlen, że jest on jedynie opcjonalny parametr wyjściowy, ale są one błędne. Inbound mówi ile bajtów w buforze sockaddr, i outbound [Winsock] wypełnia w ilu [Winsock] szt. Jeśli przejedziesz zero jak len, [Winsock] nie chce dotykać bufora.”

to nie jest dużo a serwerem, ponieważ czeka na tylko jeden użytkownik może się połączyć i następnie natychmiast rozłącza, ale że jest najbardziej fundamentalną. Wystarczy aby skasować wszystko, wezwanie do WSAStartup() zawiera słowo określające, jaka wersja chcesz załadować (w tym przypadku jest to 1,1) i adresem WSADATA struktury. Dalej dowiemy się, jak połączyć się z innymi komputerami.

Tworzenie własnych połączeń

tworzenie gniazdo do podłączania, aby ktoś inny używa w większości te same funkcje, z wyjątkiem struct HOSTENT:

  • HOSTENT: struktura opowiadała w gniazdku, do którego komputera i port do podłączenia. Struktury te często pojawiają się jako LPHOSTENT zmiennych, które są jedynie odnośniki do struktury HOSTENT. Jak masz kod dla Windows, będziesz na ogół uważają, że każdy rodzaj danych poprzedzony LP oznacza, że typ jest faktycznie jest wskaźnikiem do “base” typu (na przykład LPCSTR jest wskaźnikiem do ciągu C, znany również jako char *).

Tak, Przejdźmy od razu do 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);

 

}

 

The most complicated line in the listing is the following:

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

Ponieważ wykonuje kilka operacji—jednym z nich stosunkowo ukryte—na raz. Rzućmy ją rozciął krok po kroku:

h_addr_list członkini struct HOSTENT zasadniczo jest definiowana jako char **h_addr_list, który ma wielu strunach, lub char *’s. gethostbyname() identyfikowane i naśladowali pośród wszystkich znanych adresów serwera na tej liście. Niemniej jednak, czy pojęcie wielu adresów w sposób zasadniczy sens? To wbrew pozorom nie. Twój komputer, w rzeczywistości posiada szereg ogólnych adresów sieciowych. Twój adres internetowy może być 205.182.67.96, twój adres LAN może być 10.0.0.2, oraz wszystkie komputery, na których system Windows jest zainstalowany w sposób naturalny mają “loopback” adres 127.0.0.1, wykorzystywane przez komputer, aby odnieść się do siebie w sieci lokalnej. Takie samo rozwiązanie stosuje się w pomidorowej adresy internetowe lub IP, który jest dlaczego lista jest potrzebna raczej niż przestrzeń dla pojedynczego adresu. Należy pamiętać, że adres preferowany, to jest najbardziej dostępnym adresem, zawsze jest kopiowany do pierwszego elementu listy, następnie drugi preferowany lub inne adresy.

Co jest *hostEntry->h_addr_list idzie? Można się łatwo domyślić, że Przesadna uległość wobec operatora (*) jest używany do dostępu do pojedynczego adresu na liście. Jednakże, przez niezapewnienie konkretnego wskaźnika zjadliwości, luki operację automatycznie odsłania pierwszą, preferowany adres. Zwłaszcza, że sekcja jest odpowiednikiem *hostEntry->h_addr_list[0], która jest zagwarantowana istnieć ponieważ serwer musi mieć co najmniej jeden adres.

Dalej, char * zwróconych przez operację związanej jest wrzucony do in_addr * lub LPIN_ADDR. Wreszcie, inną uległości operacja wykonywana jest w celu zwrotu in_addr struct określona przez wskaźnik, który może zawierać tylko jeden adres. Wynikowy in_addr struct jest następnie przypisywany do serverInfo.sin_addr. Późniejsze connect() przyjmuje jeden adres jako parametr podczas tworzenia połączenia z serwerem.

Jeśli adres IP serwera jest znany, ważnego HOSTENT mogą być uzyskane poprzez zastosowanie gethostbyaddr() (w przeciwieństwie do gethostbyname() użyte w poprzednim liście):

 

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

 

}

 

W tym przypadku, parametr inet_addr() jest wykorzystana do kopiowania string oznaczający adres IP bezpośrednio do in_addr struct. Potem, o adresie struct jest wrzucony do a const char * wymagane przez gethostbyaddr(). Obie metody są określone jako rozwiązanie adres serwera ponieważ Winsock zwraca pełny adres rekordy z częściową informację.

Kilka dodatkowych uwag: port 80 używany był po prostu ponieważ strona internetowa przenosi się na tym porcie. Jeśli weźmiemy wyślij ciąg do serwera WWW żądanie konkretnego pliku i próbować odbierać coś z powrotem, będziesz mieć bardzo prosta przeglądarka internetowa. Oczywiście, że tekst musi zawierać pełne polecenie HTTP. To wspaniałe, że możemy słuchać i łączyć się z innymi komputerami, ale komunikacja obejmuje również wysyłanie i odbieranie.

Wysyłanie i odbieranie

wysyłanie jest poddawana obróbce, dogodnie enough, przez send() funkcja:

 

int send(

 

SOCKET s,

 

const char * FAR buf,

 

int len,

 

int flags

 

);

 

Basically you would copy whatever you wanted into a buffer and use the send() function on a connected socket to make the data go to the other end:

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

 

}

 

Receiving is the same process, backwards:

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

 

}

 

Co ciekawe, jest to, że na pasku narzędzi w programie Microsoft Outlook z oznaczeniem “Send/Recv.” jest “odbierać” skracany do “Recv” wystarczy do zapewnienia wygląd przycisku w prawo lub jest to programmer’s nawyk od pisania recv() tak wiele razy? Tworzyć własne teorie spiskowe (znów dobry dla smalltalk na przyjęciach).

To jest, gdzie biegałem na mały problem podczas pisania moich własnych programów Winsock. Wystarczy używając recv() jest świetny jeśli użytkownik dokładnie wie, jak dużo danych będziesz otrzymywać (takie jak w grze, gdzie pierwszy bajt może być polecenie i następnego bajtu jako parametr itp.), ale jeśli nie wiesz, co robisz? Jeśli dane odbieraniu została rozwiązana przez newline bohaterki (wspólny problem z Java klientów markowskim C serwery), można pisać readLine(), aby uwiecznić wszystko do tego znaku. Oto co używałam:

 

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

 

}

 

}

 

}

 

Wektor jest wykorzystywane zamiast macierzy, ponieważ jego pojemność może zostać zwiększona automatycznie odpowiednio do długości linii. Jeśli recv() zwraca błąd (wskazywany przez bytesReceived jest mniejsza niż zero, NULL zostanie zwrócone. Ponieważ jest to możliwość, wywoływanie funkcji powinny zapewnić, że string zwróconego przez readLine() jest ważne przed użyciem. Wewnątrz pętli, jeden char jest odbierany z gniazda, a jeżeli nie to znak newline, dodawany do wektora. Jeżeli jest to znak newline, zawartość wektora są kopiowane do C string i odwzajemniał. Łańcuszek jest potraktowana jako jeden char wiekszych wektora i memset()’teda do zera tak, że powróciła linia będzie automatycznie NULL-terminated. Kończący się strun z zastępowaniem zapobiega dziwne błędy i jest ogólnie dobre praktyki programowania.

Ani prezentów to sprytnie udoskonalona wersja z obsługą backspaces i zdolność do zmieniania newline postać łatwo:

 

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

 

}

 

}

 

}

 

Nieblokowalne i asynchronicznego gniazda

aż do tego momentu mieliśmy opowiadać o zablokowanie gniazda, gdzie wywoływanie funkcji, takich jak accept() czeka w nieskończoność, aby nakłonić użytkownika do połączenia. A non-blocking gniazdo powraca natychmiast, gdy jest ona powiedziała coś zrobić, albo z pomyślnym wynikiem, błąd, albo nic (wskazując, że tam będzie coś do odbioru w późniejszym terminie). Wadą stosowania tego typu jest to, że będziesz musiał ręcznie zapytanie gniazdo do sprawdzenia, czy plik wynikowy ma przyjść na każdą funkcję nazywasz. Możesz przekazać komplet gniazd do select(), aby sprawdzić, które z nich są gotowe do czytania, pisania lub powróciły błędy.

Funkcje za pomocą asynchronicznego gniazda również wrócić natychmiast, ale możesz podać w celu wysłania wiadomości do Twojego okna gdy zdarzenie zostało złamane. Na przykład, możesz mieć gniazdo wyślij wiadomość socket_GOTMSG wiadomość ilekroć coś otrzyma. Zazwyczaj to bystry, aby sprawdzić pod kątem błędów (kłopotliwa, ale niezbędne) When you get a gniazdo komunikat aby zapobiec, powodując niepotrzebne problemy później. Najpierw zdefiniować niektóre funkcje użyjemy na numer +1-800-015-2882 asynchronicznego gniazda:

  • int WSAAsyncSelect ( GNIAZDO s, HWND hwnd, unsigned int wMsg, długo lEvent )

funkcja ta służy do identyfikacji gniazda jak asynchroniczne i powiązać wiadomość z nim. s to gniazdo jesteś współpracownikowi. hwnd jest uchwytem do okna, które otrzymają wiadomość, gdy gniazdo generuje zdarzenie. wMsg jest wiadomość, którą chcesz wysłać do procedury okna (przykładem jest socket_GOTMSG wiadomość z góry). Z lEvent parametr przyjmuje jedną lub więcej flag, które opowiadają gniazdo, na którym zdarzenia, aby wysłać wiadomość. Niektóre z nich są flagi:

o FD_READ: gniazdo jest gotowe do odbioru danych

o FD_WRITE: gniazdo jest gotowe do wysyłania danych

o FD_ACCEPT: stosowany w serwerach, ten komunikat oznacza, że użytkownik ma podłączony

o FD_CONNECT: stosowany w aplikacjach klienckich, komunikat ten informuje Cię, że gniazdo ma podłączone

o FD_CLOSE: gniazda została właśnie zamknięta

  • WSAGETSELECTERROR ( LPARAM lparam )

określa, czy gniazdko zwróciło błąd. Technicznie, nie jest to funkcja ale makro (naprawdę można wygenerować smalltalk na przyjęciach z tego stwierdzenia dotyczące niewiele).

  • WSAGETSELECTEVENT ( LPARAM lparam )

inne przydatne makra zdefiniowane w winsock2.h jest WSAGETSELECTEVENT(), które jest używane, aby zobaczyć dokładnie, co gniazdo ma zrobić.

Tak więc, bądźmy +1-800-015-2882 asynchroniczne gniazdo:

 

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

 

 

 

}

 

}

 

Note that you cannot define one message for each event, like SOCKET_GOTMSG for FD_READ and then SOCKET_CONNECTED for FD_CONNECT. This is because Wzywały do WSAAsyncSelect (), aby skonfigurować każde flagi spowoduje zlikwidowania skutków ostatniej zacheta do WSAAsyncSelect ().

Więcej samouczków i łącza

pisałam ten samouczek w grudniu 2000 r., a siedem lat od tego czasu obserwowaliśmy to nieustający strumień odwiedzających i ulepszeń. Mam nadzieję, że podobała czytania jak najwięcej ucieszyły mnie cieszył pisząc: dziękujemy za Jańcia Winsock Tutorial. Powyższa propozycja jest ale krótki przegląd możliwości można osiągnąć poprzez Winsock, a inni robili o wiele lepszy niż zadania mnie na sondowanie specyfiki tego przedmiotu:

(Przesuń mysz nad książką pokrywę , aby uzyskać więcej informacji).

 

 

 

  • interfejs Winsock Programmer’s FAQ
  • MadWizard sieciowych Winsock Tutorial w C (zespół dostępna jest również wersja)
  • 7 poświęconych sieci programowanie gier prezentowanych przez flipcode

zgadzam z Thomas Bleeker (MadWizard), że “programowanie sieci wydaje się łatwiejsze niż jest.” nie mogę zrobić wrażenie na was znaczenie praktykowanie korzystać z tych funkcji, a także za pomocą debuggera można więc zobaczyć, co jest grane. Będziesz ostatecznie mają znacznie lepsze zrozumienie jak wszystko działa, jeśli docierasz nieprawdę, zbadać, dlaczego masz to zły a następnie doświadczyć przyjemności wkomponowania go w prawo. Popełnianiu błędów, innymi słowy, jest to, w jaki sposób będziemy się uczyć.

Jak mogę poprawić?

Czy jest coś, co wymaga wyjaśnienia? Czy tutorial nie obejmują a Winsock związanej z tematem, który chciałeś poznać? Posiada samouczek spełnia twoje potrzeby jako programista? Jest to zabawne? Reklamowo-informacyjne napisane? Nadmiernie uproszczony? Lub po prostu prawo?