Treść zadania
- Należy stworzyć projekt serwera oraz klienta
- Serwer nasłuchuje na adresie 127.0.0.1 na porcie 8080
- Klient pobiera od użytkownika najpierw jedną liczbę całkowitą
n
, a późniejn
razy pobiera kolejne dane do przetworzenia (np. najpierw użytkownik wpisuje 3, po czym podaje trzy dane wejściowe np. 1, 2, 4) - Klient tworzy z tych danych komunikat w postaci: "liczba1 liczba2 ... liczbaN" czyli dla przykładu powyżej "1 2 4"
- Klient podłącza się pod adres 127.0.0.1 na porcie 8080 i przesyła komunikat
- Serwer odbiera komunikat i interpretuje go jako tablicę liczb całkowitych
- Serwer znajduje maksimum w tej tablicy
- Serwer przygotowuje komunikat tekstowy w postaci "liczbaMaksimum" czyli dla przykładu powyżej "4"
- Serwer przesyła komunikat i kończy swoje działanie
- Klient odbiera komunikat i wypisuje wynik na konsolę
Przykład poprawnego działania
- Uruchom serwer
- Uruchom klienta
- Wpisz dane np. 3, a następnie 1 2 4
- Trwa wymiana komunikatów serwer <-> klient
- Odczytaj wynik w okienku klienta: 4
Praktyczne informacje
- Do stworzenia tekstowego komunikatu wykorzystujemy funkcję
sprintf()
np. tak:char buffer[1024];
memset(buffer, 0, 1024);
sprintf(buffer, "%d", zmienna);zmienna
pod adresbuffer
) - Analogicznie, do analizy komunikatów można wykorzystać funkcję
sscanf()
np. tak:char buffer[1024];
memset(buffer, 0, 1024);
// wypełnij buffer danymi, np. tak:
// recv(socket, buffer, sizeof(buffer), 0);
sscanf(buffer, "%d", &zmienna);zmienna
wartość całkowitą odczytaną z napisu wbuffer
) - Komunikat przesyłany przez klienta ma postać "liczba1 liczba2 ... liczbaN". Najlepiej będzie go przetworzyć przy użyciu funkcji
strtok()
np. tak:char *s = "1 2 4"; // przykładowe dane
char *delimiter = " "; // to co oddziela dane w napisie, tutaj znak spacji
char *token = strtok(s, delimiter);
while (token != NULL) {
// w token znajduje się aktualnie znalezione słowo
token = strtok(NULL, delimiter); // każde następne wywołanie strtok()
// musi mieć pierwszy argument równy NULL
}
Teoria
- W programowaniu przy użyciu gniazd (socket) istnieją dwa podmioty: serwer oraz klient
- Serwer nasłuchuje na pewnym porcie pod określonym adresem; innymi słowy, udostępnia możliwość komunikacji i jest to reprezentowane jako para ADRES:PORT np. localhost:8080 (localhost to specjalna nazwa sieciowa oznaczająca własny adres)
- Klient musi znać ADRES:PORT serwera, z którym chce się połączyć; nawiązuje połączenie i od tej chwili możliwa jest dwustronna wymiana komunikatów
- Komunikat to dowolny ciąg bajtów, może być to napis, może to być tablica liczb, itd.
- Protokołem nazywamy zestaw komunikatów i ich wzajemny porządek, które to informacje jeśli są znane (i poprawnie zaimplementowane) przez serwer i klienta, umożliwiają osiągnięcie pewnego wspólnego celu
- Przykładowo w protokole HTTP zdefiniowano komendę GET do pobierania nazwanych zasobów. Zarówno serwer HTTP "rozpoznaje" komunikat GET i wie co z nim zrobić, tak jak i każda przeglądarka www potrafi generować komendy GET by pobierać strony, obrazy, itd.
Projekt w CodeBlocks
- Projekt serwera: Attach:Client.zip
- Projekt klienta: Attach:Server.zip
- Wersja Linuksowa (serwer + klient): Attach:tcp.tar.gz
- Należy w programie dodać linię:
#include <winsock2.h>
- Należy kliknąć prawym przyciskiem myszy na nazwie projektu i wybrać "Build options". W oknie należy wskazać zakładkę "Linker settings" i tam dodać (przycisk "Add") bibliotekę o nazwie:
ws2_32
Kod serwera
- Przygotuj środowisko:
WSADATA wsadata;
WSAStartup(MAKEWORD(2, 2), &wsadata); - Stwórz gniazdo:
SOCKET tcpsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- Zdefiniuj adres:
SOCKADDR_IN address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("127.0.0.1");
address.sin_port = htons(8080); - Powiąż ze sobą gniazdo i adres:
bind(tcpsocket, (SOCKADDR*)(&address), sizeof(address));
- Oznacz gniazdo jako nasłuchujące:
listen(tcpsocket, 1);
- Zakceptuj połączenie:
tcpsocket = accept(tcpsocket, NULL, NULL);
- Komunikacja
- Zamknij gniazdo:
shutdown(tcpsocket, SD_BOTH);
closesocket(tcpsocket);
WSACleanup();
Kod klienta
- Przygotuj środowisko:
WSADATA wsadata;
WSAStartup(MAKEWORD(2, 2), &wsadata); - Stwórz gniazdo:
SOCKET tcpsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- Zdefiniuj adres:
SOCKADDR_IN address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("127.0.0.1");
address.sin_port = htons(8080); - Połącz się z wybranym adresem:
connect(tcpsocket, (SOCKADDR*)(&address), sizeof(address));
- Komunikacja
- Zamknij gniazdo:
shutdown(tcpsocket, SD_BOTH);
closesocket(tcpsocket);
WSACleanup();
Kod do komunikacji
- Wysłanie komunikatu:
send(tcpsocket, buffer, strlen(buffer), 0);
- Odebranie komunikatu:
char buffer[1024];
recv(tcpsocket, buffer, 1024, 0);