Programowanie sieciowe

Wykłady są w piątki o 14:15. Forma zdalna, w MS Teams został założony dedykowany zespół. Jeśli macie Państwo problem z dostępem do tego zespołu, to proszę mi to zgłosić.

Ćwiczenia prowadzone są klasycznie. Grupa nr 1 w środy o 10:30 w sali G-1-08, grupa nr 2 o 14:30 w sali G-1-03.

Zapraszam również na konsultacje (pokój C-2-29 lub online, terminy w USOSwebie).

Wykład i egzamin

Slajdy i inne materiały z wykładu będą sukcesywnie umieszczane na karcie „Udostępnione” w zespole MS Teams.

Przykładowe programy są dodatkowo dostępne w /home/palacz/PS/ po zalogowaniu się na którejś z linuksowych maszyn.

Egzamin jest ustny. Aby do niego móc podejść trzeba wcześniej zaliczyć ćwiczenia. Przy wpisywaniu ocen do indeksów będę brał pod uwagę zaliczenia ćwiczeń: ocena końcowa może być co najwyżej o jeden stopień wyższa od oceny z ćwiczeń.

Listę kluczowych zagadnień wymaganych na egzaminie znajdziecie Państwo tutaj.

Egzamin w pierwszym terminie rozłożony będzie na dwa dni, wtorek 16.06 i środę 17.06. Egzaminuję po dwie osoby na raz. Proszę samodzielnie wpisać się na listę egzaminacyjną.

Literatura

  1. W. Richard Stevens, „UNIX Network Programming” (2 tomy), wydane po polsku jako „UNIX: programowanie usług sieciowych” przez WNT w 2000 i 2001 roku.

    Gniazdka sieciowe BSD omówione są w tomie 1. W tomie 2 jeden z rozdziałów omawia Sun RPC.

  2. Douglas E. Comer, David L. Stevens, „Internetworking with TCP/IP, Vol. III”, wydane po polsku jako „Sieci komputerowe TCP/IP. Tom 3. Programowanie w trybie klient-serwer: wersja BSD”, WNT 1997.

  3. Dokumenty RFC.

    Definicje protokołów, w oparciu o które działa Internet. Dostępne online na witrynie https://www.rfc-editor.org/.

  4. Standardy POSIX oraz SUS (Single UNIX Specification).

    W chwili obecnej jest to jeden i ten sam standard pod dwiema różnymi nazwami. Jest czymś w rodzaju wspólnego mianownika dla uniksopodobnych systemów operacyjnych. Specyfikuje zbiór plików nagłówkowych oraz funkcji dostępnych w bibliotece standardowej języka C. Kod korzystający tylko z tych funkcji ma dużą szansę skompilować się i poprawnie działać m.in. na Linuksie, macOS-ie, iOS-ie i Androidzie.

    Specyfikacja POSIX.1-2008 / SUSv4 dostępna jest online i obejmuje funkcje potrzebne do obsługi gniazdek (socket, bind, itd.).

  5. Manual systemowy.

    Zbiór stron opisujących różne aspekty danego systemu operacyjnego: polecenia wydawane z linii komend, funkcje jądra, funkcje biblioteki standardowej, itp. Dostęp poprzez polecenie man: man socket, man bind, itd. Odstępstwa w zachowaniu funkcji od tego, co mówi POSIX powinny być wyraźnie odnotowane. Podobnie wyraźnie oznaczone powinny być specyficzne dla danego systemu funkcje rozszerzające POSIX (dla Linuksa patrz np. man epoll_create).

    Manual podzielony jest na sekcje. Może się zdarzyć, że w dwóch różnych sekcjach są strony o tej samej nazwie. Poleceniu man można więc podać dodatkowy argument, wskazujący o którą sekcję chodzi: man 2 socket, man 7 socket.

    Najnowsza wersja linuksowego manuala jest dostępna online.

  6. Dokumentacja glibc.

    W systemach linuksowych standardową biblioteką jest przeważnie GNU C Library. Ma ona swoją własną dokumentację (dostępna online), tworzoną niezależnie od manuala systemowego. Jeden z rozdziałów podręcznika jest poświęcony gniazdkom.

  7. Informacje z witryny Stack Overflow oraz te znalezione w innych zakątkach Internetu przy pomocy wyszukiwarki Google.

    Znalezione w sieci odpowiedzi na rozmaite pytania często zawierają gotowe fragmenty kodu. Można się na nich wzorować, albo wręcz je skopiować w całości, ale wcześniej trzeba je zrozumieć. Bez tego nie będziecie Państwo wiedzieć w jakich sytuacjach kod może zadziałać w inny sposób niż tego oczekujecie, nie będziecie też wiedzieć czy kod jest kompletny czy też może jego autor pominął pewne fragmenty (np. zwalnianie przydzielonych zasobów).

  8. Kod i odpowiedzi generowane przez narzędzia sztucznej inteligencji.

    Jak wyżej, tylko z jeszcze większym naciskiem położonym na konieczność rozumienia tego, co SI zwróciła. SI potrafi halucynować, musicie weryfikować jej odpowiedzi.

Ćwiczenia

W trakcie semestru można mieć co najwyżej trzy nieusprawiedliwione nieobecności. Przekroczenie tego limitu oznacza brak zaliczenia.

Zaliczenia wystawiane są podstawie zadań zaliczeniowych, kolokwiów oraz wykazanego przez Państwa podczas zajęć poziomu wiedzy i umiejętności (możecie o tym myśleć jako o plusach i minusach zapisywanych w moich notatkach po przeprowadzonych z Wami rozmowach).

Poniżej znajdziecie Państwo spis zagadnień przerabianych podczas poszczególnych spotkań. Są tam też listy zadań. Jeśli nie zdążycie ich zrobić podczas ćwiczeń, to automatycznie stają się one zadaniami domowymi.

Rozwiązania wszystkich zrobionych od początku semestru zadań należy mieć pod ręką (najlepiej zapisywać je na wydziałowym koncie linuksowym), bo podczas ćwiczeń wspólnie analizujemy kod Waszych rozwiązań. Jest to robione albo w formie indywidualnych rozmów, albo w formie dyskusji z udziałem całej grupy.

Zadania mają różny poziom ważności. Zdecydowana większość jest mało ważna: powinniście je zrobić i móc je na żądanie pokazać, ale nie musicie mi ich przesyłać. Jeśli zdarzy się Wam raz na miesiąc któreś z tych zadań pominąć, to nie będzie miało to negatywnych konsekwencji dla Waszej oceny końcowej.

