WinSock2 API. Protokol TCP.
Transkrypt
WinSock2 API. Protokol TCP.
Interfejs programowy Windows Sockets 2. Aplikacja klient-serwer TCP Echo Zagadnienia • Omówienie biblioteki Winsock. • Specyfikacja klienta tcpecho. • Specyfikacja serwera tcpecho. dr Zbigniew Lipiński Instytut Matematyki i Informatyki UO [email protected] Interfejs programowy Windows Sockets 2 Windows Sockets jest implementacja gniazd BSD (University of California-Berkeley Sockets API). 1993 - edycja WinSock wersja 1.1. 1996 - WinSock wersja 2.0. Windows Sockets jest interfejsem programowym warstwy transportowej modelu OSI, pozwalającym budować aplikacje sieciowe oparte o protokoły rodziny TCP/IP. Windows Sockets 2 definiuje interfejsy komunikacyjne do obsługi wielu standardów i usług: • DNS, SAP, X.509, • Quality of Service, • transmisji w trybie multicast, multipoint. 2 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego W skład specyfikacji WinSock2 wchodzą: • WinSock 2 API (budowa aplikacji). • WinSock 2 Service Provider Interface , udostępnianie interfejsów WinSock 2 protokołom transportowym. Komponenty Windows Open System Architecture (WOSA): • WinSock Service Provider Interface (SPI), nazwy funkcji zaczynają się od ‘WSP’, np. WSPBind(). • WinSock Provider Upcall, nazwy funkcji zaczynają się od ‘WPU’, np. WPUCloseEvent(). • WSC Windows Sockets Configuration , np. WSCDeinstallProvider(). • NSP Namespace Provider, np. NSPInstallServiceClass(). Nazwy funkcji Windows Sockets 2 zaczynają się od ‘WSA’. Przykład: WinSock 1.1: connect() WinSock 2.0: WSAConnect() 3 Architektura Windows Sockets 2 Źródło: MS Platform SDK. 4 Funkcje WinSock 2 Funkcje BSD: accept() bind() closesocket() connect() getpeername() getsockname() getsockopt() inet_addr() inet_ntoa() ioctlsocket() listen() ntohl(), ntohs() recv(), recvfrom() select() send(), sendto() setsockopt() shutdown() socket() Funkcje Windows: WSAAccept() WSAAsyncGetHostByAddr(), WSAAsyncGetHostByName(), WSAAsyncGetProtoByName(), WSAAsyncGetProtoByNumber(), WSAAsyncGetHostByName(), WSAAsyncGetServByName(), WSAAsyncGetServByPort(), WSAAsyncSelect(), WSACancelAsyncRequest(), WSAAsyncGetXByY(), WSACloseEvent(), WSACleanup(), WSAConnect(), WSACreateEvent(), WSADuplicateSocket(), WSAEnumNetworkEvents(), WSAEnumProtocols(), WSAEventSelect(), WSAGetLastError(), WSAGetOverlappedResult(), WSAGetQOSByName(), WSAHtonl(), WSAHtons(), WSAIoctl(), WSAJoinLeaf(), WSANtohl(), WSANtohs(), WSAProviderConfigChange(), WSARecv(), WSARecvFrom(), WSAResetEvent(), WSASend(), WSASendTo(), WSASetEvent(), WSASetLastError(), WSAGetLastError(), WSASocket(), WSAStartup() 5 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Funkcje WinSock 2 Funkcje Windows (cd): getservbyname(), getservbyport(), geth(), gethostbyaddr(), gethostname() gethostbyaddr(), getprotobyname(), gethostname(), get(), gets(), getservbyport(), inet_addr(), inet_ntoa() Funkcje konwersji nazw: WSAAddressToString(), WSAEnumNameSpaceProviders(), WSAGetServiceClassNameByClassId(), WSAInstallServiceClass(), WSALookupServiceBegin(), WSALookupServiceEnd(), WSALookupServiceNext(), WSARemoveServiceClass(), WSASetService(), WSAStringToAddress(), WSAAsyncGetHostByAddr(), WSAAsyncGetHostByName(), WSAAsyncGetProtoByName(), WSAAsyncGetProtoByNumber(), WSAAsyncGetServByName(), WSAAsyncGetServByPort(), WSACancelAsyncRequest() WSAGetServiceClassInfo() 6 Specyfikacja struktury WSADATA Nazwa struktury : WSADATA Opis : Struktura WSADATA zawiera informacje o implementacji gniazd (Windows Sockets). Struktura WSADATA jest zadeklarowana w pliku Winsock2.h. Aplikacja nie powinna używać atrybutów iMaxsockets, iMaxUdpDg, i lpVendorInfo struktury WSAData gdy wartość atrybutu wVersion, po wywołaniu WSAStartup jest co najmniej 2. Wyjaśnienie: This is because the architecture of Windows Sockets has been changed in version 2 to support multiple providers, and WSAData no longer applies to a single vendor's stack. Two new socket options are introduced to supply provider- specific information: SO_MAX_MSG_SIZE (replaces the iMaxUdpDg element) and PVD_CONFIG (allows any other provider-specific configuration to occur). Atrybuty : wVersion - Wersja gniazd WinSock którą ma używać biblioteka Ws2_32.dll. wHighVersion - Najwyższa wersja WinSock jaką może używać biblioteka .dll. Standardowo, wartość taka sama, jak wVersion. szDescription - Zerem zakończony string znaków ASCI (null-terminated ASCII string) do którego Ws2_32.dll kopiuje opis implementacji gniazd. Tekst (do 256 znaków) może być używany do opisów wiadomości. szSystemStatus - Zakończony zerem string znaków ASCI do którego WSs2_32.dll kopiuje informacje o statusie lub konfiguracji. Ws2_32.dll powinna używać tego atrybutu do tylko gdy informacje mogą być ważne dla użytkowników, atrybut nie powinien być traktowany jako rozszerzenie zmiennej szDescription. iMaxSockets - Pozostawiony dla zgodności poprzednimi wersjami gniazd. Parametr powinien być ignorowany przez Windows Socketsv.2 i wersje późniejsze (nie ma już jednej wartości dla wszystkich dostarczycieli usług). iMaxUdpDg - Atrybut ignorowany przez gniazda wersji 2 i następne. iMaxUdpDg pozostawiony dla zgodności z wersją gniazd Windows Sockets 1.1. Nie powinien być używany przy budowaniu nowych aplikacji. Dla określenia wielkości wiadomości dostarczyciela usług dla gniazda i typu gniazda aplikacje powinny używać getsockopt aby uzyskać wartość opcji SO_MAX_MSG_SIZE. lpVendorInfo - Atrybut ignorowany przez Windows Sockets v.2 i następne. lpVendorInfo pozostawiono dla zgodności z wersją gniazd Windows Sockets 1.1. Dla określenia konfiguracji dostarczyciela usług dla gniazda powinny używać getsockopt aby uzyskać wartość opcji PVD_CONFIG. 7 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Struktura WSADATA typedef struct WSAData { WORD wVersion; WORD wHighVersion; char szDescription[WSADESCRIPTION_LEN+1]; char szSystemStatus[WSASYS_STATUS_LEN+1]; unsigned short iMaxSockets; unsigned short iMaxUdpDg; char FAR * lpVendorInfo; }WSADATA, *LPWSADATA; 8 Specyfikacja struktury sockaddr_in Nazwa struktury: sockaddr_in Opis: Struktura sockaddr_in zawiera adres IP i numer portu odbiorcy danych. Struktura zadeklarowana w pliku Winsock2.h (dla IPv4), ws2tcpip.h (dla IPv6). Atrybuty: sin_family Kod rodziny adresów TCP//IP (wartość musi być AF_INET). sin_port Numer portu (IP port). sin_addr Adres IP hosta. sin_zero Pole służy do uzupełniania tak, aby wielkość struktury była taka sama jak SOCKADDR. Implementacja struktury sockaddr_in: struct sockaddr_in { short unsigned short struct in_addr char }; sin_family; sin_port; sin_addr; sin_zero[8]; 9 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja struktury in_addr Nazwa struktury : in_addr Opis: Struktura in_addr adres IP hosta. Struktura zdefiniowana w WINSOCK.H Atrybuty : S_un_b S_un_w S_addr - adres hosta w formacie u_chars. adres hosta w formacie dwóch u_shorts. adres hosta w formacie u_long. Implementacja struktury in_addr: struct in_addr { union { struct { unsigned char s_b1, s_b2, s_b3, s_b4; } S_un_b; struct { unsigned short s_w1, s_w2; } S_un_w; unsigned long S_addr; } S_un; }; 10 Specyfikacja struktury hostent Nazwa struktury : hostent Opis: Struktura jest używana przez funkcje do przechowywania informacji o hoście. Zadeklarowana w pliku Winsock2.h Atrybuty : h_name – Nazwa hosta. Jeżeli jest używany system DNS (lub podobny) zmienna zawiera pełną nazwę DNS’ową hosta FQDN (ang. Fully Qualified Domain Name). W pliku hosts, wartość zmiennej jest drugim wpisem, po adresie IP. h_aliases - Zerem zakończona tablica alternatywnych nazw hosta. h_addrtype - Typ adresu. h_length - Długość w bajtach każdego adresu. h_addr_list - Zerem zakończona lista adresów IP hosta. Adresy IP są zwracane w uporządkowaniu bajtów Big Endian. Makro h_addr zastępuje h_addr_list[0] w celu zgodności ze starszymi wersjami oprogramowania. Przykład. char *ServerName; LPHOSTENT hp; SOCKADDR_IN AdresIpSerwera; hp = gethostbyname(ServerName); AdresIpSerwera.sin_addr.S_addr = hp->h_addr; 11 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Implementacja struktury hostent typedef struct hostent { char FAR* char FAR FAR** short short char FAR FAR** } hostent; h_name; h_aliases; h_addrtype; h_length; h_addr_list; 12 Specyfikacja funkcji WSAStartup() Nazwa funkcji : WSAStartup() Zwracana wartość: int WSAStartup() zwraca zero gdy wywołanie funkcji zakończyło się sukcesem. W innym przypadku zwraca następujące kody błędów: WSASYSNOTREADY – WSAVERNOTSUPPORTED – WSAEINPROGRESS – Wskazuje, że sieć lub podsieć nie jest przygotowana do transmisji danych. Żądana wersja WinSock nie jest obsługiwana przez implementację WinSock. Trwa operacja blokowania WinSock 1.1. WSAEPROCLIM - Osiągnięto granicę liczby żądań dopuszczalną przez WinSock. WSAEFAULT - Błędna wartość wskaźnika lpWSAData (not a valid pointer). Argumenty: Opis: WORD wVersionRequested - [in] Najwyższa obsługiwana wersja WinSock, którą nadawca (caler) może użyć. Bajt najwyższy określa minimalną wersję, najniższy bajt określa maksymalna wersję. LPWSADATA lpWSAData - [out] Wskaźnik do struktury WSADATA. Funkcja WSAStartup() inicjuje użycie przez proces biblioteki WS2_32.DLL. Funkcja WSAStartup() musi być pierwszą funkcja Windows Sockets wywołana przez aplikacje. 13 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji WSACleanup() Nazwa funkcji: WSACleanup() Zwracana wartość: int WSACleanup() zwraca zero gdy wywołanie funkcji zakończyło się sukcesem. W innym przypadku zwraca SOCKET_ERROR, i określony kod błędu może być uzyskany poprzez wywołanie finkcji WSAGetLastError(): WSANOTINITIALISED - Przed wywołaniem tej funkcji WSACleanup() musi być wywołana z sukcesem funkcja WSAStartup(). WSAENETDOWN - Awaria sieci (network subsystem has failed). WSAEINPROGRESS - Trwa operacja blokowania WinSock 1.1 lub dostarczyciel usługi (service provider) obsługuje komunikat zwrotny (a callback function). Argumenty: brak Opis: Funkcja WSACleanup() kończy użycie biblioteki WS2_32.DLL. 14 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji socket() Nazwa funkcji: socket() Zwracana wartość: SOCKET Jeżeli nie ma błędów, funkcja socket() zwraca referencje do nowego gniazda. W przeciwnym przypadku zwraca kod INVALID_SOCKET. Kod błędu można uzyskać przez wywołanie funkcji WSAGetLastError(). Argumenty: int af, int type, int protocol af - [in] Typ adresu stosu (address family specification). type - [in] Typ adresu dla nowych gniazd (SOCK_STREAM, SOCK_DGRAM). protocol- [in] Numer protokołu użytego przez gniazdo. Wartość parametru: IPPROTO_IP, IPPROTO_TCP, 0. Opis: Funkcja socket() tworzy gniazdo o określonym typie (specific service provider). Funkcja zadeklarowana w pliku winsock2.h, implementacja w pliku ws2_32.lib. 15 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji WSAGetLastError() Nazwa funkcji: WSAGetLastError() Zwracana wartość: int Argumenty: brak Opis: Funkcja WSAGetLastError() zwraca status błędu ostatniej nieudanej operacji. 16 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji htons() Nazwa funkcji: htons() Zwracana wartość: u_short Funkcja htons() zwraca wartość w uporządkowaniu big-endian (TCP/IP network byte order). Argumenty: u_short hostshort hostshort - [in] 16-bitowa liczba w uporządkowaniu bajtów hosta (Windows: little-endian, Unix: bigendian). Opis: Funkcja htons() konwertuje u_short z uporządkowania bajtów hosta na big-endian. 17 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji inet_addr() Nazwa funkcji : inet_addr() Zwracana wartość: unsigned long Argumenty Opis : const char* cp cp - [in] Zerem zakończony string znaków reprezentujący adres IP zapisany w notacji kropkowanej (np. 127.0.0.1). : Funkcja inet_addr() konwertuje string zawierający adres IPv4 w wersji kropkowanej na odpowiedni adres w strukturze IN_ADDR. Przykład: użycie funkcji inet_addr() w strukturze sockaddr_in atrybut sin_addr.s_addr = inet_addr(nazwa_serwera); 18 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji gethostbyname() Nazwa funkcji : gethostbyname() Zwracana wartość: struct hostent* FAR Argumenty : const char* name name - [in] Wskaźnik do zakończonej zerem nazwy hosta (odbiorcy danych). Opis : Funkcja gethostbyname() pobiera nazwę hosta z bazy hosta. Zamiast gethostbyname() zalecane jest używanie funkcji getaddrinfo(). 19 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji CopyMemory() Nazwa funkcji : CopyMemory() Zwracana wartość: void Argumenty : PVOID Destination, const VOID* Source, SIZE_T Length Destination - [in] Wskaźnik zawierający początkowy adres docelowego bloku danych. Source - [in] Wskaźnik zawierający początkowy adres źródłowego bloku danych. Length - [in] Wielkość kopiowanego bloku danych (wielkość liczona w bajtach). Opis: Funkcja CopyMemory() służy do kopiowania bloków danych między obszarami pamięci. Funkcja zadeklarowana w pliku Winbase.h, Windows.h. 20 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji connect() Nazwa funkcji : connect() Zwracana wartość: int Argumenty : SOCKET s, const struct sockaddr* name, int namelen s - [in] Obiekt identyfikujący niepołączone gniazdo. name - [in] Nazwa gniazda w strukturze SOCKADDR do którego powinno być zrealizowane połączenie. namelen - [in] Długość nazwy gniazda (liczona bajtach). Opis : Funkcja buduje połączenie do określonego gniazda. 21 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji send() Nazwa funkcji : send() Zwracana wartość: int Argumenty : SOCKET s, const char* buf, int len, int flags s [in] Obiekt identyfikujący połączone gniazdo. buf [in] Bufor zawierający dane do wysłania. len [in] Długość danych w buforze (liczona w bajtach). flags [in] Wskaźnik określający typ wywołania funkcji. Flagi: MSG_DONTROUTE – Dane nie powinny być rutowane. WinSock service provider może ignorować tą flagę. MSG_OOB – Wysyła dane ‘Out-of-Band Data’ (gniazdo typu TCP, np. SOCK_STREAM). Opis : Funkcja send() wysyła dane do połączonego gniazda. 22 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji recv() Nazwa funkcji : recv() Zwracana wartość: int Jeśli nie ma błędów, funkcja recv() zwraca liczbę odebranych bajtów. Jeśli połączenie zostało zamknięte (gracefully closed), zwracaną wartością jest zero. W pozostałych przypadkach zwracana jest wartość SOCKET_ERROR. Kod błędu można uzyskać wywołując funkcję WSAGetLastError(). Argumenty : SOCKET s, char* buf, int len, int flags s - [in] Obiekt identyfikujący połączone gniazdo. buf - [out] Bufor na przychodzące dane. len - [in] Długość bufora (argumentu buf) w bajtach. flags - [in] Parametr określający typ wywołania funkcji. Opis : Funkcja recv() odbiera dane z połączonego (connected) gniazda lub z gniazda połączonego funkcją bind() (bound socket). 23 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji closesocket() Nazwa funkcji : closesocket() Zwracana wartość: int Argumenty : SOCKET s s Opis [in] Obiekt określający gniazdo do zamknięcia. : Funkcja closesocket() zamyka gniazdo. 24 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji bind() Nazwa funkcji : bind() Zwracana wartość: int W przypadku błędu funkcja zwraca SOCKET_ERROR z kodem który można uzyskać wywołując metodę WSAGetLastError(), w przeciwnym przypadku zwaraca zero. Argumenty : SOCKET s, const struct sockaddr* name, int namelen s- [in] Obiekt identyfikujący niepołączone gniazdo (unbound socket). name - [in] Adres IP do przypisania do gniazda (brany ze struktury SOCKADDR). namelen - [in] Długość (w bajtach) wartości parametru name. Opis : Funkcja kojarzy adres lokalny hosta z gniazdem. 25 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji listen() Nazwa funkcji : listen() Zwracana wartość: int Jeśli nie ma błędów funkcja listen() zwraca zero. W pozostałych przypadkach zwracana jest wartość SOCKET_ERROR. Kod błędu można uzyskać wywołując funkcję WSAGetLastError(). Argumenty Opis : SOCKET s, int backlog s- [in] Obiekt identyfikujący połączone (bound), nierozłączone (unconnected) gniazdo. backlog - [in] Maksymalna długość kolejki czekających połączeń. Jeżeli wartość parametru jest ustawiona na SOMAXCONN, serwer ustawia wartość tego parametru na maksymalnym, ale ‘rozsądnym’ poziomie. Nie ma standardowej procedury określania wartości tego parametru. : Funkcja listen() ustawia gniazdo w stan nasłuchiwania. 26 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji accept() Nazwa funkcji : accept() Zwracana wartość: SOCKET Jeżeli nie ma błędu funkcja accept() zwraca typ SOCKET. Zwrócona wartość jest uchwytem do gniazda do którego jest tworzone połączenie. W pozostałych przypadkach jest zwracana wartości INVALID_SOCKET. Kod błędu można uzyskać wywołując funkcję WSAGetLastError(). Argumenty : SOCKET s, struct sockaddr* addr, int* addrlen s[in] Obiekt identyfikujący gniazdo, które przeszło w stan nasłuchiwania poprzez wywołanie funkcji listen(). addr [out] Parametr opcjonalny, wskaźnik do bufora, który przechowuje adres łączącego się hosta. Format parametru addr jest wyznaczany na podstawie wartości parametru sa_family ze struktury SOCKADDR. addrlen - [out] Opcjonalny wskaźnik do liczby zawierającej długość parametru addr. 27 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji htonl() Nazwa funkcji : htonl() Zwracana wartość: u_long Argumenty : u_long hostlong hostlong [in] 32-bitowa liczba w uporządkowaniu little endian (windows). Opis : Funkcja htonl() konwertuje u_long z uporządkowania little endian na big endian. 28 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji ntohs() Nazwa funkcji : ntons() Zwracana wartość: u_short Funkcja ntons() zwraca wartość w uporządkowaniu hosta. Argumenty Opis : u_short netshort netshort - [in] 16-bit liczba w uporządkowaniu big-endian (TCP/IP network byte order). : Funkcja ntons() konwertuje liczbę u_short z uporządkowania bajtów big-endian na uprządkowanie hosta. Aplikacja decyduje czy konwersja musi być dokonana. Przykład: local.sin_port = ntons((short)iPort); 29 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji inet_ntoa() Nazwa funkcji: inet_ntoa() Zwracana wartość: char* FAR Jeżeli nie ma błędów inet_ntoa() zwraca wskaźnik char* do statycznego bufora zawierającego adres w standardzie kropkowanym. W pozostałych przypadkach funkcja zwraca NULL. Argumenty : struct in_addr in in - [in] Wskaźnik do struktury in_addr reprezentującej adres IP hosta. Opis : Funkcja inet_ntoa() konwertuje adres IPv4 na adres w formacie kropkowanym (dotted format). 30 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji CreateThread() Nazwa funkcji : CreateThread() Zwracana wartość: HANDLE Funkcja zwraca uchwyt do nowego wątku. Argumenty: LPSECURITY_ATTRIBUTES lpThreadAttributes - [in] Wskaźnik do struktury SECURITY_ATTRIBUTES która określa czy zwrócony uchwyt może być dziedziczony (przez proces potomny). Gdy wartość lpThreadAttributes = NULL, uchwyt nie może być dziedziczony. SIZE_T dwStackSize - [in] Początkowa wartość stosu liczona w bajtach. System zaokrągla wartość wielkość ‘strony’. Jeżeli parametr ma wartość zero nowy wątek używa domyślnej wartość. LPTHREAD_START_ROUTINE lpStartAddress - [in] Wskaźnik do funkcji aplikacji typu LPTHREAD_START_ROUTINE która ma być wywołana przez wątek. Wskaźnik zawiera początkową (startową) wartość adresu wątku. LPVOID lpParameter - [in] Wskaźnik do zmiennej która ma być przekazana do wątku. DWORD dwCreationFlags - [in] Flaga która służy do kontroli tworzenia wątku. Gdy flaga ma wartość CREATE_SUSPENDED, wątek jest po utworzeniu zawieszony, nie zostanie uruchomiony do czasu wywołania funkcji ResumeThread(). Gdy flaga ma zwartość zero wątek jest uruchamiany zaraz po utworzeniu. LPDWORD lpThreadId - [out] Wskaźnik do zmiennej zawierającej informacje o identyfikatorze wątku (thread identifier). 31 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji CreateThread(), cd. Wartości Arg: Opis lpThreadAttributes = NULL dwStackSize = 0 lpStartAddress = ClientThread lpParameter = (LPVOID)sClient dwCreationFlags = 0 lpThreadId = &dwThreadId : Funkcja CreateThread() tworzy wątek wykonywany wewnątrz wirtualnej przestrzeni adresowej wywołanego procesu. 32 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji CloseHandle() Nazwa funkcji : CloseHandle() Zwracana wartość: BOOL Funkcja zwraca TRUE gdy zamknięcie uchwytu zakończyło się sukcesem. W sytuacji błędnej funkcja zwraca zero. Argumenty : HANDLE hObject hObject - [in, out] Uchwyt do otwartego obiektu. Opis : Funkcja CloseHandle() zamyka otwarty uchwyt obiektu. 33 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji ClientThread() Nazwa funkcji : ClientThread() Zwracana wartość: DWORD WINAPI Argumenty : LPVOID Implementacja funkcji ClientThread(): DWORD WINAPI ClientThread(LPVOID lpParam) { SOCKET sock = (SOCKET)lpParam; char chBuff[DEFAULT_BUFFER]; int iRet, iNumLeft, iIdx; while(1) { iRet = recv(sock, chBuff, DEFAULT_BUFFER, 0); if (iRet == 0) return 0; else if (iRet == SOCKET_ERROR) { cout << "recv() error no: " << WSAGetLastError() << endl; return 0; } 34 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja funkcji ClientThread() chBuff[iRet] = '\0'; cout << "Liczba odebranych bajtów: " << chBuff << endl; iNumLeft = iRet; iIdx = 0; while(iNumLeft > 0) { iRet = send(sock, &chBuff[iIdx], iNumLeft, 0); if (iRet == 0) return 0; else if (iRet == SOCKET_ERROR) { cout << "send() error: " << WSAGetLastError() << endl; return 0; } iNumLeft -= iRet; iIdx += iRet; } // koniec while(iNumLeft > 0) } // koniec while(1) return 0; } 35 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Protokół TCP TCP, ang. Transmission Control Protocol. Specyfikacja RFC 793. TCP jest protokołem warstwy transportowej modelu OSI. Protokół TCP jest protokołem połączeniowym umożliwiającym wykrywanie błędów transmisji. Cechy protokołu TCP: • stosuje pozytywne potwierdzanie odbioru danych (pole ACK=1) • ma możliwość ustalania priorytetu przesyłania segmentów • ma możliwość kontroli i usuwania błędów (np. retransmisji niepotwierdzonych pakietów). 36 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Protokół TCP W procesie synchronizacji 'połączenia TCP' między nadawcą (A) i odbiorcą (B) wymieniane są trzy segmenty TCP. Uzgadnianie są wartości w polu 'Sequence number' w wysyłanych i potwierdzanych pakietach: (1) A ----> B flaga SYN=1, pole ‘Sequence number=X’, ISN nadawcy (A), Np. SEQ=X=100 (2) A <---- B flagi ACK=1, SYN=1 pole ‘Sequence number=Y’, ISN odbiorcy (B), pole ‘Acknowledgement number=X+1’ Np. SEQ=Y=300, ACK=X+1=101 (3) A ----> B flaga ACK=1 pole ‘Acknowledgement number=Y+1, Np. SEQ=X+1=101, ACK=Y+1=301 37 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja projektu Serwer TCP Echo Nazwa projektu: tcpserver Typ projektu: Win32 console application Lista plików: tcpserver.cpp Metoda kompilacji: Microsoft Visual C++ 2008. Utworzyć projekt typu 'Win32 console application‘ w menu (-)File-> New -> Project-> Other languages-> Visual C++ -> win32 -> Win32 console application -> wpisać nazwę: tcpserver -> przycisk (OK) -> Okno Win32 application wizard – tcpserver-> wybrać: Application settings -> wybrać: Empty project-> Przycisk (Finish). Konfiguracja projektu: (-)Project-> nazwa_projektu Properies... -> Configuration Properies-> Linker-> Input -> Additional Dependecies, wpisać: ws2_32.lib. Funkcjonalność: Aby uruchomić serwer należy w linii komend (Run-> cmd) wpisać: \> tcpserver Aby uruchomić klienta należy w linii komend (Run-> cmd) wpisać: \> tcpclient 38 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja projektu. Serwer TCP Echo Pliki nagłówkowe: Funkcje programu: Funkcje API: Funkcja użytkownika: winsock2.h, iostream WSAStartup(), socket(), inet_addr(), bind(), listen(), accept(), recv(), send(), closesocket(), htonl(), ntohs(), inet_ntoa(), CloseHandle(), WSAGetLastError(), WSACleanup(). CreateThread() 39 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja projektu. Serwer TCP Echo Zmienne programu: #define DEFAULT_PORT 5150 #define DEFAULT_BUFFER 4096 #define DEFAULT_IP_ADDRESS "127.0.0.1" WSADATA SOCKET int wsd; sListen, sClient; iAddrSize; HANDLE DWORD struct hThread; dwThreadId; sockaddr_in int BOOL char iPort = DEFAULT_PORT; // numer portu do nasluchiwania bInterface = FALSE; // nasluchiwanie na okreslonej karcie szAddress[128] = DEFAULT_IP_ADDRESS; // adres IP karty do nasłuchowania local, client; 40 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego (1) Sprawdzenie czy zostały otwarte biblioteki WinSock. Czy WSAStartup(MAKEWORD(2,2), &wsd) != 0 ? (2) Utworzenie gniazda. sListen = socket(AF_INET, SOCK_STREAM, , IPPROTO_TCP ); (3) Inicjowanie obiektu local struktury sockaddr_in. local.sin_addr.s_addr = inet_addr(szAddress); local.sin_family = AF_INET; local.sin_port = htons(iPort); (4) Kojarzenie adresu lokalnego hosta z gniazdem. bind(sListen, (struct sockaddr *)&local, sizeof(local)); (5) Nasłuchiwanie na połączenie i akceptacja połączenia. listen(sListen, 8); // 8 - maksymalna długość kolejki czekających połączeń sClient = accept(sListen, (struct sockaddr *)&client, &iAddrSize); (6) Utworzenie wątku dla połączenia z klientem. hThread = CreateThread(NULL, 0, ClientThread, (LPVOID)sClient, 0, &dwThreadId); (7) Zamknięcie wątku i sesji CloseHandle(hThread); closesocket(sListen); WSACleanup(); 41 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja projektu. Klient TCP Echo Nazwa projektu: Typ projektu: Lista plików: tcpclient Win32 console application tcpclient.cpp Metoda kompilacji: Microsoft Visual C++ 2008. Utworzyć projekt typu 'Win32 console application‘ w menu (-)File-> New -> Project-> Other languages-> Visual C++ -> win32 -> Win32 console application -> wpisać nazwę: tcpclient -> przycisk (OK) -> Okno Win32 application wizard – tcpclient-> wybrać: Application settings -> wybrać: Empty project-> Przycisk (Finish). Konfiguracja projektu: (-)Project-> nazwa_projektu Properies... -> Configuration Properies-> Linker-> Input -> Additional Dependecies, wpisać: ws2_32.lib. Funkcjonalność: tcpclient łączy się z serwerem za pomcą protokołu TCP, wysyła wiadomość do serwer. Serwer odpowiada, odsyłając tą samą wiadomość (odpowiedź echa). Uruchomienie serwera: \>tcpserver Uruchomienie klienta: \>tcpclient 42 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja projektu. Klient TCP Echo Pliki nagłówkowe: winsock2.h, iostream Funkcje programu: Funkcje API: WSAStartup(), WSAGetLastError(), socket(), htons(), inet_addr(), gethostbyname(), connect(), send(), recv(), closesocket(), WSACleanup(). Struktury programu: WSDATA, sockaddr_in, hostent 43 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Specyfikacja projektu. Klient TCP Echo Zmienne programu: #define DEFAULT_COUNT #define DEFAULT_SERVER_PORT #define DEFAULT_BUFFER #define DEFAULT_MESSAGE #define DEFAULT_SERVER //#define DEFAULT_SERVER WSADATA SOCKET char int 1 5150 2048 ”Test serwera TCP” ”127.0.0.1” ”host_name” wsd; sClient; chBuffer[DEFAULT_BUFFER]; iRetSend; struct sockaddr_in struct hostent server; *host = NULL; char chServer[128] = DEFAULT_SERVER, chMessage[1024] = DEFAULT_MESSAGE; int iPort = DEFAULT_SERVER_PORT; DWORD dwCount = DEFAULT_COUNT; // // // // nazwa serwera tekst wiadomosci do wyslania numer portu serwera do polaczenia liczba wyslanych wiadomosci 44 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Klient TCP Echo. Struktura programu. (1) Sprawdzenie poprawności argumentów przekazywanych do programu. (2) Sprawdzenie czy zostały otwarte biblioteki WinSock Czy WSAStartup(MAKEWORD(2,2), &wsd) != 0 (3) Utworzenie gniazda. sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); (4) Inicjowanie obiektu server struktury sockaddr_in. server.sin_family = AF_INET; server.sin_port = htons(iPort); server.sin_addr.s_addr = inet_addr(chServer); (5) Pobranie adresu IP serwera gdy podano jego nazwę host = gethostbyname(chServer); 45 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego Klient TCP Echo. Struktura programu. (6) Przekazanie adresu IP serwera do obiektu server CopyMemory(&server.sin_addr, host->h_addr_list[0], host->h_length); (7) Połączenie i sprawdzenie czy połączenie zostało utworzone connect(sClient, (struct sockaddr *)&server, sizeof(server)) == SOCKET_ERROR (8) Wysłanie danych na serwer, ret – liczba wysłanych bajtów. iRet = send(sClient, chMessage, strlen(chMessage), 0); (9) Odebranie danych z serwera. iRet = recv(sClient, chBuffer, DEFAULT_BUFFER, 0); (10) Zamkniecie gniazda i sesji closesocket(sClient); WSACleanup(); 46 Z. Lipiński, Instytut Matematyki i Informatyki, Uniwersytet Opolski, Podstawy programowania sieciowego ///////////////////////////////// // TCP serwer ///////////////////////////////// #include <winsock2.h> #include <iostream> using namespace std; #define DEFAULT_SERVER_PORT 5150 #define DEFAULT_BUFFER 4096 #define DEFAULT_IP_ADDRESS "127.0.0.1" int main() { WSADATA SOCKET int HANDLE DWORD struct int BOOL char wsd; sListen, sClient; iAddrSize; hThread; dwThreadId; sockaddr_in local, client; iPort = DEFAULT_SERVER_PORT; // numer portu do nasluchiwania bInterface = FALSE; // nasluchiwanie na okreslonej karcie szAddress[128] = DEFAULT_IP_ADDRESS; // adres IP karty do nasluchowania if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) { cout << "WSAStartup() error" << endl; return 0; } 47 sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if (sListen == SOCKET_ERROR) { cout << "socket() error: " << WSAGetLastError() << endl; closesocket(sListen); WSACleanup(); return 0; } if (bInterface) { local.sin_addr.s_addr = inet_addr(szAddress); } else local.sin_addr.s_addr = htonl(INADDR_ANY); local.sin_family = AF_INET; local.sin_port = htons(iPort); if (bind(sListen, (struct sockaddr *)&local, sizeof(local)) == SOCKET_ERROR) { cout << "bind() error: " << WSAGetLastError() << endl; closesocket(sListen); WSACleanup(); return 0; } listen(sListen, 8); 48 while (1) { iAddrSize = sizeof(client); sClient = accept(sListen, (struct sockaddr *)&client, &iAddrSize); if (sClient == INVALID_SOCKET) { cout << "accept() error: " << WSAGetLastError() << endl; closesocket(sListen); WSACleanup(); return 0; } cout << "Accepted client: " << inet_ntoa(client.sin_addr) << ":" << ntohs(client.sin_port) << endl; hThread = CreateThread(NULL, 0, ClientThread,(LPVOID)sClient, 0, &dwThreadId); if (hThread == NULL) { cout << "CreateThread() error: " << GetLastError() << endl; return 0; } CloseHandle(hThread); } // koniec while (1) closesocket(sListen); WSACleanup(); return 0; } // koniec main() 49 ///////////////////////////// // TCP Echo client ///////////////////////////// #include <winsock2.h> #include <iostream> using namespace std; #define DEFAULT_COUNT #define DEFAULT_SERVER_PORT #define DEFAULT_BUFFER #define DEFAULT_MESSAGE #define DEFAULT_SERVER // #define DEFAULT_SERVER 1 5150 2048 "Test serwera TCP" "127.0.0.1" "builder" //mozna wpisac nazwe hosta int main() { WSADATA SOCKET char int wsd; sClient; chBuffer[DEFAULT_BUFFER]; iRetSend; struct sockaddr_in server; struct hostent *host = NULL; char chServer[128] = DEFAULT_SERVER, chMessage[1024] = DEFAULT_MESSAGE; int iPort = DEFAULT_SERVER_PORT; DWORD dwCount = DEFAULT_COUNT; // nazwa serwera // tekst wiadomosci do wyslania na serwer // numer portu serwera do polaczenia // liczba wyslanych wiadomosci 50 if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) { cout << "WSAStartup() error" << endl; return 0; } sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sClient == INVALID_SOCKET) { cout << "socket() error no: " << WSAGetLastError() << endl; return 0; } server.sin_family = AF_INET; server.sin_port = htons(iPort); server.sin_addr.s_addr = inet_addr(chServer); if (server.sin_addr.s_addr == INADDR_NONE) { host = gethostbyname(chServer); if (host == NULL) { cout << "Unable to resolve: " << chServer << endl; return 0; } CopyMemory(&server.sin_addr, host->h_addr_list[0], host->h_length); } if (connect(sClient, (struct sockaddr *)&server, sizeof(server)) == SOCKET_ERROR) { cout << "connect() error no: " << WSAGetLastError()<< endl; return 0; } 51 for(unsigned int i=0; i<dwCount; i++) { iRetSend = send(sClient, chMessage, strlen(chMessage), 0); if (iRetSend == 0) return 0; else if (iRetSend == SOCKET_ERROR) { cout << "send() error no: " << WSAGetLastError() << endl; return 0; } cout << "Wyslano bajtow: " << iRetSend << endl; iRetSend = recv(sClient, chBuffer, DEFAULT_BUFFER, 0); if (iRetSend == 0) return 0; else if (iRetSend == SOCKET_ERROR) { cout << "recv() error no: " << WSAGetLastError() << endl; return 0; } chBuffer[iRetSend] = '\0'; cout << "Odebrano " << iRetSend << " bajtow. Msg: " << chBuffer << endl; } // end for() closesocket(sClient); WSACleanup(); return 0; } // end main() 52