Aplikacje sieciowe i wielowątkowe

Zadanie 1 - Komunikator tekstowy z wilowątkowością

Zaimplementuj serwer programu do komunikacji tekstowej (czat) obsługujący połączenia w osobnych wątkach. Główny wątek serwera czeka na połączenie. W momencie uzyskania połączenia rozpoczyna odczyt kolejnych napływających komunikatów od klienta i umieszcza je w polu tekstowym (kontrolka edycyjna). Dodatkowo serwer umożliwia przesłanie do klienta komunikatu tekstowego umieszczonego w osobnym oknie edycyjnym. Aplikacja umożliwia zamknięcie połączenia (wraz z wątkiem głównym serwera) po wciśnięciu odpowiedniego przycisku. Wówczas ustawiane jest odpowiednie zdarzenie, które odebrane w wątku serwera powoduje zamknięcie gniazda nasłuchu i zakończenie wątku.

Ź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 rozpoczyna oczekiwanie na nadchodzące komunikaty tekstowe

w przypadku otrzymania komunikatu umieszcza go w pierwszym (wejściowym) oknie edycyjnym

w przypadku odebrania zdarzenia zamknięcia zamyka gniazda i kończy działanie wątku

Przycisk Send

w przypadku gdy istnieje aktywne połączenie wysyła do klienta tekst umieszczony w drugim (wyjściowym) polu edycyjnym

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)

Synchronizacja wątków za pomocą zdarzeń

CreateEvent tworzy uchwyt do obiektu zdarzenia

SetEvent ustawia obiekt zdarzenia w stanie sygnalizującym zajście zdarzenia

ResetEvent przywraca pierwotny stan zdarzenia

WaitForSingleObject

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