Podstawy komputerowych systemów sterowania Programowanie TCP/IP dr inż. Krzysztof Kołek Materiały wyłącznie dla potrzeb wykładu Podstawy komputerowych systemów sterowania IV rok RA wydział EAIiEB AGH. Inne wykorzystanie bez zgody autora zabronione.
Plan Koncepcja gniazd (socket) Model OSI IP, TCP i UDP Funkcje wywoływane dla klienta i serwera Przykładowy klient i serwer Biblioteka Winsock 2
Berkeley sockets (BSD sockets): Biblioteka API do komunikacji sieciowej Zgodna z POSIX (Portable Operating System Interface specyfikuje API w celu zapewnienia zgodności między OS). Otwarty standard Pierwotnie dostępna dla UNIX, aktualnie dla wielu OS Wersja dla MS Windows Winsock (Winsock nie był rozwijany w firmie Microsoft!!!) BSD sockets zapewnia standard binarny, ale również kompatybilność na poziomie kodu źródłowego (!!!???) Rodzaj wtyczki do sieci komputerowej, analogicznie do wtyczki od sieci elektrycznej Podstawowo dostępny jako biblioteka w języku C 3
Berkeley sockets (BSD sockets): Standaryzuje komunikację (standardy ważniejsze od technologii, bo dopiero one czynią technologię dostępną) Możliwość mieszania i łączenia komponentów od różnych dostawców 4
Model OSI (Open System Interconnect): Ogólne spojrzenie na systemy sieciowe Redukcja złożonego problemu transmisji sieciowej do komponentów, określając ich współpracę Powstał 1977 Dane przepływają w dół ; warstwy wymagają od niższych warstw wykonania zadań Niższe warstwy są przezroczyste dla wyższych (z wyjątkiem błędów, które propagują w górę ) 5
Warstwa aplikacji: Aplikacje komunikują się za pomocą protokołu. Protokół zbiór zasad zarządzający komunikacją. Protokoły definiowane samodzielnie lub standardy Warstwa aplikacji wykonuje interakcję z użytkownikiem Wywołuje usługi warstwy prezentacji, lub omija warstwę prezentacji i wywołuje usługi warstwy sesji Przykładem program SCADA prezentujący dane ze zdalnych źródeł 6
Warstwa prezentacji: Formatuje informacje uzyskane z warstwy aplikacji To np. kodowanie, dekodowanie, kompresja, konwersja ASCII/binary Wywołuje usługi warstwy sesji w celu wysłania/odbioru danych Warstwa nie jest obowiązkowa Nie ma standardowego interfejsu z udostępnionymi usługami SCADA np. szyfrowanie/rozkodowanie danych w celu zapewnienia poufności 7
Warstwa sesji: Tworzy, zarządza oraz zamyka połączenie wysokiego poziomu (połączenia niskiego poziomu występują w warstwie transportowej) Rodzaj wirtualnego połączenia, występującego np. również przy braku łączności sieciowej. Np. transfer dużych plików w warunkach zaniku łączności Warstwa nie jest obowiązkowa aplikacja może nie wymagać takich usług SCADA nawiązanie połączenia, cykliczna wymiana danych ze stałym okresem oraz zakończenie połączenia ze zdalnym serwerem 8
Warstwa transportowa: Zawiera usługi sieciowe związane z transferem punkt-punkt Rodzaj wirtualnego połączenia, występującego np. również przy braku łączności sieciowej. Np. transfer dużych plików w warunkach zaniku łączności Wywołuje funkcje związane z odbiorem/transferem oraz adresowaniem ramek danych Odpowiada za integralność danych; może potwierdzić otrzymanie danych lub zażądać retransmisji brakującego pakietu SCADA jednorazowy cykl wymiany danych, powtarzający się cyklicznie ze znanym okresem 9
Warstwa sieciowa: Adresuje pakiety umożliwiając ich transfer przez routery i bridges Dla danych przekraczających rozmiar pakietu dzieli ramki na pakiety, scala pakiety; nadaje numery fragmentom Dodaje do pakietów sumy kontrolne SCADA ta warstwa przezroczysta dla pakietów SCADA 10
Warstwa łącza danych: Zwana warstwą sterowników sieciowych Zarządza sprzętem, umożliwiając jego współdzielenie jeżeli kilka warstw sieciowych pracuje jednocześnie Realizuje dokładanie/usuwanie adresów MAC z pakietów danych SCADA ta warstwa przezroczysta dla pakietów SCADA 11
Warstwa fizyczna: Określa parametry techniczne związane z przesyłaniem danych przez medium (przewód, światłowód, radio) Wysyła/odbiera dane bit po bicie SCADA ta warstwa przezroczysta dla pakietów SCADA 12
Warstwy transportowa, sieciowa i łącza danych dokładają kolejne nagłówki do danych otrzymanych z wyższych warstw Wireshark 13
14
15
TCP/IP (Transmissin Control Protocol / Internet Protocol) Zapewnia łączność sieciową między dowolnymi systemami komputerowymi, poprzez różnorodne media sieciowe TCP/IP podstawowe protokoły Internetu Zapewnia łączność dla różnych charakterystyk sieci: różnego pasma, opóźnień, utraty pakietów, zmiany kolejności pakietów, zmiennego rozmiaru pakietów, uszkodzeń fragmentów sieci Biblioteka BSD Sockets ukrywa szczegóły implementacyjne TCP, UDP i IP 16
UDP (User Datagram Protocol) Usługa bezpołączeniowa wysyła datagramy (datagram jednostka transmitowanych danych) Nie jest usługą niezawodną nie ma gwarancji dostarczenia pakietu oraz gwarancji zachowania kolejności transmitowanych pakietów Otrzymanie datagramu nie powoduje wysłania potwierdzenia odbioru Usługa prosta, a dzięki temu efektywna Aplikacja może w oparciu o datagramy wprowadzić usługi gwarantujące niezawodność Preferowane zastosowania: podczas przesyłania krótkich danych, gdy koszty tworzenia i zamykania kanału są większe od kosztów transmisji; Np. w systemach SCADA: pytamy o daną czasu rzeczywistego; jak przyjdzie to OK, jak się nie pojawi ponawiamy pytanie; jeżeli przyjdą zduplikowane dane to można je odfiltrować 17
UDP Niektóre usługi wymagają zawodnej transmisji np. ping jeżeli nastąpi automatyczna retransmisja pakietów nie da się robić statystyk lub strumieniowa transmisja audio lepiej odtworzyć posiadane dane niż długo czekać na retransmisję pakietu Bezpołączeniowa możliwy multicasting i broadcasting Multicasting - jeden pakiet wysłany do wielu odbiorców, zakres adresów 224.0.0.0 do 239.255.255.255 przypisany jest do multicastu Broadcasting - tryb rozgłoszeniowy - jeden pakiet do wszystkich w danej sieci; adres wyznaczany na podstawie maski sieci (zera w masce sieci zamieniane na jedynki oraz suma logiczna z adresem podsieci) 18
TCP Tworzy połączenie; połączenie jest wirtualne wydaje się iż są połączone fizycznie Analogia z rozmową telefoniczną Niezawodna transmisja: automatyczne potwierdzanie otrzymanych danych, retransmisja danych utraconych, zachowanie kolejności danych, unikanie duplikacji danych Powyższe usługi mają swój narzut na ruch sieciowy TCP NIE GWARANTUJE granic pakietów. Wysyła taką ilość danych jaką potrzebuje. Nadawca może wysłać dane jedną operacją, a odbiorca będzie potrzebował kilku operacji odczytu w celu skompletowania danych 19
!!! Używaj TCP, chyba że istnieje powód aby tego nie robić!!! Zwykle koszt dodatkowych usług TCP jest pomijalny, a programy wykorzystujące TCP są prostsze Powody zastosowania UDP: Użycie multicast lub broadcast Przesyłanie danych czasu rzeczywistego wrażliwych na opóźnienia (strumienie audio i video) Krótkie transmisje z wbudowanym domyślnie potwierdzeniem np. zapytanie do bazy danych 20
IP Realizuje adresowanie pakietów, routowanie, fragmentację i defragmentację Szczegóły ukryte przed użytkownikiem UDP oraz TCP 21
Klient/serwer Kanał komunikacyjny ma dwa końce: klient i serwer Serwer rozpoczyna oczekiwanie na komunikację, a klient wysyła pierwszy pakiet Klient i serwer posiadają własne gniazda (sockets), oba tego samego typu UDP lub TCP Gniazdo definiuje: Protokół Adres IP Numer portu - liczba 16 bitowa umożliwiający jego identyfikację Kanał zdefiniowany przez: Protokół Adres IP klienta i adres IP serwera Numer portów: klienta i serwera Kanał powstaje po stowarzyszeniu gniazd klienta i serwera 22
Klient/serwer Serwer Klient Protokół komunikacyjny TCP lub UDP Serwer poznaje adres komputera klienta w momencie nawiązywania połączenia Klient musi znać adres komputera serwera Serwer oczekuje na połączenia z klientem na określonym porcie Klient nawiązuje połączenie z serwerem na porcie używanym przez serwer do nasłuchiwania 23
Klient/serwer Zarówno klient jak i serwer w trakcie komunikacji wykonują pięć kroków: Otwarcie gniazda Określenie parametrów gniazda Stowarzyszenie z innym gniazdem Transmisja danych między gniazdami Zamknięcie gniazda Występuje pewna analogia do operacji na plikach, np. w obu przypadkach otwarcie zwraca uchwyt (handle) 24
Otwarcie gniazda #include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol); Zwraca (-1) gdy błąd, a w przeciwnym przypadku deskryptor gniazda domain domena komunikacji; kilka wartości, między innymi PF_INET dla IPv4 i PF_INET6 dla IPv6 type - kilka wartości, między innymi SOCK_DGRAM dla komunikacji datagramowej i SOCK_STREAM dla komunikacji z kanałem komunikacyjnym; wartość SOCK_RAW dla protokołów innych niż TCP i UDP protocol protokół, kilka wartości; między innymi IPPROTO_TCP oraz IPPROTO_UDP 25
Określenie parametrów gniazda (Name the Socket) Określa dla gniazda adres, port i protokół Operacja musi być zrealizowana dla serwera, a dla klienta może być wykonana (choć jest to niewskazane) Dwie struktury: struct sockaddr { } sa_family_t sa_family; char sa_data[14]; To ogólna forma struktury danych określających parametry gniazda. Praktycznie niewykorzystywana (używana tylko do rzutowania adresu w funkcji bind()). Praktycznie używana struktura sockaddr_in. Formalnie sockaddr oraz sockaddr_in nie są zadeklarowane jako union ale tak są wykorzystywane 26
Określenie parametrów gniazda struct sockaddr_in { }; sa_family_t sin_family; /* address family: PF_INET */ in_port_t sin_port; /* port in network byte order */ struct in_addr sin_addr; /* internet address */ char sin_zero[8]; /* unused */ sin_family dla TCP/IP PF_INET (dla IPv6 PF_INET6, ale wówczas struktura sockaddr_in6) sin_port 16-bitowy numer portu. Każdy serwis ma swój numer (np. 80 dla http). Numery 0-1024 są zarezerwowane. Do własnego użytku numery powyżej 1024, choć do ok. 5000 używane przez wiele popularnych serwisów sin_addr dla funkcji bind() zawsze lokalny adres IP; wygodnie zastosować INADDR_ANY, co spowoduje automatyczne nadanie adresu sin_zero dla wyrównania długości struktury do długości sockaddr 27
Określenie parametrów gniazda #include <sys/types.h> #include <sys/socket.h> int bind(int s, const struct sockaddr *addr, socklen_t addrlen); Zwraca 0 w przypadku sukcesu lub SOCKET_ERROR (-1) w przypadku błędu s uchwyt do gniazda addr wskaźnik na strukturę z parametrami addrlen rozmiar struktury, sizeoff( sockarrd_in ) Nie zaleca się wykonywania bind() dla gniazda klienta. Stos TCP/IP określi parametry (szczególnie unikalny numer portu) podczas, dla TCP wykonywania funkcji connect() lub,dla UDP, wykonanie connect() lub sendto() 28
Stowarzyszenie z innym gniazdem Dwa stowarzyszone gniazda tworzą unikalne połączenie Wykonywane inaczej dla UDP i TCP oraz inaczej dla klienta i serwera UDP Dla serwera UDP: nie ma potrzeby asocjacji stowarzyszenie wykonywane podczas odczytu danych, po wykonaniu funkcji recv(), recvfrom() lub select() Dla klienta dwie metody: Wywołanie sendto(). Funkcja jako parametr pobiera strukturę sockaddr, można więc za każdym razem zmienić adres odbiorcy. Metoda rekomendowana Wywołując connect(), a następnie send(). Po uprzednim rozłączeniu (wykonując połączenie z adresem INADDR_ANY) powtórne wykonanie connect() zmieni adres odbiorcy 29
Stowarzyszenie z innym gniazdem TCP: Serwer wykonuje dwie funkcje #include <sys/types.h> #include <sys/socket.h> int listen(int sockfd, int backlog); Funkcja przygotowuje do akceptacji połączenia. Zwraca 0 gdy sukces lub SOCKET_ERROR (-1) gdy porażka sockfd uchwyt do gniazda; po wykonaniu bind() ale przez stowarzyszeniem backlog liczba przychodzących żądań połączenia, czekających w kolejce, podczas przetwarzania połączeń już zaakceptowanych. To maksymalny rozmiar kolejki oczekujących połączeń 30
Stowarzyszenie z innym gniazdem TCP: #include <sys/types.h> #include <sys/socket.h> int accept(int s, struct sockaddr *addr, socklen_t *addrlen); Funkcja akceptuje połączenie. Zwraca uchwyt do gniazda nowo utworzonego połączenia gdy sukces lub SOCKET_ERROR (-1) gdy porażka. Funkcja tworzy nowe gniazdo niezbędne zamknięcie więcej niż jednego gniazda. s uchwyt do gniazda; po wykonaniu listen(). Po wykonaniu funkcji gniazdo s ciągle może nasłuchiwać nadchodzących połączeń sockaddr adres struktury, do której będą wpisane parametry tworzonego gniazda. Jeżeli parametr jest równy NULL żadna struktura nie jest wypełniana parametrami addrlen wskaźnik na uzupełniany przez funkcję rozmiar wypełnionej struktury 31
Stowarzyszenie z innym gniazdem TCP: Klient wykonuje funkcje #include <sys/types.h> #include <sys/socket.h> int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); Funkcja inicjalizuje połączenie. Zwraca 0 gdy sukces lub SOCKET_ERROR (-1) gdy porażka sockfd uchwyt do lokalnego gniazda addr wskaźnik na strukturę z parametrami; tutaj sin_port i sin_addr określają zdalne gniazdo addrlen rozmiar struktury, sizeoff( sockarrd_in ) Funkcja connect() określa automatycznie parametry gniazda sockfd, nie trzeba więc dla niego wykonywać bind() (określa port i adres) 32
Transmisja #include <sys/types.h> #include <sys/socket.h> int send(int sockfd, const void *buf, int len, int flags); Zwraca -1 w przypadku błędu lub liczbę wysłanych bajtów sockfd uchwyt do stowarzyszonego gniazda *buf wskaźnik do bufora z danymi len liczba danych do wysłania; dla UDP nie może przekroczyć maksymalnej długości datagramu; dla TCP nie ma gwarancji wysłania wszystkich danych w pojedynczym pakiecie aplikacja musi śledzić czy nie trzeba ponownie wywołać funkcję w celu wysłania pozostałych danych flags dodatkowe flagi (np. wysłanie danych priorytetowych); zwykle wartość zero Dla UDP send() można wywołać tylko po uprzednim stowarzyszeniu gniazda funkcją connect() 33
Transmisja #include <sys/types.h> #include <sys/socket.h> int sendto(int sockfd, const void *buf, int len, int flags, const struct sockaddr *dest_addr, int addrlen); Zwraca -1 w przypadku błędu lub liczbę wysłanych bajtów sockfd, buf, len i flags identycznie jak dla send() dest_addr wskaźnik do uchwytu gniazda zawierającego numer portu i IP komputera przeznaczenia addrlen długość struktury wskazywanej przez dest_addr Stosowana zwykle dla UDP; w przypadku TCP send() i sendto() działają identycznie 34
Transmisja #include <sys/types.h> #include <sys/socket.h> int recv(int sockfd, void *buf, size_t len, int flags); int recvfrom(int sockfd, void *buf, int len, int flags, struct sockaddr *src_addr, int addrlen); Zwraca -1 w przypadku błędu lub liczbę odebranych bajtów Podobne znaczenie parametrów jak dla funkcji wysyłających len maksymalna liczba odczytanych danych. Funkcja kończy działanie gdy system sieciowy zwróci jakąkolwiek liczbę danych Flaga MSG_OOB umożliwia w pierwszej kolejności odczyt danych o podwyższonym priorytecie Flaga MSG_PEEK umożliwia sprawdzenie ile danych jest w buforze odbiornika, a nawet wgląd w te dane bez usuwania ich z bufora 35
Zamknięcie gniazda int close(int sockfd); Zamyka gniazdo; zwraca zero w przypadku sukcesu 36
TCP 37
UDP 38
Tryby pracy Blocking: funkcja kończy działanie po zakończeniu operacji, z błędem lub nie. To tryb domyślny Nonblocking funkcja kończy działanie natychmiast, czasami z błędem, a czasami nie. Ale błąd nie musi oznaczać niepowodzenia może oznaczać, iż operacja nie zdążyła się zakończyć. Niezbędne przepytywanie (pooling) w celu określenia zakończenia operacji. Przejście do trybu nonblocking wymaga wywołania funkcji ioctlsocket() z parametrem FIONBIO Asynchronous funkcja kończy działanie natychmiast, ale dodatkowo wysyłany jest sygnał gdy operacja się zakończy Najprostszy tryb do pisania programów to blocking, szczególnie w środowisku wielozadaniowym. Zalecane wykonanie procedur timeout Zalecana obsługa wielowątkowa możliwa obsługa wielu gniazd jednocześnie 39
Przykład serwera TCP #include <fcntl.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char* argv[]) { int sockfd, sockfd2; int addrlen; struct sockaddr_in svr_name, cli_name; int status; char inbuf[4096]; /* Create the socket */ sockfd = socket(pf_inet, SOCK_STREAM, IPPROTO_TCP ); if (sockfd == (-1) ) { printf("socket() error\n"); exit(1); } if (argc < 2) { printf( "Usage: %s port_no\n", argv[0]); exit(2); } /* Server parameters */ svr_name.sin_family = PF_INET; svr_name.sin_addr.s_addr = INADDR_ANY; svr_name.sin_port = htons(atoi(argv[1])); status = bind(sockfd, (struct sockaddr*)&svr_name, sizeof(svr_name)); if (status == -1) { printf("bind() error\n"); exit(3); } status = listen(sockfd, 5); if (status == -1) { printf("listen() error\n"); exit(4); } for(;;) { printf("wait for a connection...\n" ); addrlen = sizeof(cli_name); sockfd2 = accept(sockfd, (struct sockaddr*)&cli_name, &addrlen); if (sockfd2 == -1) { printf("accept() error\n"); exit(1); } status = recv(sockfd2, inbuf, sizeof(inbuf), 0); if (status == -1) { printf("recv() error\n"); exit(5); } printf( "Received: %s\n", inbuf ); status = send(sockfd2, "Received!\n", sizeof("received!\n"), 0); if (status == -1) { printf("send() error\n"); exit(5); } close(sockfd2); if( strcmp( inbuf, "!shutdown" ) == 0 ) break; } close(sockfd); return 0; } 40
Przykład klienta TCP #include <fcntl.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char* argv[]) { int sockfd; int count; struct sockaddr_in serv_socket; char inbuf[256]; int status; if (argc < 4) { printf("usage: %s IP port_no message\n", argv[0]); exit(1); } sockfd = socket(pf_inet, SOCK_STREAM, IPPROTO_TCP ); if (sockfd == -1) { printf("socket() error\n"); exit(2); } serv_socket.sin_family = PF_INET; inet_aton(argv[1], &serv_socket.sin_addr); serv_socket.sin_port = htons(atoi(argv[2])); status = connect(sockfd, (struct sockaddr*)&serv_socket, sizeof(serv_socket)); if (status == -1) { printf("connect() error\n"); exit(3); } status = send(sockfd, argv[3], sizeof("received!\n"), 0); if (status == -1) { printf("send() error\n"); exit(4); } status = recv(sockfd, inbuf, sizeof(inbuf), 0); if (status == -1) { printf("recv() error\n"); exit(5); } printf( "Received: %s\n", inbuf ); close(sockfd); return 0; } 41
Winsock Odpowiednik BSD Sockets dla systemów MS-Windows Zaimplementowana jako biblioteka DLL Wprowadzono nowe funkcje Linkowanie z biblioteką Ws2_32.lib 42
Winsock serwer: // WIN32_LEAN_AND_MEAN macro prevents the Winsock.h // from being included by the Windows.h header #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <winsock2.h> #include <ws2tcpip.h> #include <stdlib.h> #include <stdio.h> // Need to link with Ws2_32.lib #pragma comment (lib, "Ws2_32.lib") #define DEFAULT_BUFLEN (512) 43
int main(int argc, char* argv[]) { WSADATA wsadata; int iresult; SOCKET ListenSocket = INVALID_SOCKET; SOCKET ClientSocket = INVALID_SOCKET; struct addrinfo *result = NULL; struct addrinfo hints; int isendresult; char recvbuf[default_buflen]; int recvbuflen = DEFAULT_BUFLEN; // Validate the parameters if (argc < 2) { printf("usage: %s port_no\n", argv[0]); return 1; } 44
// Initialize Winsock iresult = WSAStartup(MAKEWORD(2,2), &wsadata); if (iresult!= 0) { printf("wsastartup failed with error: %d\n", iresult); return 1; } ZeroMemory(&hints, sizeof(hints)); hints.ai_family = PF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE; // Resolve the server address and port iresult = getaddrinfo(null, argv[1], &hints, &result); if ( iresult!= 0 ) { printf("getaddrinfo failed with error: %d\n", iresult); WSACleanup(); return 1; } 45
// Create a SOCKET for connecting to server ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); if (ListenSocket == INVALID_SOCKET) { printf("socket failed with error: %ld\n", WSAGetLastError()); freeaddrinfo(result); WSACleanup(); return 1; } // Setup the TCP listening socket iresult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen); if (iresult == SOCKET_ERROR) { printf("bind failed with error: %d\n", WSAGetLastError()); freeaddrinfo(result); closesocket(listensocket); WSACleanup(); return 1; } 46
freeaddrinfo(result); iresult = listen(listensocket, SOMAXCONN); if (iresult == SOCKET_ERROR) { printf("listen failed with error: %d\n", WSAGetLastError()); closesocket(listensocket); WSACleanup(); return 1; } // Accept a client socket do { ClientSocket = accept(listensocket, NULL, NULL); if (ClientSocket == INVALID_SOCKET) { printf("accept failed with error: %d\n", WSAGetLastError()); closesocket(listensocket); WSACleanup(); return 1; } 47
iresult = recv(clientsocket, recvbuf, recvbuflen, 0); if (iresult > 0) { printf("bytes received: %d >> %s\n", iresult, recvbuf); // Echo the buffer back to the sender isendresult = send( ClientSocket, recvbuf, iresult, 0 ); if (isendresult == SOCKET_ERROR) { printf("send failed with error: %d\n", WSAGetLastError()); closesocket(clientsocket); WSACleanup(); return 1; } printf("bytes sent: %d\n", isendresult); } else if (iresult == 0) printf("connection closing...\n"); else { printf("recv failed with error: %d\n", WSAGetLastError()); closesocket(clientsocket); WSACleanup(); return 1; } 48
closesocket(clientsocket); if( strcmp( recvbuf, "!shutdown" ) == 0 ) break; } while( 1 ); closesocket(listensocket); WSACleanup(); } return 0; 49
Winsock klient: #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <winsock2.h> #include <ws2tcpip.h> #include <stdlib.h> #include <stdio.h> // Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib #pragma comment (lib, "Ws2_32.lib") #pragma comment (lib, "Mswsock.lib") #pragma comment (lib, "AdvApi32.lib") #define DEFAULT_BUFLEN 512 50
Winsock klient: int main(int argc, char* argv[]) { WSADATA wsadata; SOCKET ConnectSocket = INVALID_SOCKET; struct addrinfo *result = NULL, *ptr = NULL, hints; char sendbuf[default_buflen]; char recvbuf[default_buflen]; int iresult; int recvbuflen = DEFAULT_BUFLEN; // Validate the parameters if (argc < 4) { printf("usage: %s IP port_no message\n", argv[0]); return 1; } strcpy_s( sendbuf, strlen(argv[3])+1, argv[3] ); 51
Winsock klient: // Initialize Winsock iresult = WSAStartup(MAKEWORD(2,2), &wsadata); if (iresult!= 0) { printf("wsastartup failed with error: %d\n", iresult); return 1; } ZeroMemory( &hints, sizeof(hints) ); hints.ai_family = PF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; // Resolve the server address and port iresult = getaddrinfo(argv[1], argv[2], &hints, &result); if ( iresult!= 0 ) { printf("getaddrinfo failed with error: %d\n", iresult); WSACleanup(); return 1; } 52
Winsock klient: // Attempt to connect to an address for(ptr=result; ptr!= NULL ;ptr=ptr->ai_next) { // Create a SOCKET for connecting to server ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); if (ConnectSocket == INVALID_SOCKET) { printf("socket failed with error: %ld\n", WSAGetLastError()); WSACleanup(); return 1; } // Connect to server. iresult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen); if (iresult == SOCKET_ERROR) { closesocket(connectsocket); ConnectSocket = INVALID_SOCKET; continue; } break; } 53
Winsock klient: freeaddrinfo(result); if (ConnectSocket == INVALID_SOCKET) { printf("unable to connect to server!\n"); WSACleanup(); return 1; } // Send an initial buffer iresult = send( ConnectSocket, sendbuf, (int)strlen(sendbuf)+1, 0 ); if (iresult == SOCKET_ERROR) { printf("send failed with error: %d\n", WSAGetLastError()); closesocket(connectsocket); WSACleanup(); return 1; } printf("bytes Sent: %ld\n", iresult); 54
Winsock klient: iresult = recv(connectsocket, recvbuf, recvbuflen, 0); if ( iresult > 0 ) printf("bytes received: %d >> %s\n", iresult, recvbuf); else if ( iresult == 0 ) printf("connection closed\n"); else printf("recv failed with error: %d\n", WSAGetLastError()); // cleanup iresult = shutdown(connectsocket, SD_BOTH ); if (iresult == SOCKET_ERROR) { printf("shutdown failed with error: %d\n", WSAGetLastError()); closesocket(connectsocket); WSACleanup(); return 1; } closesocket(connectsocket); WSACleanup(); return 0; } 55
MATLAB Obiekt tcpip: obj = tcpip('rhost') obj = tcpip('rhost',rport) obj = tcpip(...,'propertyname',propertyvalue,...) obj = tcpip('localhost', 30000, 'NetworkRole', 'client') NetworkRole : wartości client lub server Przykład: echotcpip('on',4012) t = tcpip('localhost',4012); % get(t) zwraca właściwości fopen(t) fwrite(t,65:74) A = fread(t, 10); fclose(t) echotcpip('off') Uwaga na format ramek. Np. łańcuchy znakowe niezgodne z językiem C 56
MATLAB Serwer: t=tcpip('0.0.0.0', 30000, 'NetworkRole', 'server'); % 0.0.0.0 akceptuje połączenia z dowolnego klienta % Podanie IP limituje akceptację połączenia fopen(t); data=fread(t, t.bytesavailable, 'double'); fclose(t) Klient: data=sin(0:0.01:2*pi); t=tcpip('localhost', 30000, 'NetworkRole', 'client'); fopen(t) fwrite(t, data, 'double') fclose(t) 57
MATLAB przybornik TCP/UDP/IP Toolbox 2.0.6 sockcon=pnet('tcpsocket',port) Tworzy gniazdo socket pracujące na porcie port. W przypadku błędu zwraca 1, w innych przypadkach zwraca identyfikator gniazda. Odpowiada funkcjom socket() oraz bind(). con=pnet('tcpconnect','hostname',port) Tworzy połączenie z serwerem hostname na porcie port. W przypadku błędu zwraca 1, w innych przypadkach zwraca identyfikator gniazda. Funkcja pracuje w trybie non-blocking, powinna być uruchamiana po uruchomieniu serwera. Odpowiada funkcji connect(). con=pnet(sockcon,'tcplisten', ['noblock'] ) W przypadku gdy zdalny komputer żąda połączenia z serwerem funkcja zwraca uchwyt con do tego połączenia. W przypadku błędu zwraca 1, w innych przypadkach zwraca identyfikator gniazda. Funkcja pracuje w trybie non-blocking, powinna być uruchamiana po uruchomieniu serwera. W przypadku braku parametru noblock funkcja blokuje się do czasu uzyskania połączenia lub na okres określony przez funkcję setreadtimeout. Odpowiada funkcji listen(). 58
MATLAB przybornik TCP/UDP/IP Toolbox 2.0.6 str=pnet(con,'readline' [,limitsize] [,'view'] [,'noblock']) Funkcja odczytuje i zwraca jako str łańcuch znakowy do napotkania w strumieniu wejściowym znaku końca linii (newline - \n). Znak końca linii nie jest dodawany do zwracanego łańcucha. Jeżeli łańcuch jest dłuższy od limitsize to jest skracany to zadanej długości (domyślna wartość tego parametru to 65536). Opcja view odczytuje znaki z bufora ale ich nie usuwa z bufora odczytu. W przypadku braku parametru noblock funkcja blokuje się do czasu odczytu znaku końca linii lub na okres określony przez funkcję setreadtimeout. Odpowiada funkcji recv() ale dotyczy wyłącznie odczytu łańcuchów znakowych. pnet(con,'printf', 'format',...) Wysyła sformatowany łańcuch po łączu określonym gniazdem con. Funkcja blokuje się do czasu wysłania łańcucha lub na okres określony przez funkcję setwritetimeout. Odpowiada funkcji send() ale dotyczy wyłącznie wysyłania łańcuchów znakowych. [ip,port]=pnet(con,'gethost') Funkcja zwraca adres IP (w postaci czteroelementowej tablicy) oraz port związany z gniazdem con. 59
MATLAB przybornik TCP/UDP/IP Toolbox 2.0.6 stat=pnet(con,'setreadtimeout',sec) Funkcja ustawia dla gniazda con maksymalny czas oczekiwania dla funkcji read oraz listen. Czas oczekiwania podawany jest jako liczba zmiennopozycyjna w sekundach. Podanie czasu równego 0 oznacza wykonywanie funkcji w trybie non-blocking. stat=pnet(con,'setwritetimeout',sec) Funkcja ustawia dla gniazda con maksymalny czas oczekiwania dla funkcji printf oraz write. Czas oczekiwania podawany jest jako liczba zmiennopozycyjna w sekundach. Podanie czasu równego 0 oznacza wykonywanie funkcji w trybie non-blocking. pnet(con,'close') Zamyka łączność w kanale określonym przez gniazdo con. Powinna być wykonana po zakończeniu transmisji nawet gdy wcześniej transmisję zakończył zdalny komputer. Odpowiada funkcji close(). pnet('closeall') Zamyka wszystkie gniazda w aktualnej sesji MATLAB-a. Jest to bezpieczny sposób zakończenia pracy. 60
MATLAB przybornik TCP/UDP/IP Toolbox 2.0.6 elements=pnet(con,'write', data [,swapping]) data=pnet(con,'read' [,size] [,datatype] [,swapping] [,'view'] [,'noblock']) ret=pnet(con,'readtofile','filename'[,bytes][,'view'][,'noblock'][,'append']) ret=pnet(con,'writefromfile','filename'[[,start],len]) stat=pnet(con,'status') Opis dostępny jest po wykonaniu komendy help pnet w MATLAB Command Window 61
MATLAB przybornik TCP/UDP/IP Toolbox 2.0.6 Odczyt strony HTTP function ret = getwebpage( host, page, port ) con=pnet('tcpconnect',host,port); if con==-1, error( sprintf('brak łączności z %s', host ) ); end pnet(con,'setwritetimeout',1); pnet(con,'setreadtimeout',3); % Rządanie odczytu strony - polecenie GET protokołu HTTP pnet(con,'printf','get %s HTTP/1.0\n', page); pnet(con,'printf','host: %s\n', host); pnet(con,'printf','\n'); ret = pnet(con,'read',1000*1000); % Read data pnet(con,'close'); Przykładowe wywołanie to: ret = getwebpage( 'www.agh.edu.pl', '/', 80 ) 62
Kontrolka MSWINSCK.OCX Instalacja: 1. Załaduj MSWINSCK.OCX 2. Skopiuj do C:\Windows\System32 (32-bity) lub C:\Windows\SysWOW64 (64 bity) 3. Zarejestruj komendą a uprawnieniami administratora: regsvr32 c:\windows\syswow64\mswinsck.ocx (istotne podanie pełnej ścieżki) 63
Kontrolka MSWINSCK.OCX - VBA VBA (Alt+F11) Tools/References/Browse pokazać na mswinsck.ocx Private mywinsock As Object Sub Test() Set mywinsock = New MSWinsockLib.Winsock MsgBox mywinsock.localip End Sub 64
Kontrolka SocketWrench 8.0 Rejestracja jak poprzednio Bogaty zestaw właściwości, metod i zdarzeń (patrz dokumentacja >200str). function server_tcpip( port ) hfig = figure('visible','off'); % Stworzyć obiekt figure aby umieścić na nim kontrolkę % Utworzenie kontrolki oraz przypisanie funkcji obsługi zdarzeń hax=actxcontrol('sockettools.socketwrench.6', [0 0 10 10], hfig,... {'OnAccept' 'OnAcceptCallback';... 'OnDisconnect' 'OnDisconnectCallback'} ); % Ustawienie kluczowych właściwości hax.blocking = 0; %!!!! BRAK BLOKARY WYWOŁANIA METODY Listen hax.protocol = 6; % swprotocoltcp hax.localport = port; % Aktywacja nasłuchiwania nadchodzących połączeń na zadanym porcie Err = hax.listen; if Err ~= 0, disp( [ 'Unable to listen for connection: ' num2str(err) ] ); end return 65
Kontrolka SocketWrench 8.0 function OnAcceptCallback(varargin) % Utworzyć nowe gniazdo hax=actxcontrol('sockettools.socketwrench.6',[0 0 10 10], gcf,... {'OnRead' 'OnReadCallback';... 'OnDisconnect' 'OnDisconnectCallback'} ); hax.blocking = 0; hax.protocol = 6; % swprotocoltcp Err = hax.accept( varargin{1}.handle ); % Akceptuj połączenie if Err ~= 0, MsgBox( [ 'Unable to accept for connection: ' num2str(err) ] ) end 66
Kontrolka SocketWrench 8.0 function OnDisconnectCallback( varargin ) varargin{1}.disconnect; delete(varargin{1}) function OnReadCallback( varargin ) strbuff = char('x'*ones(1,1024)); [Len, strbuff, host, port ] = varargin{1}.read(strbuff, length(strbuff )); if Len > 0 disp( [ 'Read: ' strbuff ] ) end 67
Kontrolka SocketWrench 8.0 function send_tcpip( host, port, text ) hfig = figure('visible','off'); hax=actxcontrol('sockettools.socketwrench.6'); hax.blocking = 1; % tu lepszy blokking hax.protocol = 6; % swprotocoltcp Err = hax.connect( host, port ); if Err ~= 0, MsgBox 'Unable to connect to remote host'; end Err = hax.write(text, length(text)); if Err == -1, MsgBox 'Unable to send data to server' ; end hax.disconnect; delete( hax ); delete( hfig ); 68
Kontrolka SocketWrench 8.0 VBA Private mywinsock As SocketWrenchCtl.SocketWrench Sub Test() Set mywinsock = New SocketWrenchCtl.SocketWrench MsgBox mywinsock.version End Sub 69
Wnioski: Biblioteki TCP/IP dostępne dla większości systemów operacyjnych Znaczna część ruchu w Internecie bazuje na TCP/IP Komunikacja sieciowa często wykorzystywana w systemach sterowania Oprogramowanie komunikacji TCP/IP jest proste 70
Literatura Douglas E. Comer: Sieci komputerowe TCP/IP, WNT, 1998. Andrew S. Tanenbaum: Sieci komputerowe, Helion. BSD Sockets Interface Programmer s Guide, dokument dostępny w Internecie http://sockets.com/winsock.htm, strona na temat WinSock Quinn Bob, Shute Dave: Windows Sockets Network Programming. Stevens, Fenner, Rudoff: Unix Network Programming volume 1: The Sockets Networking API, 3rd Edition, Addison-Wesley, 2003. Norman Matloff: Overview of Computer Networks, http://heather.cs.ucdavis.edu/~matloff/networks/intro/netintro.pdf, INTERNET!!! 71