Drugi poziom to tzw. zadania zaliczeniowe. W semestrze jest ich około pięciu, pierwsze pojawia się na trzecich ćwiczeniach. Dla nich wymagane jest wysłanie rozwiązania poprzez Pegaza do określonego dnia — termin będzie podany przy zadaniu, spóźnienia skutkują obniżeniem oceny za dane zadanie.

Trzeci poziom to zadania oznaczone jako nieobowiązkowe. Osoby szczególnie zainteresowane tematem mogą spróbować się z nimi zmierzyć, można je ze mną przedyskutować, ale nie wpływają na końcową ocenę z ćwiczeń.

Środowisko pracy

Oficjalnym środowiskiem są narzędzia zainstalowane na linuksowych komputerach w pracowniach studenckich oraz na serwerze spk-ssh.if.uj.edu.pl (można się na niego zalogować z domu). Oddawany kod musi dawać się za ich pomocą uruchomić.

W szczególności kod w języku C musi się kompilować bez ostrzeżeń za pomocą gcc -std=c99 -pedantic -Wall. Obowiązuje standard języka C z 1999 roku, bo POSIX 2008 zakłada dostępność kompilatora z nim zgodnego.

Przy pisaniu kodu w C++ proszę używać standardu C++17, tak aby kod można było skompilować używając g++ -std=c++17 -pedantic -Wall.

Proszę w miarę możności korzystać tylko z funkcji bibliotecznych opisanych w standardzie POSIX. Gniazdka początkowo pojawiły się w systemach z rodziny BSD, wiele przykładów w podręcznikach i na witrynach korzysta więc z funkcji udostępnianych przez bibliotekę standardową BSD, ale nieobecnych w POSIX-ie. Pamiętajcie, że zazwyczaj można je łatwo zastąpić POSIX-owymi odpowiednikami (zamiast bzero użyć memset, itp.).

Zajęcia nr 1, 2026-03-04

Na następny tydzień musicie mieć Państwo działające konta linuksowe. Sprawy związane z tymi kontami załatwia się u pana Damiana Lisa.

Dzisiejsze zajęcia poświęcone są zagadnieniom przerabianym wcześniej na przedmiotach „Język C” i „Systemy operacyjne”. Gniazdka sieciowe pojawią się dopiero w następnym tygodniu.

Zadania:

  1. Napisz program w C deklarujący w funkcji main tablicę int liczby[50] i wczytujący do niej z klawiatury kolejne liczby. Wczytywanie należy przerwać gdy użytkownik wpisze zero albo gdy skończy się miejsce w tablicy (tzn. po wczytaniu 50 liczb).

    Z main należy następnie wywoływać pomocniczą funkcję drukuj, przekazując jej jako argumenty adres tablicy oraz liczbę wczytanych do niej liczb. Funkcję tę zadeklaruj jako void drukuj(int tablica[], int liczba_elementow). W jej ciele ma być pętla for drukująca te elementy tablicy, które są większe od 10 i mniejsze od 100.

  2. Przypomnij sobie wiadomości o wskaźnikach i arytmetyce wskaźnikowej w C. Napisz alternatywną wersję funkcji drukującej liczby, o sygnaturze void drukuj_alt(int * tablica, int liczba_elementow). Nie używaj w niej indeksowania zmienną całkowitoliczbową (nie może się więc pojawić ani tablica[i], ani *(tablica+i)), zamiast tego użyj wskaźnika przesuwanego z elementu na element przy pomocy ++.

    W dwóch następnych zadaniach też używaj przesuwanego wskaźnika zamiast indeksowania zmienną całkowitoliczbową.

  3. Opracuj funkcję sprawdzającą, czy przekazany jej bufor zawiera tylko i wyłącznie drukowalne znaki ASCII, tzn. bajty o wartościach z przedziału domkniętego [32, 126]. Funkcja ma mieć następującą sygnaturę: bool is_printable_buf(const void * buf, int len). Pamiętaj o włączeniu nagłówka <stdbool.h>, bez niego kompilator nie rozpozna ani nazwy typu bool, ani nazw stałych true i false.

    Konieczne będzie użycie rzutowania wskaźników, bo typ void * oznacza „adres w pamięci, ale bez informacji o tym co w tym fragmencie pamięci się znajduje”. Na początku ciała funkcji trzeba go więc zrzutować do typu „adres fragmentu pamięci zawierającego ciąg bajtów”.

    Napisz też jakiś prosty program, który pozwoli Ci przetestować działanie funkcji is_printable_buf.

  4. Opracuj alternatywną wersję funkcji, biorącą jako argument łańcuch w sensie języka C, czyli ciąg niezerowych bajtów zakończony bajtem równym zero (ten końcowy bajt nie jest uznawany za należący do łańcucha). Ta wersja funkcji ma mieć sygnaturę bool is_printable_str(const char * str).

  5. W dokumentacji POSIX API znajdź opisy czterech podstawowych funkcji plikowego wejścia-wyjścia, tzn. open, read, write i close. Czy zgadzają się one z tym, co pamiętasz z przedmiotu „Systemy operacyjne”? Jakie znaczenie ma wartość 0 zwrócona jako wynik funkcji read?

  6. Zaimplementuj program kopiujący dane z pliku do pliku przy pomocy powyższych funkcji. Zakładamy, że nazwy plików są podawane przez użytkownika jako argumenty programu (tzn. będą dostępne w tablicy argv). Zwróć szczególną uwagę na obsługę błędów — każde wywołanie funkcji we-wy musi być opatrzone testem sprawdzającym, czy zakończyło się ono sukcesem, czy porażką.

    Funkcje POSIX zwracają -1 aby zasygnalizować wystąpienie błędu, i dodatkowo zapisują w globalnej zmiennej errno kod wskazujący przyczynę wystąpienia błędu (na dysku nie ma pliku o takiej nazwie, brak wystarczających praw dostępu, itd.). Polecam Państwa uwadze pomocniczą funkcję perror, która potrafi przetłumaczyć ten kod na zrozumiały dla człowieka komunikat i wypisać go na ekranie.

  7. (nieobowiązkowe) Modyfikacja powyższego zadania. Zakładamy, że kopiowany plik jest plikiem tekstowym. Linie są zakończone bajtami o wartości 10 (znaki LF, w języku C zapisywane jako '\n'). Podczas kopiowania należy pomijać parzyste linie (tzn. w pliku wynikowym mają się znaleźć pierwsza, trzecia, piąta linia, a druga, czwarta, szósta nie).

  8. (nieobowiązkowe) Kolejna modyfikacja: popraw program tak, aby i znaki '\n', i dwubajtowe sekwencje złożone ze znaku '\r' i następującego po nim znaku '\n' były traktowane jako terminatory linii.

