2.01k likes | 2.34k Views
Programowanie Usług Sieciowych. Tematyka wykładu. Wprowadzenie do Informatyki. Podstawy Programowania. Programowanie Obiektowe. Inżynieria Oprogramowania I. Podstawy Sieci Komputerowych. Inżynieria Oprogramowania II. Programowanie Usług Sieciowych. Sieciowe Systemy Operacyjne.
E N D
Tematyka wykładu Wprowadzenie do Informatyki Podstawy Programowania Programowanie Obiektowe Inżynieria Oprogramowania I Podstawy Sieci Komputerowych Inżynieria Oprogramowania II Programowanie Usług Sieciowych Sieciowe Systemy Operacyjne
Sprawy organizacyjne (laboratorium) • Sposób zaliczenia laboratorium analogiczny do laboratoriów WDI i PSK – odpowiedź z programu • Wykorzystywane języki programowania:C++, C# • Wykorzystywane środowiska programistyczne: Dev-Cpp, Visual C# 2005 Express Edition, Visual Web Developer 2005 Express Edition,
Tematyka laboratorium • Prog. 1 – klient i serwer wykorzystujące bibl. winsock i protokoły TCP/IP i UDP/IP • Prog. 2 – klient i serwer wykorzystujące obiekty C# obsługujące TCP/IP • Prog. 3 – klient i serwer wykorzystujące .NET Remoting w C# • Prog. 4 – klient i serwer wykorzystujące WebServices i SOAP w C# • Prog. 5 – klient wykorzystujący WebService dostarczany przez zewnętrzny serwer
Pojęcia podstawowe - zakres wykładu Model DoD (Department of Defense ) Model OSI aplikacji aplikacji prezentacji sesji pr. 2 - 5 transportowa transportowa pr. 1 sieciowa sieciowa łącza danych dostępu do sieci fizyczna
Pojęcia podstawowe – warstwy Użyteczne dla człowieka aplikacje: Serwer www, przeglądarka Model DoD (Department of Defense ) Rozdysponowuje informacje do odpowiednich aplikacji aplikacji zastosowania Ustala drogę do docelowego komputera w sieci transportowa TCP UDP sieciowa IPv4, IPv6 dostępu do sieci dostępu do sieci Fizyczne połączenie między urządzeniami sieciowymi (np. modem, karta sieciowa) Warstwą transportową może być jeden z protokołów TCP lub UDP. Możliwe jest też ominięcie warstwy transportowej i korzystanie z bezpośrednio z oprogramowania IPv4 i IPv6 (tzw. Gniazdo surowe – raw socket)
Pojęcia podstawowe - Ogólny przegląd protokołów IPv4 IPv6 ping traceroute Prog 1 Prog 1 traceroute Prog 2 Prog 2 ping TCP UDP ICMP IPv6 ICMPv6 IPv4 Warstwa dostępu do sieci
Pojęcia podstawowe - Omówienie wybranych protokołów • IPv4 – wersja 4 protokołu IP (Internet Protocol). Używa się w nim 32 bitowych adresów. Obsługuje on dostarczanie pakietów danych dla TCP, UDP, ICMP i innych. Przykład 192.168.1.2 = 1100 0000.1010 1000.0000 0001.0000 0010= 11000000101010000000000100000010= 3232235778
Pojęcia podstawowe - Omówienie wybranych protokołów • TCP – protokół sterowania transmisją (Transmission Control Protocol) jest protokołem obsługi połączeniowej procesu użytkownika, umożliwiającym niezawodne i w pełni dwukierunkowe (full-duplex) przesyłanie strumienia bajtów. Przykładem gniazd strumieniowych są gniazda gniazda TCP. Do zadań protokołu TCP należy należy potwierdzanie, uwzględnianie procesu oczekiwania, dokonywanie retransmisji itp. W większości internetowych programów użytkowych stosuje się protokół TCP. Protokół TCP może korzystać z IPv4 albo IPv6.
Pojęcia podstawowe - Omówienie wybranych protokołów • Protokół UDP – (User Datagram Protocol) jest protokołem obsługi bezpołączeniowej procesów użytkownika. Przykładem gniazd datagramowych są gniazda UDP. W odróżnieniu od protokołu TCP, który jest niezawodny, protokół UDP nie daje gwarancji, że datagramy UDP zawsze dotrą do wyznaczonego celu. Protokół TCP może korzystać z IPv4 albo IPv6
Pojęcia podstawowe - Omówienie wybranych protokołów • Protokół ICMP – (Internet Control Message Protocol). Obsługuje komunikaty o błędach i infrormacje sterujące przesyłane między routerami a stacjami. Komunikaty te są zazwyczaj generowane i przetwarzane przez oprogramowanie sieciowe protokołów TCP/IP, nie zaś przez procesy użytkownika. Istnieje też oprogramowanie użytkowe, które używa protokołu ICMP (program ping). Protokół ICMP istnieje w dwóch wersjach ICMPv4 i ICMPv6
biblioteki • Za komunikację sieciową w systemie Windows odpowiada biblioteka WinSock (w Dev-Cpp plik nagłówkowy winsock.h (i winsock2.h) jest położony w c:\dev-cpp\include\ • W systemach unixowych jest to przeważnie biblioteka socket, jej plik nagłówkowy nosi nazwę socket.h • Przy programowaniu wyższego poziomu wykorzystywać będziemy odpowiednie obiekty C#
Program 1 #include <iostream> #include <winsock.h> using namespace std; void PrintWSAData(LPWSADATA pWSAData); int main(int argc, char **argv) { WORD wVersionRequested = MAKEWORD(1,1); WSADATA wsaData; int rc; cout << (int)LOBYTE(wVersionRequested) << "." << (int)HIBYTE(wVersionRequested) << endl; rc = WSAStartup(wVersionRequested, &wsaData); if (!rc) PrintWSAData(&wsaData); else cout << rc << endl; WSACleanup(); system("PAUSE"); return 0; }
Program 1 cd void PrintWSAData(LPWSADATA pWSAData) { cout << endl; cout << "WSADATA" << endl; cout << "-----------------------" << endl; ; cout << "Version .............: " << (int)LOBYTE(pWSAData->wVersion) << "." << (int)HIBYTE(pWSAData->wVersion) << endl ; cout << "HighVersion .........: " << (int)LOBYTE(pWSAData->wHighVersion) << "." << (int)HIBYTE(pWSAData->wHighVersion) << endl; cout << "Description..........: " << pWSAData->szDescription << endl; cout << "System status........: " << pWSAData->szSystemStatus << endl; cout << "Max number of sockets: " << pWSAData->iMaxSockets << endl; cout << "MAX UDP datagram size: " << pWSAData->iMaxUdpDg << endl; }
Omówienie programu 1 • c:\dev-cpp\include\windef.htypedef unsigned short WORD • MAKEWORD() – makro tworzy wartość WORD z dwóch wartości BYTE MAKEWORD(1,1) -> 0000 0001 0000 0001 MAKEWORD(1,2) -> 0000 0002 0000 0001 • LOBYTE, HIBYTE – zwraca
Wersja interfejsu Winsock, której chce używać funkcja wywołująca Struktura WSAData Najwyższa wersja interfejsu Winsock, obsługiwana przez bibliotekę WSAData WORD wVersion Opis tekstowy załadowanej biblioteki wHighVersion WORD char szDescription[WSADESCRIPTION_LEN+1] Informacje o stanie lub konfiguracji char szSystemStatus[WSADESCRIPTION_LEN+1] unsigned short iMaxSockets Maksymalna liczba gniazd unsigned short iMaxUdpDg char * lpVendorInfo Maksymalny rozmiar datagramu UDP Informacje o sprzedawcy
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 * lpVendorInfo; } WSADATA;
Omówienie programu 1 cd int WSAStartup(WORD,LPWSADATA); typedef WSADATA *LPWSADATA; (winsock.h) void PrintWSAData(LPWSADATA pWSAData); - to zdefiniowaliśmy sami WSACleanup();
Program 1 #include <iostream> #include <winsock.h> using namespace std; void PrintWSAData(LPWSADATA pWSAData); int main(int argc, char **argv) { WORD wVersionRequested = MAKEWORD(1,1); WSADATA wsaData; int rc; cout << (int)LOBYTE(wVersionRequested) << "." << (int)HIBYTE(wVersionRequested) << endl; rc = WSAStartup(wVersionRequested, &wsaData); if (!rc) PrintWSAData(&wsaData); else cout << rc << endl; WSACleanup(); system("PAUSE"); return 0; }
Program 1 cd void PrintWSAData(LPWSADATA pWSAData) { cout << endl; cout << "WSADATA" << endl; cout << "-----------------------" << endl; ; cout << "Version .............: " << (int)LOBYTE(pWSAData->wVersion) << "." << (int)HIBYTE(pWSAData->wVersion) << endl ; cout << "HighVersion .........: " << (int)LOBYTE(pWSAData->wHighVersion) << "." << (int)HIBYTE(pWSAData->wHighVersion) << endl; cout << "Description..........: " << pWSAData->szDescription << endl; cout << "System status........: " << pWSAData->szSystemStatus << endl; cout << "Max number of sockets: " << pWSAData->iMaxSockets << endl; cout << "MAX UDP datagram size: " << pWSAData->iMaxUdpDg << endl; }
Wynik działania programu 257 1.1 WSADATA ----------------------- Version .............: 1.1 HighVersion .........: 2.2 Description..........: WinSock 2.0 System status........: Running Max number of sockets: 32767 MAX UDP datagram size: 65467
Podsumowanie cz1 • Modele OSI, DoD • Protokoły TCP, UDP, ICMP, IP • Winsock • Struktura WSAData, • Funkcja WSAStartup
Część 2 - UDP • Charakterystyka gniazd UDP • Przebieg komunikacji przy użyciu gniazd UDP • Funkcje wywoływane w celu przesłania danych i ich parametry • Struktury niezbędne w komunikacji UDP
Część 2 - UDP • Protokół UDP jest bezpołączeniowy – nie wymaga żadnego długotrwałego związku między klientem i serwerem, klient nie ustanawia połączenia z serwerem, a serwer nie akceptuje połączenia z klientem. Czeka na odbiór danych z dowolnego źródła • Przez jedno gniazdo serwer może wysłać datagram do jednego serwera i w następnej chwili czasowej może wysłać przez to samo gniazdo drugi datagram do drugiego serwera. • Jest wykorzystywany w DNS, NFS, SNMP • Opisany w RFC 768 • Komunikacja przy użyciu datagramów UDP • Każdy datagram można traktować jako rekord
Komunikacja UDP/IP Aplikacja n Gniazdo n Gniazdo n Aplikacja n Aplikacja 3 Gniazdo 3 Gniazdo 3 Aplikacja 3 Aplikacja 2 Gniazdo 2 Gniazdo 2 Aplikacja 2 Aplikacja 1 Gniazdo 1 Gniazdo 1 Aplikacja 1 Host Adres ip: 192.168.1.2 Host Adres ip: 192.168.1.2
Komunikacja UDP Klient Klient Datagram UDP Datagram UDP Do kogo wysyłamy Do kogo wysyłamy Do którego gniazda Do którego gniazda Kto wysyła Kto wysyła Z którego gniazda Z którego gniazda Datagram UDP Datagram UDP IPv4/IPv6 IPv4/IPv6 Do kogo wysyłamy Do kogo wysyłamy Kto wysyła Kto wysyła
Wywołania funkcji gniazd podczas współpracy serwera i klienta UDP klient udp serwer udp socket() socket() sednto() bind() recvfrom() Blokuje do czasu nadejścia datagramu od klienta recvfrom() Przetwarzanie żądania close() sendto()
Funkcja socket SOCKET socket(int , int, int); • family - określa rodzinę protokołów (IPv4, IPv6, NetBios, IPX) • type - Rodzaj gniazda (strumieniowe, datagramowe, surowe) • Protocol – wskazanie dostawcy transportowego (TCP, UDP, ICMP) • SOCKET – deskryptor gniazda – do tej zmiennej zapisujemy jaki rodzaj gniazda został utworzony po wywołaniu funkcji socket.
Funkcja bind int bind(SOCKET,const struct sockaddr*,int); Funkcja przypisuje gniazdu lokalny adres protokołowy. Adres protokołowy składa się z 32-bitowego adresu IPv4 oraz 16-bitowego numeru portu (dla IPv6 adres ma 128bitów) • Deskryptor gniazda – uzyskany w wyniku wywołania funkcji socket() • Wskaźnik do gniazdowej struktury adresowej • Rozmiar gniazdowej struktury adresowej
Funkcja recvfrom int recvfrom(SOCKET,char*,int,int,struct sockaddr*,int*); Funkcja oczekuje na przyjęcie danych, jeżeli zakończyła się sukcesem zwraca liczbę otrzymanych bajtów. • Deskryptor gniazda – uzyskany w wyniku wywołania funkcji socket() • Wskaźnik do bufora do którego zostaną zapisane dane • Rozmiar bufora w bajtach • Flaga, wpisujemy 0 • Wskaźnik do struktury adresowej do której zostaną zapisane dane hosta z którego dane zostały odebrane • Wskaźnik do rozmiaru bufora do przechowywania gniazdowej struktury adresowej
Funkcja sendto int sendto(SOCKET,const char*,int,int,const struct sockaddr*,int); Funkcja oczekuje na przyjęcie danych • Deskryptor gniazda – uzyskany w wyniku wywołania funkcji socket() • Wskaźnik do bufora z którego zostaną pobrane dane • Rozmiar bufora w bajtach • Flaga, wpisujemy 0 • Wskaźnik do struktury adresowej zawierającej adres hosta do którego dane zostaną wysłane. Wpisujemy tutaj długość ogólnej gniazdowej struktury gniazdowej struct sockaddr { u_short sa_family; char sa_data[14]; }; jest to właściwie niepotrzebna struktura, która jest wykorzystywana do przekazywania parametrów funkcji, tam gdzie trzeba przekazać strukturę gniazdową. Struktura gniazdowa jest inna dla każdego protokołu i dlatego wymyślono strukturę ogólną dla wszystkich protokołów żeby na nią rzutować. W zasadzie problem byłby rozwiązany przez parametr void ale kiedy pisano funkcje gniazd w standardzie ansi C nie było jeszcze void
Program 2 serwer #include <iostream> #include <winsock.h> • using namespace std; • void DatagramServer(short nPort); • #define PRINTERROR(s) \ • cout << "Blad " << s << " " << WSAGetLastError() << endl; • int main() • { • WORD wVersionRequested = MAKEWORD(1,1); • WSADATA wsaData; • int nRet; • short nPort; • cout << "Startujemy program serwera udp" << endl; • cout << "Podaj port na ktorym mam nasluchiwac: "; • cin >> nPort; • nRet = WSAStartup(wVersionRequested, &wsaData); • if (wsaData.wVersion != wVersionRequested) • { • cout << ”Zla wersja winsock” << endl; • return -1; • } • DatagramServer(nPort); • WSACleanup(); • return 0; • }
2 void DatagramServer(short nPort); Prototyp funkcji, która będzie serwerem UDP 3-4 #define PRINTERROR(s) \cout << "Blad " << s << " " << WSAGetLastError() << endl; Definicja makra wyświetlającego na ekranie błąd s – treść komunikatu Int WSAGetLastError(void) – funkcja zwraca wartość całkowitą odpowiadającą kodowi błędu. Wszystkie kody błędów zwracane przez funkcję mają predefiniowane stałe w pliko winsock.h
9 int nRet; Zmienna do przechowywania wartości zwracanych przez funkcje 10 int nPort; Zmienna do której zostanie zapisany numer portu na którym będzie nasłuchiwał serwer 13 cin >> nPort; Wczytujemy z klawiatury nr portu na którym sewer będzie nasłuchiwał
13 if (wsaData.wVersion != wVersionRequested); Sprawdzamy czy wersja winsock załadowana do pamięci pokrywa się z tą o którą „prosiliśmy” 20 DatagramServer(nPort); Wywołujemy funkcję serwera z parametrem nPort (numer portu)
Program 2 serwer • void DatagramServer(short nPort) • { • SOCKET theSocket; • theSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); • if (theSocket == INVALID_SOCKET) • { • PRINTERROR("socket()"); • return; • } • SOCKADDR_IN saServer; • saServer.sin_family = AF_INET; • saServer.sin_addr.s_addr = INADDR_ANY; • saServer.sin_port = htons(nPort); • int nRet; • nRet = bind(theSocket,(LPSOCKADDR)&saServer, sizeof(struct sockaddr)); • if (nRet == SOCKET_ERROR) • { PRINTERROR("bind()"); • closesocket(theSocket); • return; • } • int nLen; • nLen = sizeof(SOCKADDR); • char szBuf[256]; • nRet = gethostname(szBuf, sizeof(szBuf)); • if (nRet == SOCKET_ERROR) • { PRINTERROR("gethostname()"); • closesocket(theSocket); • return; • } • cout << "Serwer o nazwie: " << szBuf << " nasłuchuje na porcie: " << nPort << endl; • SOCKADDR_IN saClient; • memset(szBuf, 0, sizeof(szBuf)); • nRet = recvfrom(theSocket, szBuf, sizeof(szBuf),0,(LPSOCKADDR)&saClient,&nLen); • cout << "Dane otrzymane od klienta: " << szBuf << endl; • sendto(theSocket, szBuf,strlen(szBuf), 0, (LPSOCKADDR)&saClient, nLen); • closesocket(theSocket); • return; • }
funkcja void DatagramServer(short nPort) 3 deklarujemy zmienną typu SOCKET; Zmienna ta w pliku winsock.h jest zdefiniowana jako typedef u_int SOCKET; Zmienna ta nazywa się deskryptorem gniazda i do niej funkcja socket() zapisuje jaki rodzaj gniazda został stworzony po jej wywołaniu; mogą to przykładowo być: #define INVALID_SOCKET (0) #define SOCKET_ERROR (-1) #define SOCK_STREAM 1 #define SOCK_DGRAM 2 #define SOCK_RAW 3 #define SOCK_RDM 4 #define SOCK_SEQPACKET 5 .
4 theSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); Aby wykonać operacje wyjścia wejścia w sieci proces musi najpierw wywołać funkcję socket określając żądany rodzaj protokołu komunikacyjnego, np TCP IPv4 Funkcja ta ma 3 parametry: family, type, protocol i zwraca zmienną SOCKET, Paramety funkcji socekt mogą przyjmować następujące wartości: family - określa rodzinę protokołów i może przyjmować: - AF_INTET - IPv4 - AF_INTET6 - IPv6 - AF_LOCAL - protokół z dziedziny Unix - AF_ROUTE - gniazda wyznaczania tras - AF_KEY - gniazda kluczowe - AF_NETBIOS - dla netbiosu - AF_IPX - dla protokołu IPX/SPX - i inne zależnie od protokołu type - określa rodzaj gniazda: - SOCK_STREAM - gniazdo strumieniowe - SOCK_DGRAM - gniazdo datagramowe - SOCK_RAW - gniazdo surowe - i inne zależnie od protokołu protocol - wskazanie dostawcy transportowego (w unix wstawia się 0) - IPPROTO_TCP - IPPROTO_UDP - IPPROTO_RAW - IPPROTO_ICMP
Nie wszystkie połączenia family + type + protocol są poprawne. Odpowiednie kombinacje można znaleźć w literaturze Nas interesują tylko połączenia AF_INET + SOCK_STREAM + IPPROTO_TCP - co implikuje użycie protokołu TCPv4 + TCP AF_INET + SOCK_DGRAM + IPPROTO_UDP - co implikuje użycie protokołu TCPv4 + UDP AF_INET + SOCK_RAW + IPPROTO_RAW - co implikuje użycie protokołu IPv4 i gniazd surowych
10 SOCKADDR_IN saServer Deklaracja zmiennej SOCKADDR_IN SOCKADDR_IN jest zdefiniowane jako: typedef struct sockaddr_in SOCKADDR_IN; gdzie struct sockaddr_in zdefinowana w winsocket.h dana jest poniżej: struct sockaddr_in { short sin_family; // AF_INET - jezeli IPv4, ale ogólnie tak jak w socket u_short sin_port; // wybieramy na którym porcie chcemy się łączyć struct in_addr sin_addr; // struktura zdefiniowana poniżej char sin_zero[8]; // to jest tylko wypełnienie, żeby struktura miała taką samą długość jak struktutra sockaddr: struct sockaddr { u_short sa_family; char sa_data[14]; }; struct in_addr zdefiniowana jest poniżej struct in_addr { union { struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { u_short s_w1,s_w2; } S_un_w; u_long S_addr; } S_un; to jest unia (rezerwuje obszar w pamięci do zapisywania danych różnego typu w tym samym miejscu) czyli ogólnie zależnie od zastosowanego standardu możemy tam wpisać dane różnego typu i zawsze będzie działało. Obecnie stosuje się trzecią z możliwości tej unii czyli u_long - 32 bity na adres, po jednym bajcie między kropkami
protokół warstwy trzeciej, przypisujemy wartość AF_INET – IPv4 Struktura sockaddr_in • porty 0-1023 zarezerwowane są dla powszechnie znanych usług • porty 1024-49151 porty zwykłych użytkowników • porty 49152-65535 porty dynamiczne i prywatne użytkownicy powinni korzystać z portów z drugiego zakresu sockaddr_in short sin_family sin_port u_short sin_addr struct in_addr sin_zero[8] char struktura adresowa 32 bity to jest tylko wypełnienie, żeby struktura miała taką samą długość jak sockaddr Postać tej struktury zależy od systemu operacyjnego. Standard Posix.1g wymaga istnienia tylko trzech pierwszych pól. Różne systemy operacyjne wykorzystują jeszcze dodatkowe pola.
sin_addr struct in_addr { union { struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { u_short s_w1,s_w2; } S_un_w; u_long S_addr; } S_un; to jest unia (rezerwuje obszar w pamięci do zapisywania danych różnego typu w tym samym miejscu) czyli ogólnie zależnie od zastosowanego standardu możemy tam wpisać dane różnego typu i zawsze będzie działało. Obecnie stosuje się trzecią z możliwości tej unii czyli u_long - 32 bity na adres, po jednym bajcie między kropkami
11 saServer.sin_family = AF_INET; Wypełniamy strukturę saServer. Wybieramy protokół IPv4 12 saServer.sin_addr.s_addr = INADDR_ANY Pozwala serwerowi zaakceptować połączenie z klientem przez dowolny interfejs co ma znaczenie jeżeli stacja ma wiele interfejsów 13 saServer.sin_port = htons(nPort); do kolejnego pola rekordu wpisujemy port na którym serwer będzie nasłuchiwał przy czym dokonujemy zmiany kolejności bitów przy użyciu funkcji htons() interpretacja nazwy tej funkcji jest taka: h - host, to, n-network, s-short
15 nRet = bind(theSocket, //wpisujemy deskryptor gniazda otrzymany w wyniku wywołania funkcji socket() (LPSOCKADDR)&saServer, //przekazujemy adres struktury zawierającej gniazdową strukturę adresową sizeof(struct sockaddr)); //określa rozmiar gniazdowej struktury adresowej Funkcja bind przypisuje gniazdu lokalny adres protokołowy Adres protokołowy składa się z 32-bitowego adresu IPv4 oraz 16-bitowego numeru portu (dla IPv6 ma 128bitów) funkcja bind zwraca SOCKET_ERROR, WSAEADDRINUSE - gdy jakiś inny proces używa tego adresu i gniazda. Jeżeli drugi raz wywołamy w tym samym programie bind z tymi samymi parametrami otrzymamy WSAFAULT
16-18 if (nRet == SOCKET_ERROR) { PRINTERROR("bind()"); closesocket(theSocket); return; } Wyświetlamy błąd przy użyciu naszego makra 21 int nLen; Do tej zmiennej wpiszemy rozmiar struktury adresowej
24 nRet = gethostname(szBuf, sizeof(szBuf)); funkcja przekazuje do zmiennej szBuf nazwę stacji lokalnej szBuf - wksaźnik do tablicy znakowej do której zostanie wpisana nazwa lokalna komputera drugi argument - rozmiar tablicy funkcja zwraca 0 kiedy wszystko OK, -1 gdy wystąpił błąd 25-29 Korzystamy z makra aby wyświetlić ewentualny błąd i zamknąć gniazdo. 30 Wyświetlamy nazwę serwera
31 SOCKADDR_IN saClient; Tworzymy drugą strukturę adresową do przechowywania adresu klienta. 32 memset(szBuf, 0, sizeof(szBuf)); Zerujemy bufor danych. szBuf – wskaźnik bufora 0 – wartość, która zostanie wpisana w wybrany zakres sizeof(szBuf) – rozmiar pamięci, który ma zostać wypełniony zerami
33 nRet = recvfrom(theSocket, // Socket na którym będziemy odbierać dane szBuf, // Bufor do którego zapiszemy dane sizeof(szBuf), // Rozmiar bufora w bajtach 0, // Flagi (LPSOCKADDR)&saClient, // wskaźnik do struktury adresowej do której zapiszemy adreshosta z któego przyszły dane &nLen); // Wskaźnik do rozmiar bufora do przechowywania gniazdowej struktury adresowej 34 Wyświetlamy na serwerze to co przyszło od klienta
35 sendto(theSocket, // socket przez który będziemy // wysyłać dane szBuf, // wskaźnik do bufora, z którego wysyłamy dane strlen(szBuf), // długość bufora 0, // flagi (LPSOCKADDR)&saClient, // gniazdowa struktura adresowa do której dane wysyłamy nLen) // rozmiar adresowej strukturyadresowej do której wysyłamy dane 36 closesocket(theSocket); zamknięcie gniazda. Funkcja powoduje zwolnienie deskryptora gniazda. Wszystkie ewentualne wywołania odnoszące się do tego gniazda będasię kończyły błędem WSAENOTSOCK
Program 2 klient • #include <string.h> • #include <winsock.h> • #include <iostream> • #include <cstdlib> • using namespace std; • void DatagramClient(char *szServer, short nPort); • #define DLUGOSC_ADRESU 256 • int main(int argc, char **argv) • { • WORD wVersionRequested = MAKEWORD(1,1); • WSADATA wsaData; • int nRet; • short nPort; • char adres_hosta[DLUGOSC_ADRESU]; • cout << "Podaj adres hosta : "; • cin.getline(adres_hosta, DLUGOSC_ADRESU); • cout << "Podaj numer portu : "; • cin >> nPort; • nRet = WSAStartup(wVersionRequested, &wsaData); • if (wsaData.wVersion != wVersionRequested) • { • cout << " Wrong version” << endl; • return 0; • } • DatagramClient(adres_hosta, nPort); • WSACleanup(); • system("PAUSE"); • return 0; • };