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)

WaitForSingleObject

WaitForMultipleObjects

Komunikacja sieciowa:

plik nagłówkowy winsock2.h i biblioteka WS2_32.lib

WSAStartup, WSACleanup()
inicjalizacja WinSocket

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

htons i htonl transfrormacja liczby short/long wymagana przy definiowaniu portu w protokole AF_INET i AF_INET6

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() i recv()

send() wysyła dane przez gniazdo

recv() odbiera dane z połacznoego gniazda