Laboratorium 12: Sieć, łącza i mapowanie pamięci #
Sieć #
Celem zadania jest stworzenie prostej aplikacji konsolowej, umożliwiającej dwóm użytkownikom wymianę wiadomości przez sieć.
Kod początkowy #
Student
- Chat.Common/
- ChatClient/
- ChatServer/
- NetworkStreams.sln
Opis zadania #
ChatCommon #
Projekt ChatCommon zawiera wspólny kod wykorzystywany zarówno przez program serwera, jak i klienta. Zawiera klasę MessageDTO, która reprezentuje pojedynczą wiadomość przesyłaną w sieci, oraz folder MessageHandlers z klasami odpowiedzialnymi za jej obsługę.
Komunikacja pomiędzy programami realizowana jest z wykorzystaniem protokołu TCP. Każda wiadomość składa się z 32-bitowego nagłówka będącego liczbą typu int w zapisie big endian, który określa długość wiadomości w bajtach. Bezpośrednio po nagłówku przesyłana jest treść wiadomości w formacie JSON, zakodowana w UTF-8. Maksymalny rozmiar pojedynczej wiadomości nie może przekraczać 10 kB.
W ramach projektu należy uzupełnić implementację następujących metod:
ReadMessagew klasieMessageReader- w przypadku błędu deserializacji metoda powinna zgłosić wyjątekInvalidMessageExceptionzawierający odpowiedni opis błędu. Jeśli długość wiadomości przekracza dopuszczalny limit, należy zgłosićTooLongMessageException. W sytuacji osiągnięcia końca strumienia metoda powinna zwrócićnull.WriteMessagew klasieMessageWriter- przed wysłaniem wiadomości należy zweryfikować jej długość. W przypadku przekroczenia dopuszczalnego limitu metoda powinna zgłosić wyjątekTooLongMessageException.
Do serializacji i deserializacji wiadomości należy wykorzystać bibliotekę Newtonsoft.Json.
ChatClient #
W projekcie ChatClient zaimplementuj asynchroniczną metodę Connect, która próbuje nawiązać połączenie TCP z serwerem. W przypadku braku połączenia w ciągu trzech sekund metoda powinna zwrócić null. Do logowania przebiegu operacji wykorzystaj przekazany obiekt progress.
ChatServer #
W projekcie ChatServer należy zaimplementować asynchroniczną metodę ForwardMessagesAsync w pliku ChatServer.cs, która - do momentu zgłoszenia anulowania przez CancellationToken - odbiera wiadomości od jednego klienta, zapisuje je na standardowe wyjście, a następnie przekazuje drugiemu klientowi.
W tym samym pliku należy również zaimplementować metodę Run, która - do momentu zgłoszenia anulowania przez CancellationToken - oczekuje na połączenie dwóch klientów i inicjuje wymianę wiadomości pomiędzy nimi. Po zakończeniu konwersacji metoda powinna ponownie rozpocząć oczekiwanie na kolejnych klientów. Do obsługi połączonych klientów należy wykorzystać metodę HandleClientsAsync.
Materiały pomocnicze:
Program klienta przyjmuje adres IP serwera oraz port jako opcjonalne argumenty z wiersza poleceń. Jako ćwiczenie możesz spróbować połączyć się z serwerem działającym na innym komputerze w sieci lokalnej.
Adres IP komputera możesz sprawdzić za pomocą następujących komend.
ip aipconfigPrzykładowe rozwiązanie #
Rozwiązanie
- Chat.Common/
- ChatClient/
- ChatServer/
- NetworkStreams.sln
Łącza #
Bazy danych typu klucz-wartość
Bazy danych typu klucz-wartość to sposób przechowywania danych, w którym każda informacja zapisywana jest w postaci pary: unikalny klucz oraz odpowiadająca mu wartość.
Zamiast korzystać z rozbudowanych tabel i relacji, dane są organizowane w prostą strukturę przypominającą słownik, co umożliwia bardzo szybkie wyszukiwanie, zapisywanie i odczyt danych na podstawie klucza.
Taki model jest szczególnie przydatny w systemach wymagających dużej wydajności i łatwej skalowalności, na przykład do obsługi pamięci podręcznej, sesji użytkowników czy konfiguracji aplikacji.
Kod początkowy #
Student
Opis zadania #
Celem zadania jest implementacja prostej bazy danych typu klucz-wartość, która będzie mogła odpowiadać na zapytania z innych procesów działających na tym samym komputerze. Dla uproszczenia będziemy obsługiwać jednego klienta na raz.
Przyjmujemy następującą składnię zapytań do serwera:
- utworzenie nowej pary klucz-wartość lub aktualizacja wartości -
SET <key> <value>, na co serwer w przypadku powodzenia odpowiadaOK. - pobranie wartości -
GET <key>na co serwer zwraca żądaną wartość lubNOT_FOUND, jeśli podany klucz nie istnieje w bazie. - usunięcie pary klucz-wartość -
DELETE <key>, na co serwer odpowiadaOKprzy powodzeniu lubNOT_FOUND, jeśli dany klucz nie był obecny. - w przypadku nieprawidłowego zapytania serwer wysyła wiadomość
ERROR <msg>
Wszystkie wiadomości kodowane są za pomocą UTF-8 i oddzielane od siebie znakiem nowej linii. Z tego powodu żadna z wiadomości nie może zawierać w sobie tego znaku.
Klient #
W projekcie Client zaimplementuj następujące fragmenty kodu:
- w metodzie
Mainutwórz zmiennąclienttypuNamedPipeClientStreami połącz się z serwerem. Jeśli łączenie będzie trwało więcej niż trzy sekundy - zakończ program z odpowiednim komunikatem. - Zaimplementuj metodę
GetResponse, która wysyła zapytanie do serwer i oczekuje na odpowiedź. W przypadku przerwania połączenia zwracana jest wartośćnull
Serwer #
W projekcie Server w klasie KvServer zaimplementuj następujące fragmenty kodu:
- W metodzie
Startutwórz zmiennąservertypuNamedPipeServerStream, połącz się z klientem. Łączenie może zostać przerwane przezCancellationToken - Zaimplementuj metodę
HandleClientAsync, która w sposób asynchroniczny odczytuje zapytania od klienta i odpowiada na nie. Uwzględnij możliwość przerwania przezCancellationToken. Do uzyskania odpowiedzi wykorzystaj metodęProcessCommand.
Materiały pomocnicze:
Przykładowe rozwiązanie #
Rozwiązanie
Mapowanie plików #
Celem zadania jest zaimplementowanie prostej biblioteki do odczytu dużych plików CSV. Biblioteka powinna umożliwiać przetwarzanie plików, które są zbyt duże, aby można je było w całości wczytać do pamięci RAM.
Kod początkowy #
Student
- BigCSVReaders/
- BigCSVReaders.Benchmark/
- BigCSVReaders.Tests/
- MMF.sln
Opis zadania #
Projekt BigCSVReader zawiera abstrakcyjną klasę BigCsvReader, która odpowiada za odczytywanie fragmentów dużych plików CSV. W konstruktorze klasa tworzy pomocniczy plik z rozszerzeniem .offsets. Jest to binarny plik, w którym kolejno zapisywane są 8-bajtowe wartości reprezentujące przesunięcia poszczególnych wierszy w oryginalnym pliku CSV. Biblioteka obsługuje wyłącznie kodowanie UTF-8.
Celem zadania jest uzupełnienie implementacji klas pochodnych StreamBigCsvReader oraz MmfBigCsvReader, a następnie porównanie ich wydajności przy użyciu projektu BigCSVReader.Benchmark.
W
StreamBigCsvReadernależy wykorzystać standardowy mechanizm odczytu pliku z użyciemFileStream.W
MmfBigCsvReadernależy zastosować mapowanie pliku do pamięci (Memory-Mapped Files).
Materiały pomocnicze:
Przykładowe rozwiązanie #
Rozwiązanie
- BigCSVReaders/
- BigCSVReaders.Benchmark/
- BigCSVReaders.Tests/
- MMF.sln