Aplikacje wielowątkowe
Zadanie 1 - Komunikator tekstowy z wieloma klientami
Zaimplementuj serwer programu do komunikacji tekstowej (czat) obsługujący wielu klientów równocześnie. Każda aplikacja kliencka po połączeniu z serwerem ma możliwość wysyłania komunikatów, które następnie są przez serwer przekazywane do wszystkich pozostałych połączonych aplikacji klienckich. Przesyłanie komunikatów tekstowych pomiędzy serwerem a aplikacjami klienckimi odbywa się asynchronicznie z wykorzystaniem wielowątkowości.
Źródła:
- szablon aplikacji serwerowej serwer.cpp
- przykładowa aplikacja kliencka, którą można wykorzystać do testów czat_klient.cpp
Zaimplementuj następujące funkcjonalności:
- Przycisk
Run
- tworzy gniazdo i ustawia je w trybie do nasłuchu.
- tworzony główny wątek serwera, który oczekuje na połączenia
- Główny wątek serwera:
- w przypadku uzyskania połączenia tworzony jest wątek odpowiedzialny za obsługę danych nadchodzących od pojedynczego klienta.
- w momencie przekroczenia dozwolonej liczby połączeń uniemożliwia nawiązanie połączenia z kolejnymi klientami
- może być przerwany poprzez odpowiedni przycisk (np. ponowne wciśnięcie klawisza
Run
)
- W przypadku nawiązania połączenia tworzony jest osobny wątek obsługujący to połączenie:
- oczekuje na nadchodzące komunikaty
- w przypadku otrzymania komunikatu rozsyła jego kopię do wszystkich połączonych z serwerem klientów
- w przypadku zerwania połączenia zamyka gniazdo związane z tym połączeniem
Przydatne funkcje
Obsługa wątków:
- CreateThread tworzenie wątku
- TerminateThread przerywanie działania wątku
- CloseHandle zamyka uchwyt do obiektu (np. do wątku)
Komunikacja sieciowa:
- plik nagłówkowy
winsock2.h
i bibliotekaWS2_32.lib
- socket, closesocket(), tworzenie gniazda
- bind kojarzy gniazdo z lokalnym adresem
sockaddr_in adres; adres.sin_port=htons(numer_portu); adres.sin_family=AF_INET; adres.sin_addr.s_addr=htonl(INADDR_ANY); bind(gniazdo, &adres, sizeof(adres));
- inet_addr zamienia adres IP z postaci łańcucha znakowego do postaci liczby całkoiwtej
- listen() ustawia gniazdo w stanie nasłuchu
- accept() ustanawia połaczenie, zwraca gniazdo łączące z klientem, pozwala na przesyłanie danych za pomocą
send()
irecv()
- send() wysyła dane przez gniazdo
- recv() odbiera dane z połacznoego gniazda