Zajęcia nr 2, 2026-03-11

Na dzisiejszych zajęciach obowiązuje język C. Dzięki temu Wasze programy będą bezpośrednio korzystały z funkcji jądra systemu operacyjnego. Pamiętajcie o konieczności sprawdzania rezultatów przez te funkcje zwracanych!

Zadania:

  1. Linuksowe dystrybucje zazwyczaj zawierają program netcat (może być też dostępny pod nazwą nc) lub jego ulepszoną wersję, ncat. Pozwala on m.in. nawiązać połączenie ze wskazanym serwerem, a następnie wysyłać do niego znaki wpisywane z klawiatury; odpowiedzi zwracane przez serwer są drukowane na ekranie. Pozwala też uruchomić się w trybie serwera czekającego na połączenie na wskazanym numerze portu.

    Otwórz dwa okna terminalowe, w pierwszym z nich uruchom

    ncat -v -l 20123
    

    a w drugim

    ncat -v 127.0.0.1 20123
    

    (adres 127.0.0.1 to taki magiczny adres IPv4, który zawsze oznacza lokalny komputer). Jeśli wszystko poszło dobrze i netcaty nawiązały połączenie, to linie wpisywane w jednym z okien powinny pojawiać się w drugim. Aby przerwać działanie netcata użyj kombinacji klawiszy Ctrl-C.

    Sprawdź co się dzieje, jeśli spróbujesz uruchomić klienta (netcat bez opcji -l) jako pierwszego. Sprawdź, w jaki sposób netcat-klient oraz netcat-serwer reagują, gdy proces na drugim końcu połączenia zostanie zabity przez Ctrl-C. Proszę nie uogólniać tych obserwacji na wszystkie programy korzystające z gniazdek, inne programy mogą się zachowywać trochę inaczej niż netcat.

    Uwaga: jeśli pracujesz zdalnie na spk-ssh, to uruchomiony przez Ciebie netcat może wejść w kolizję z netcatami uruchomionymi w tym samym czasie przez innych studentów. Wybierz wtedy numer portu inny niż 20123, ale większy niż 1024.

  2. Wszystkie wersje netcata domyślnie korzystają z TCP. Trzeba im podać w linii komend opcję -u, aby zamiast gniazdka TCP utworzone zostało gniazdko UDP. Powtórz eksperymenty używając poleceń

    ncat -v -u -l 20123
    
    ncat -v -u 127.0.0.1 20123
    

    Sprawdź co się teraz będzie działo, gdy jeden z działających netcatów zabijesz przez Ctrl-C. Co się zmieniło w porównaniu do eksperymentów z TCP? I czy treść wyświetlanych komunikatów o błędach jest taka sama?

  3. Przejrzyj dokumentację netcata, upewnij się co do znaczenia opcji -v, -l oraz -u. Sprawdź też co robi opcja -C, czyli --crlf. W jakich sytuacjach może ona być potrzebna?

  4. (nieobowiązkowe) Jeśli oprócz polecenia ncat dostępna jest również któraś z odmian polecenia nc albo polecenie socat, to sprawdź czy za jego pomocą też da się wykonać powyższe eksperymenty. Może to wymagać zmiany lub dodania opcji w poleceniach uruchamiających serwer i klienta.

  5. Napisz prosty serwer zwracający wizytówkę. Powinien tworzyć gniazdko TCP nasłuchujące na porcie o numerze podanym jako argv[1] (użyj socket, bind i listen), następnie w pętli czekać na przychodzące połączenia (accept), wysyłać ciąg bajtów Hello, world!\r\n jako swoją wizytówkę, zamykać odebrane połączenie i wracać na początek pętli. Pętla ma działać w nieskończoność, aby przerwać działanie programu trzeba będzie użyć Ctrl-C.

    Zamiast pisać kod programu od zera możesz wykorzystać szkielet tcp_srv_skel.c, odpowiednio go rozbudowując (albo przycinając, jeśli są w nim rzeczy niepotrzebne w tym zadaniu).

  6. Przetestuj netcatem powyższy serwer.

  7. Napisz prostego klienta, który łączy się (użyj socket i connect) z usługą wskazaną argumentami podanymi w linii komend (adres IPv4 w argv[1], numer portu TCP w argv[2]), drukuje na ekranie wizytówkę zwróconą przez serwer i kończy pracę. Pamiętaj o zasadzie ograniczonego zaufania i przed przesłaniem odebranego bajtu na stdout weryfikuj, czy jest to znak drukowalny lub znak kontrolny używany do zakończenia linii bądź wstawienia odstępu ('\n', '\r' oraz '\t').

    Możesz użyć tcp_clnt_skel.c jako punktu startowego.

  8. Sprawdź, czy program-klient poprawnie współdziała z programem-serwerem.

  9. Spróbuj napisać podobną parę klient-serwer komunikującą się za pomocą protokołu UDP. Pamiętaj, że UDP nie jest protokołem połączeniowym: wywołanie connect na gniazdku UDP nie powoduje wysłania w sieć żadnych pakietów. Klient musi jako pierwszy wysłać jakiś datagram, a serwer dowiaduje się o istnieniu klienta dopiero gdy ten datagram do niego dotrze. Sprawdź, czy możliwe jest wysyłanie pustych datagramów (tzn. o długości zero bajtów) — jeśli tak, to może niech klient właśnie taki wysyła?

  10. (nieobowiązkowe) Przepisz powyższe rozwiązania w innym języku, np. w Javie lub Pythonie. Porównaj obie wersje i oceń, czy nowy kod jest krótszy i / lub czytelniejszy od starego.

Zajęcia nr 3, 2026-03-18

Dalej obowiązuje język C. Ostatnie zadanie jest ważne, macie Państwo sześć dni na jego zrobienie. W przyszłym tygodniu przedyskutujemy programy, które przysłaliście, i w razie potrzeby będziecie mieli następne sześć dni na opracowanie poprawionych wersji.

Testy łączności w dwóch pierwszych zadaniach proszę robić w parach lub trzyosobowych zespołach.

