Skip to content

Prosty serwer plików UDP

Kod serwera realizującego ten protokół:

Dostępne pliki

Na serwerze (zarówno w wersji TCP, jak i UDP) uruchomionym pod adresem serwer.sieci.tcs.ovh:4567 są dostępne następujące pliki:

-rw-r--r-- 1 root root 16777216 Nov 21 09:47 16M
-rw-r--r-- 1 root root     1024 Nov 21 09:47 1K
-rw-r--r-- 1 root root  1048576 Nov 21 09:47 1M

Naiwna implementacja klienta

Schemat działania programu:

  • Klient wysyła pakiet inicjalizacyjny.
  • Klient otrzymuje informację o liczbie fragmentów.
  • W pętli:
    • Klient wysyła prośbę o \(i\)-ty fragment.
    • Klient otrzymuje \(i\)-ty fragment.

Taka implementacja będzie pobierać pliki powoli i może się zaciąć, jeżeli któryś z komunikatów się zagubi.

Prosta implementacja klienta

Schemat działania programu jest taki sam jak w wersji naiwnej, ale dodatkowo obsługuje timeout w wywołaniach recvfrom. Taka implementacja będzie pobierać pliki powoli, ale zadziała nawet jeżeli któryś z komunikatów się zagubi.

Naiwnie przyspieszona implementacja

Schemat działania programu:

  • Klient wysyła pakiet inicjalizacyjny.
  • Klient otrzymuje informację o liczbie fragmentów.
  • W pętli:
    • Klient wysyła prośbę o \(i\)-ty fragment.
  • W pętli
    • Klient otrzymuje \(i\)-ty fragment.

Taka implementacja przy większej liczbie fragmentów będzie przekraczać bufory i większość komunikatów się zagubi. Spowoduje to zacięcie się programu.

Poprawiona naiwnie przyspieszona implementacja

Schemat działania programu:

  • Klient wysyła pakiet inicjalizacyjny.
  • Klient otrzymuje informację o liczbie fragmentów.
  • W pętli (dopóki jakiś fragment wciąż nie jest pobrany):
    • W pętli:
      • Klient wysyła prośbę o każdy brakujący fragment.
    • W pętli:
      • Klient otrzymuje fragmenty aż do timeout.

Taka implementacja się nie zatnie, ale będzie pobierać pliki powoli i niepotrzebnie przeciążać sieć.

Implementacja pobierająca z zadaną prędkością \(D\) KB/s

Zakładając, że fragmenty mają po 1024 bajty i chcemy pobierać plik z prędkością \(D\) KB/s, musimy pobierać średnio \(D\) fragmentów na sekundę. Można to zaimplementować w jednym wątku przeplatając sendto i recvfrom, ale łatwiej jest podzielić program na dwa wątki: Wątek wysyłający prośby o fragmenty:

  • W pętli (dopóki jakiś fragment wciąż nie jest pobrany):
    • Klient wysyła prośbę o kolejny brakujący fragment.
    • Klient zasypia na \(1/D\) sekundy ( time.sleep(1/D) ).

Wątek odbierający fragmenty:

  • W pętli (dopóki jakiś fragment wciąż nie jest pobrany):
    • Klient otrzymuje fragment.

Schemat działania programu:

  • Klient wysyła pakiet inicjalizacyjny.
  • Klient otrzymuje informację o liczbie fragmentów.
  • Inicjalizacja tablicy fragmentów.
  • Startuje równolegle wątek wysyłający i odbierający.
  • Czeka na zakończenie wątków.

Taka implementacja będzie pobierać pliki z zadaną prędkością i będzie odporna na zagubione pakiety. Jeżeli serwer nie działa, lub zostanie wyłączony (lub odcięty), to program się zatnie. Tą sytuację można wykryć w wątku odbierającym i odpowiednio obsłużyć - zakończyć działanie obu wątków i zgłosić błąd.