Zadania:

  1. Dokończ pisanie par klient-serwer dla TCP/IPv4 oraz UDP/IPv4 (co razem daje cztery programy). Przetestuj czy działają poprawnie gdy klient i serwer są uruchomione na dwóch różnych komputerach w SPK. Wymaga to znajomości adresu IP przydzielonego komputerowi, na którym uruchamiany jest serwer — można go znaleźć w wynikach polecenia ip address show.

  2. Sprawdź co się dzieje, gdy podasz zły adres IP albo zły numer portu serwera. Czy jądro systemu operacyjnego daje nam w jakiś sposób o tym znać? Jeśli tak, to jak długo trzeba czekać, aż jądro poinformuje nasz proces o wystąpieniu błędu?

    Pamiętaj, że protokoły sieciowe z korekcją błędów wykonują wielokrotne retransmisje pakietów w zwiększających się odstępach czasu. Może to zająć nawet kilkadziesiąt minut. Nie pomyl sytuacji „proces zawiesza się na pięć minut zanim jądro zwróci -1” z sytuacją „zawiesza się na stałe”.

    Jeśli któryś z klientów może się zawiesić czekając w nieskończoność na odpowiedź z nieistniejącego serwera, to popraw jego kod aby tego nie robił. W slajdach z wykładu są pokazane funkcje, które pozwalają na wykonywanie operacji we-wy z timeoutem (można go ustawić np. na 10 sekund).

  3. Przeanalizuj niniejszą specyfikację protokołu sprawdzania, czy wyrazy są palindromami. Czy jest ona jednoznaczna, czy też może zostawia pewne rzeczy niedopowiedziane?

    Komunikacja pomiędzy klientem a serwerem odbywa się przy pomocy datagramów. Klient wysyła datagram zawierający wyrazy do sprawdzenia. Serwer odpowiada datagramem zawierającym albo ułamek z liczbą otrzymanych wyrazów w mianowniku i liczbą wyrazów-palindromów w liczniku, albo komunikat o błędzie.

    Zawartość datagramów interpretujemy jako tekst w ASCII. Datagramy wysyłane przez klienta mogą zawierać litery i spacje. Datagramy wysyłane przez serwer mogą zawierać albo cyfry i znak /, albo pięć liter składających się na słowo „ERROR”. Żadne inne znaki nie są dozwolone (ale patrz następny akapit).

    Aby ułatwić ręczne testowanie serwera przy pomocy ncat, serwer może również akceptować datagramy mające na końcu dodatkowy znak \n (czyli bajt o wartości 10) albo dwa znaki \r\n (bajty 13, 10). Serwer może wtedy, ale nie musi, dodać \r\n do zwracanej odpowiedzi.

  4. Napisz serwer UDP/IPv4 nasłuchujący na porcie nr 2020 i implementujący powyższy protokół.

    Serwer musi weryfikować odebrane dane i zwracać komunikat o błędzie jeśli są one nieprawidłowe w sensie zgodności ze specyfikacją protokołu.

    To zadanie jest ważne — zaimplementowane rozwiązanie trzeba oddać najpóźniej we wtorek 24 marca, nawet jeśli nie jest jeszcze w pełni gotowe.

Podsumowanie analizy specyfikacji z zajęć nr 3

Proszę uwzględnić poniższe decyzje przy implementowaniu serwera.

Czy między wyrazami może być więcej niż jedna spacja?

Nie. Nie pozwalamy na używanie ciągu dwóch lub więcej spacji jako separatora.

Spacje przed pierwszym wyrazem bądź za ostatnim też są zabronione.

Czy utożsamiamy wielkie i małe litery?

Tak. Jeśli klient jako jeden z wyrazów prześle „Ala”, to serwer powinien ten wyraz uznać za palindrom.

Czy jednoliterowe wyrazy są palindromami?

Tak.

Jak serwer odpowiada na puste zapytania? Zwraca „0/0” czy „ERROR”?

„0/0”. Zapytania zawierające pusty zbiór wyrazów uznajemy za poprawne.

Czy jest jakiś limit na długość zapytań-datagramów wysyłanych przez klienta?

Na poziomie naszego protokołu aplikacyjnego klientowi nie stawiamy żadnych wymagań. Jedynym ograniczeniem rozmiaru wysyłanych przez niego zapytań jest więc limit narzucany przez używany protokół transportowy. W szczególności, UDP/IPv4 ma limit równy 65507 bajtom (ciut poniżej 64 KiB).

Serwer musi być w stanie przetwarzać zapytania mające 1024 bajty lub mniej, na większe może odpowiadać „ERROR”. Zalecane jest, aby podczas implementowania serwera przyjąć wyższe ograniczenie, jeśli jest to możliwe. Najlepiej, gdy serwer może przetwarzać zapytania maksymalnego rozmiaru dopuszczanego przez używany protokół transportowy.

Zajęcia nr 4, 2026-03-25

Raporty z próbnych kompilacji oddanych programów i testu sprawdzającego, czy na „kajak” odpowiadają „1/1”, można znaleźć na spk-ssh.if.uj.edu.pl w katalogu /home/palacz/PS/.

W tym tygodniu zajmiemy się testowaniem. Przy implementowaniu protokołów sieciowych samo napisanie kodu to tylko początek pracy — potem trzeba sprawdzić, czy ten kod jest zgodny ze specyfikacją protokołu. Trzeba przetestować, czy akceptuje on wszystkie zapytania, które wg specyfikacji są poprawne, czy wykrywa i odrzuca wszystkie te, które są niepoprawne, i czy zwracane odpowiedzi mają zgodny ze specyfikacją format.

Jeśli dzięki testowaniu znajdziecie Państwo błędy w kodzie i je poprawicie, albo jeśli wczoraj przesłana wersja serwera ma jakieś braki, to możecie wysłać poprawioną wersję najpóźniej we wtorek 31 marca.

Zadania:

  1. Przetestuj ręcznie znajdujący palindromy serwer UDP. Jeśli akceptuje końcowe \r\n, to możesz to zrobić uruchamiając

    ncat --udp --crlf 127.0.0.1 2020
    
    albo
    socat stdio udp4:127.0.0.1:2020,crlf
    

    i wpisując kolejne zapytania z klawiatury.

    Zapytanie bez końcowego \r\n czy też \n można wygenerować poleceniem printf podłączonym do wejścia socata:

    printf "Ala i kot" | socat -t 5.0 stdio udp4:127.0.0.1:2020
    

    Przełącznik -t 5.0 nakazuje socatowi odczekać pięć sekund po zakończeniu wysyłania danych do serwera i zakończyć działanie. Jakiś timeout jest niezbędny, bo socat nie wie, że serwer zwróci dokładnie jeden datagram. W ogólnym przypadku odpowiedź z serwera UDP może się przecież składać z wielu datagramów, a na poziomie bezpołączeniowego protokołu transportowego, jakim jest UDP, nie ma po czym poznać że już je wszystkie odebrano.

  2. Często spotykanym w poprzednich latach błędem było zwracanie przez serwer dodatkowych bajtów o wartości zero, bo np. ktoś w kodzie zadeklarował sobie char wynik[20], użył sprintf aby „wydrukować” do tej tablicy tekstową reprezentację wyniku (która zajęła tylko kilka początkowych elementów tablicy), a potem przez pomyłkę wysłał klientowi całą 20-bajtową tablicę. Ten błąd łatwo przegapić, bo bajty o wartości zero są niewidoczne gdy się je wyświetla na ekranie.

    Aby sprawdzić jakie dokładnie bajty są w strumieniu danych trzeba ten strumień wysłać nie wprost na ekran, lecz np. na wejście programu od. Proszę porównać to, co wypisują dwa poniższe polecenia:

    printf "abc ijk\0xyz\n"
    
    printf "abc ijk\0xyz\n" | od -A d -t u1 -t c
    

    Użyte przełączniki nakazują wyświetlić kolejne bajty w postaci dziesiętnej oraz jako znaki ASCII (bajty odpowiadające niedrukowalnym znakom kontrolnym są wyświetlane jako sekwencje z backslashem na początku).

    Proszę spróbować zapisać zwrócone przez serwer dane do pliku, a potem ten plik wyświetlić za pomocą od:

    ncat --udp --crlf 127.0.0.1 2020 > wynik-z-serwera.txt
    
    printf "xyz" | socat -t 5.0 stdio udp4:127.0.0.1:2020 > wynik-z-serwera.txt
    
    od -A d -t u1 -t c < wynik-z-serwera.txt
    
  3. Możliwość przekierowania równocześnie wejścia i wyjścia socata można wykorzystać do stworzenia powtarzalnych testów. Załóżmy, że w pliku test-dane.txt jest ciąg bajtów składający się na zapytanie testowe. Uruchamiamy

    socat -t 5.0 stdio udp4:127.0.0.1:2020 < test-dane.txt > wynik-z-serwera.txt
    

    Jeśli przygotowaliśmy również plik test-wynik.txt, to przy pomocy poleceń cmp albo diff można łatwo porównać zawartość pliku wynik-z-serwera.txt ze wzorcowym wynikiem.

    socat nie radzi sobie z datagramami mającymi długość zero bajtów, nie da się więc przy jego pomocy wysłać pustego zapytania. Nie ma też jak odróżnić sytuacji, gdy serwer zwrócił pustą odpowiedź, od sytuacji gdy w ogóle żadna odpowiedź nie została zwrócona. Z tych powodów zamiast socata możesz chcieć użyć programu mini-udpcat.py, który został napisany specjalnie na potrzeby tych zajęć.

  4. Przygotuj kilka par plików z przykładowymi zapytaniami i oczekiwanymi wynikami. Uwzględnij także błędne zapytania, na które odpowiedzią powinno być „ERROR”.

    Wymień się tymi plikami z dwiema-trzema innymi osobami z grupy. Sprawdź, czy Twój serwer poprawnie obsługuje zapytania przygotowane przez inne osoby. Jeśli nie, to spróbujcie wspólnie ustalić przyczynę: różnice w rozumieniu specyfikacji protokołu, korzystanie w teście z opcjonalnej funkcjonalności, którą nie wszystkie serwery muszą implementować (u nas: \r\n na końcu datagramu), błędy w kodzie serwera, coś innego?

  5. (nieobowiązkowe, ale przydatne) Przygotuj sobie narzędzie automatycznie testujące sumator w oparciu o powyższe pliki z zapytaniami i odpowiedziami. Na przykład skrypt dla uniksowej powłoki, wywołujący polecenia używane w poprzednich punktach. Możesz też napisać program w C albo innym języku, wczytujący te pliki oraz komunikujący się przez gniazdko z serwerem. Zanim jednak się zabierzesz za jego pisanie, to lepiej sprawdź czy w sieci nie da się znaleźć gotowego narzędzia do testowania usług UDP — wielce możliwe, że ktoś już coś takiego zaimplementował.

Zajęcia nr 5, 2026-04-01

Wyniki testów przesłanych mi serwerów UDP dostępne są jako pliki komentarzy na Pegazie.

Jeśli wszystkie testy są poprawnie zaliczone to gratuluję, pierwsze zadanie zaliczeniowe jest za Wami. W przeciwnym razie macie Państwo następne siedem dni na zrobienie poprawek i przesłanie ostatecznej wersji kodu. Należy to zrobić najpóźniej w środę 8 kwietnia (bo we wtorek 7.04 jest dzień wolny od zajęć).

To byłoby wszystko, jeśli chodzi o serwer datagramowy. Na dzisiejszych zajęciach przechodzimy do protokołów korzystających z transportu strumieniowego. Dalej będziemy rozważać problem znajdywania wyrazów-palindromów, ale teraz zapytania i odpowiedzi będą przesyłane za pomocą TCP.

Zadania (głównie praca koncepcyjna):

  1. Napisz specyfikację strumieniowego protokołu zliczania palindromów. Dopuść możliwość przesyłania przez jedno połączenie wielu zapytań i wielu odpowiedzi (obliczonych wyników albo komunikatów o wystąpieniu błędu). Zastanów się, czego użyć jako terminatora mówiącego „w tym miejscu kończy się zapytanie” — dwuznaku \r\n, tak jak w wielu innych protokołach sieciowych? A może czegoś innego (ale wtedy miej jakieś uzasadnienie odejścia od powszechnie przyjętej konwencji)? Czy odpowiedzi serwera będą używać takiego samego terminatora?

    Rozważ, czy trzeba do specyfikacji dodawać warunek ograniczający długość przesyłanych przez klienta zapytań, np. 1024 bajty łącznie z terminatorem. To ułatwiłoby implementowanie serwera, bo dzięki temu programista piszący serwer mógłby zadeklarować roboczy bufor o rozmiarze 1024 bajtów i to na pewno wystarczyłoby, aby wczytać do niego całe zapytanie. Ale czy to jest niezbędne? Czy nasz problem wykrywania palindromów wymaga, aby serwer odebrał całe zapytanie, zanim zacznie je przetwarzać?

  2. Zastanów się nad algorytmem serwera. Będzie on musiał być bardziej złożony niż w przypadku serwera UDP. Tam pojedyncza operacja odczytu zawsze zwracała jeden datagram, czyli jedno kompletne zapytanie. W przypadku połączeń TCP niestety tak łatwo nie jest.

    Po pierwsze, jeśli klient od razu po nawiązaniu połączenia wysłał kilka zapytań jedno za drugim, to serwer może je odebrać sklejone ze sobą. Pojedyncza operacja odczytu ze strumienia może np. zwrócić 15 bajtów odpowiadających znakom xyz\r\nucho oko\r\n — jak widać, są to dwa zapytania. Serwer w odpowiedzi powinien zwrócić 0/1\r\n1/2\r\n.

    Po drugie, operacja odczytu może zwrócić tylko początkową część zapytania. Kod serwera musi wtedy ponownie wywołać read(). Takie ponawianie odczytów i odbieranie kolejnych fragmentów wyrażenia musi trwać aż do chwili odebrania \r\n — dopiero wtedy wiemy, że dotarliśmy do końca zapytania.

    Po trzecie, mogą się zdarzyć oba powyższe przypadki równocześnie. Serwer może np. odczytać ze strumienia 7 bajtów odpowiadających znakom xyz\r\nuc.

  3. Spróbuj rozpisać w formie pseudokodu algorytm serwera obsługujący powyższe komplikacje i starannie przeanalizuj, czy na pewno poradzi on sobie nawet przy założeniu maksymalnie złej woli ze strony klienta.

    Radzę wykorzystać jako inspirację przedstawiony na wykładzie automat, który otrzymywał kolejne bajty z wejścia i w swych wewnętrznych polach zapisywał wyniki ich przetworzenia. Zastanów się, jak tę koncepcję tu zastosować, skoro sprawdzenie czy słowo jest palindromem możliwe jest dopiero po wczytaniu całego słowa z gniazdka sieciowego.

  4. (nieobowiązkowe) Jeśli chcesz, możesz zacząć implementować w C/C++ taki algorytm. Zdobyte doświadczenie i napisany kod przydadzą się na następnych zajęciach.

Zajęcia nr 6, 2026-04-08

Osoby, które z wyprzedzeniem przesłały poprawione wersje serwerów UDP mają wyniki testów na Pegazie już dziś. Kod wysłany dzisiaj (macie czas do północy) zostanie przetestowany jutro.

Zaczynamy prace nad serwerem potrafiącym obsługiwać wielu klientów TCP równocześnie. Na wykładzie zostały omówione ogólne zasady pisania takich serwerów, patrz przykłady rot13_server.c oraz rot13_server.py. Pierwszy z nich demonstruje kilka uniksowych mechanizmów, które można wykorzystać do równoległej obsługi klientów, drugi pokazuje jak zaimplementować w obiektowym stylu serwer sterowany zdarzeniami (użytym językiem jest Python, ale da się coś bardzo podobnego napisać w C++ używając funkcji epoll).

Zadania:

  1. Przeczytaj poniższą specyfikację strumieniowego protokołu zliczania palindromów. Porównaj ją ze specyfikacją, którą samodzielnie napisałeś(-aś) w poprzednim tygodniu. Przeanalizuj, które części obu specyfikacji są ze sobą zgodne, a które są sprzeczne. Całkiem możliwe, że części sprzecznych nie będzie — przedstawione tydzień temu wytyczne prawdopodobnie doprowadziły większość z Was do specyfikacji takiej jak ta tutaj.

    Komunikacja pomiędzy klientem a serwerem odbywa się przy pomocy połączenia strumieniowego. Klient wysyła jedną lub więcej linii zawierających wyrazy. Dla każdej odebranej linii serwer zwraca linię zawierającą albo obliczony wynik, albo komunikat o błędzie.

    Ogólna definicja linii jest zapożyczona z innych protokołów tekstowych: ciąg drukowalnych znaków ASCII (być może pusty) zakończony dwuznakiem \r\n pełniącym rolę terminatora linii.

    Linia z zapytaniem klienta może zawierać tylko litery oraz spacje pełniące rolę separatorów słów. Obowiązują te same wymagania i interpretacje co w uprzednio rozważanym protokole datagramowym (puste zapytanie z zero słów jest uznawane za poprawne, utożsamiamy wielkie i małe litery, itd.).

    Linia z odpowiedzią serwera może zawierać albo dwa niepuste ciągi cyfr rozdzielone znakiem /, albo pięć liter składających się na słowo „ERROR”.

    (Uwaga na marginesie: wszystkie linie, i te wysyłane przez klientów, i przez serwer, mają oczywiście do opisanej powyżej zawartości dołączony terminator linii, czyli \r\n.)

    Serwer może, ale nie musi, zamykać połączenie w reakcji na nienaturalne zachowanie klienta. Obejmuje to wysyłanie danych binarnych zamiast znaków ASCII, wysyłanie linii o długości przekraczającej przyjęty w kodzie źródłowym serwera limit, długi okres nieaktywności klienta itd. Jeśli serwer narzuca maksymalną długość linii, to limit ten powinien wynosić co najmniej 1024 bajty (1022 drukowalne znaki i dwubajtowy terminator linii).

    Serwer nie powinien zamykać połączenia gdy udało mu się odebrać poprawną linię w sensie ogólnej definicji, ale dane w niej zawarte są niepoprawne (np. oprócz liter i spacji są przecinki). Powinien wtedy zwracać komunikat błędu i przechodzić do przetwarzania następnej linii przesłanej przez klienta.

  2. Napisz serwer TCP/IPv4 nasłuchujący na porcie nr 2020 i implementujący powyższy protokół. Serwer musi być w stanie równocześnie obsługiwać co najmniej 100 połączeń. Użytym językiem może być C albo C++. Proszę pisać kod tak, aby się kompilował bez ostrzeżeń odpowiednio pod gcc -std=c99 -pedantic -Wall bądź g++ -std=c++17 -pedantic -Wall.

    To zadanie jest ważne. Wstępną wersję rozwiązania trzeba oddać najpóźniej we wtorek 14 kwietnia. Zostanie ona przeze mnie przetestowana i będziecie Państwo mieli kolejny tydzień na poprawienie wykrytych błędów.

Zajęcia nr 7, 2026-04-15

Wyniki testów przesłanych serwerów strumieniowych są na Pegazie. Proszę poprawić wykryte błędy i wysłać wersję drugą najpóźniej we wtorek 21 kwietnia.

Zadania przygotowujące do pisania programów używających HTTP:

  1. Zapoznaj się z co najmniej dwoma-trzema sposobami ściągania zasobów po HTTP. Mogą to być narzędzia linii poleceń wget i curl, biblioteka C/C++ libcurl, standardowe pythonowe moduły urllib i http, niestandardowy requests, klasa java.net.URL i jej metoda openConnection(), itd.

    Przez „zapoznaj się” rozumiem „znajdź dokumentację i przeczytaj w niej, co dane narzędzie potrafi zrobić”. Powinno to również obejmować przykłady użycia, tak abyście Państwo mieli orientację, czy najprostsze ściągnięcie dokumentu spod danego URL-a wymaga napisania trzech linii kodu, czy trzydziestu.

  2. Dla każdego wybranych z narzędzi sprawdź, czy potrafi ono obsługiwać inne metody niż GET i POST, w jaki sposób specyfikuje się argumenty przesyłane w zapytaniach POST, czy potrafi obsługiwać ciasteczka i czy potrafi je zapisywać w tzw. cookie jar.

  3. (nieobowiązkowe) Zapoznaj się z pythonową biblioteką Beautiful Soup lub jej javową wersją znaną jako jsoup. Bardzo ułatwiają pisanie programów przetwarzających treść dokumentów HTML.

Uwaga: eksperymentów ze ściąganiem danych z witryn nie da się wykonać na spk-ssh. Firewall jest tam tak skonfigurowany, że blokuje próby łączenia się z spk-ssh do innych hostów.

Zajęcia nr 8, 2026-04-22

Wyniki testów przesłanych programów są na Pegazie. Zachęcam do opracowania i wysłania trzeciej wersji serwera, jeśli nie wszystkie testy są poprawnie zaliczone. Należy to zrobić najpóźniej we wtorek 28 kwietnia.

Na zajęciach 6 maja będzie kolokwium. Oto kilka pytań podobnych do tych, które się na kolokwium pojawią:

  1. Poniższy pseudokod jest niekompletny. Co trzeba do niego dodać, aby zaczął odbierać przychodzące połączenia i wysyłać wizytówki?

    s = socket(INET, STREAM)
    bind(s, "0.0.0.0:2020")    # 0.0.0.0 to INADDR_ANY
    while (true) {
        c = accept(s)
        write(c, "Cześć! Jestem serwerem TCP/IPv4.\r\n")
        close(c)
    }
    
  2. Klient wysłał serwerowi dwa datagramy UDP, liczące odpowiednio 5 i 20 bajtów. Serwer był przez kilka sekund zajęty czymś innym, więc w chwili gdy wykonał instrukcję read(s, buf, 16) oba datagramy już dotarły i czekały w buforach jądra. Co w tej sytuacji zrobi jądro?

  3. Czy funkcja socket może zgłosić błąd (tzn. zwrócić -1)?

  4. Czym się różni utworzenie nowego procesu potomnego od utworzenia nowego wątku?

Proszę spróbować samodzielnie na te pytania odpowiedzieć i gdzieś na boku odpowiedzi zapisać, pod koniec ćwiczeń wspólnie je przedyskutujemy.

W drugiej kolejności proszę zabrać się za dzisiejsze zadania. Można używać Pythona, Javy lub C/C++ wraz z biblioteką obsługującą wysyłanie zapytań HTTP i HTTPS. Programy mają się dawać skompilować i uruchomić w studenckiej pracowni komputerowej (na spk-ssh nie da się ich uruchomić, bo tam firewall blokuje wychodzące połączenia HTTP).

Zadania:

  1. Serwery HTTP zazwyczaj podają swoją nazwę i czasem również wersję, np. Apache/2.2.10 (Unix), w polu Server w nagłówku odpowiedzi.

    Napisz program, który w argv dostaje nazwy witryn, takie jak google.com, www.uj.edu.pl itp. Do każdej z nich wysyła zapytanie GET / przez HTTP i HTTPS (na domyślne numery portów 80 i 443) i drukuje zwrócone wartości pól Server.

    Przykładowo, dla www.uj.edu.pl należy wypisać:

    port 80: nginx
    port 443: nginx
    
  2. Napisz program sprawdzający, czy pewna określona witryna działa poprawnie. Sprawdzenie ma polegać na pobraniu strony spod ustalonego adresu (np. spod http://th.if.uj.edu.pl/). Proszę nie zapomnieć o zweryfikowaniu, czy na pewno udało się ją poprawnie pobrać (status 200) i czy to jest strona HTML (typ text/html). Następnie należy sprawdzić, czy rzeczywiście jest to spodziewana strona, a nie np. komunikat o wewnętrznym błędzie serwera WWW — to można zweryfikować sprawdzając czy w pobranej treści znajduje się pewien zadany z góry ciąg znaków (np. „Institute of Theoretical Physics”).

    Program, w zależności od wyniku sprawdzenia, musi zwracać jako wynik funkcji main kod sukcesu (zero) bądź porażki (wartość większa od zera). Osoby piszące w Pythonie powinny użyć sys.exit(0) albo sys.exit(1), a w Javie należy wywołać metodę System.exit z odpowiednim argumentem.

    Programy tego typu używane są w systemach monitorowania usług sieciowych. Jeśli na filmie z centrum zarządzania siecią widać ekran z listą serwerów i usług, a przy nich zielone komunikaty „OK” i gdzieniegdzie czerwone komunikaty błędów, to za tymi kolorami kryją się uruchamiane w regularnych odstępach czasu programy sprawdzające status danej usługi.

    To zadanie jest ważne — zaimplementowany program trzeba oddać najpóźniej we wtorek 5 maja. Nie będzie możliwości oddawania poprawionych wersji w późniejszym terminie, ten program jest na to zbyt prosty. Z tego samego powodu przy wystawianiu zaliczeń będę to zadanie brał z wagą 25% w porównaniu do zadań z palindromami.

  3. (nieobowiązkowe) Sprawdź, czy w Twoim ulubionym języku programowania jest standardowa biblioteka pozwalająca serializować i deserializować dane w formacie JSON. Zapoznaj się z nią, spróbuj znaleźć przykład pokazujący zamianę ciągu bajtów / znaków na struktury danych tego języka programowania (czyli deserializację ciągu).

Zajęcia nr 9, 2026-04-29

Wyniki testów trzeciej wersji palindromowego serwera TCP, o ile ją Państwo wysłaliście, są jak zwykle na Pegazie.

W tym tygodniu zajmujemy się techniką web scraping, czyli zautomatyzowanym pobieraniem danych z witryn internetowych.

Zadanie:

  1. Proszę znaleźć stronę WWW zawierającą jakąś potencjalnie potrzebną informację (aktualna temperatura w Krakowie, kurs dolara itp.), a następnie napisać program ściągający tę stronę i wyłuskujący z niej te dane. Dane te można zapisywać do jakiegoś pliku lub drukować na stdout, nie jest to ważne — ważne za to jest to, aby format zapisywanych danych pozwalał na ich wygodne dalsze przetwarzanie. Jeśli programowi z jakiejkolwiek przyczyny nie uda się ściągnąć poszukiwanej informacji, to musi zakończyć swe działanie zwracając z main kod porażki.

    Radzę wykorzystać kod programu z poprzednich zajęć. Ściąganie strony już w nim jest, wystarczy dodać ekstrahowanie interesujących nas danych.

Wyłuskiwanie danych ze strony HTML jest dość kruchą techniką, bo witryna może nieoczekiwanie zmienić swój wygląd lub treść. Biorąc to pod uwagę łatwo zauważyć, że podejście typu „zwróć bajty od 5078 do 5081” jest skazane na rychłą porażkę; „zwróć zawartość czwartego elementu <p> znajdującego się wewnątrz elementu <div> o identyfikatorze »temp«” jest lepsze. Warto postarać się o to, aby program zauważał nieoczekiwane bądź podejrzane sytuacje i je zgłaszał (np. jeśli w tym czwartym <p> jest ciąg znaków nie będący liczbą, to raczej nie jest to temperatura; jeśli znaleziona liczba wykracza poza przedział [-30, 40] to raczej nie jest to temperatura w stopniach Celsjusza).

Nawigację po treści strony ułatwia zbudowanie drzewa obiektów reprezentujących elementy strony HTML, tu ponownie polecam Państwa uwadze bibliotekę Beautiful Soup (Python) i jej odpowiednik jsoup (Java). Do sprawdzania, czy łańcuch znaków pasuje do zadanego wzorca dobrze nadają się wyrażenia regularne.

Może się zdarzyć, że traficie na stronę, która w oknie przeglądarki wyświetla potrzebne nam informacje, ale gdy się ją ściągnie to nigdzie w jej treści nie można ich znaleźć. Prawie na pewno przyczyną jest jej dynamiczna, AJAX-owa natura. Takie strony mają puste miejsca, wypełniane zawartością ściąganą przez javascriptowy kod z innych URL-i.

Współczesne przeglądarki mają wbudowane narzędzia deweloperskie, jednym z nich jest analizator połączeń sieciowych. Można przy jego pomocy spróbować odnaleźć rzeczywiste źródło potrzebnych nam danych. Poszukiwania proponuję zacząć od połączeń zainicjowanych przez XHR (czyli javascriptową klasę XMLHttpRequest bądź funkcję fetch) i / lub tych, które ściągały dokumenty typu JSON.

Udane znalezienie JSON-owego źródła surowych danych jest dobrą wiadomością, bo ryzyko zmiany formatu tych danych jest znacznie mniejsze niż ryzyko zmiany struktury strony HTML.

Może się też zdarzyć, że witryna korzysta z CAPTCHA bądź mechanizmu sprawdzającego, czy łączy się z nią typowa przeglądarka internetowa (takie mechanizmy są częścią zabezpieczeń przed atakami DDoS). Stron z takiej witryny nie da się wykorzystać jako źródła automatycznie pobieranych danych.

Zajęcia nr 10, 2026-05-06

Krótkie kolokwium na początku zajęć, proszę się nie spóźnić!

Zadania:

  1. Zapoznaj się z witryną Discogs oraz ze specyfikacją udostępnianego przez nią REST API.

  2. Spróbuj napisać program wyszukujący w bazie Discogs wszystkie albumy danego wykonawcy (np. Budki Suflera, czyli zespołu nr 359282). Sprawdź czy to, co drukuje program zgadza się z tym, co można ręcznie wyszukać na witrynie.

    Może się okazać, że Discogs odrzuca zapytania, jeśli są one całkowicie anonimowe. Sprawdź wtedy w dokumentacji kim / czym trzeba być, aby uzyskać odpowiedź — zalogowanym użytkownikiem witryny, aplikacją napisaną przez zarejestrowanego dewelopera?

  3. Napisz program, który w oparciu o informacje z Discogs sprawdza, czy muzycy z zadanego zbioru grali razem w jakichś zespołach. Wyświetl nazwy tych zespołów oraz imiona i nazwiska tych muzyków ze zbioru, którzy do nich należeli. Załóż, że numeryczne identyfikatory muzyków podawane są w argv.

    Dla przykładu: 516820 to Tomasz Zeliszewski, 532854 Marek Stefankiewicz, a 702387 Mieczysław Jurecki. Wszyscy trzej grali w Budce Suflera (id 359282), do tego Zeliszewski i Jurecki występowali w zespole Wieko (id 4751291), a Stefankiewicz i Jurecki — w Perfekcie (id 669348).

    Zadbaj, aby drukowane wyniki były posortowane po nazwie zespołu. Nie zapomnij o weryfikowaniu poprawności odpowiedzi zwracanych przez serwer. Dobrze by było, gdyby w razie przekroczenia limitu zapytań na minutę program chwilę czekał i ponawiał zapytanie, ale nie jest to obowiązkowe (program może taką sytuację traktować jak każdy inny błąd i kończyć działanie).

    To zadanie jest ważne — zaimplementowany program oddaj najpóźniej we wtorek 19 maja. Możesz użyć Pythona, Javy lub C/C++. Nie będzie możliwości oddawania poprawionych wersji w późniejszym terminie. Przy wystawianiu zaliczeń będę to zadanie brał z wagą 50% w porównaniu do zadań z palindromami.

    Uwaga: nie wolno Państwu używać bibliotek dedykowanych bazie Discogs, które są wymienione na początku strony https://www.discogs.com/developers (pod nagłówkiem „Quickstart”). To podkopałoby cel dydaktyczny, którym jest sprawdzenie, czy za pomocą zwykłej biblioteki klienckiej HTTP potraficie pobrać dane z serwera REST. Innymi słowy, zadanie sprawdza, czy potraficie przeczytać ze zrozumieniem dokumentację serwera REST i napisać kod konstruujący odpowiednie zapytania HTTP.

  4. (nieobowiązkowe) Rozszerz powyższy program tak, aby jako argument można było podawać nie tylko identyfikator, lecz również nazwisko (albo imię i nazwisko) muzyka. Najprawdopodobniej będzie wymagać to zarejestrowania się na witrynie Discogs w celu uzyskania tokena dla aplikacji, bo operacji wyszukiwania po nazwie zdaje się nie można wywoływać anonimowo.