Dwa protokoły warstwy transportowej obsługują prawie cały internet: TCP dba o to, by dane dotarły w całości i w kolejności, a UDP wysyła pojedyncze datagramy bez gwarancji. Wybór zależy od zastosowania: strona WWW i poczta potrzebują TCP, rozmowa wideo i gry w czasie rzeczywistym wolą szybkość UDP. Na lekcji uczniowie sami piszą krótkie programy sieciowe na własnym komputerze (adres 127.0.0.1) i widzą różnice w działaniu.
Scenariusz jest przeznaczony na 45 minut dla klasy znającej funkcje w Pythonie, adresy IP i porty — na przykład po lekcji o adresach prywatnych i publicznych. Program łączy się tylko z komputerem, na którym działa; nie wysyła niczego do internetu.
Przygotowanie stanowisk
Potrzebny jest Python 3 i edytor lub IDLE. Rozpakuj pakiet z sekcji materiałów do osobnego folderu i uruchamiaj skrypty z tego folderu. Program uruchomisz przez Run → Run Module albo poleceniem python start.py, python3 start.py lub py start.py.
Cele i plan lekcji
Po zajęciach uczeń potrafi:
- otworzyć połączenie TCP (
connect,accept) i wymienić dane; - wysłać datagram UDP bez nawiązywania połączenia;
- wyjaśnić, że TCP przesyła strumień bajtów, a UDP zachowuje granice wiadomości;
- dobrać protokół do zastosowania i opisać zachowanie obu przy zamkniętym porcie.
| Czas | Praca na lekcji |
|---|---|
| 0–5 min | Problem: dlaczego film i poczta używają różnych protokołów? |
| 5–15 min | TCP: połączenie, potwierdzenia, kolejność |
| 15–22 min | UDP: datagramy bez połączenia |
| 22–37 min | Trzy zadania w Pythonie |
| 37–42 min | Dobór protokołu do zastosowań |
| 42–45 min | Pytania kontrolne |
Porównanie
| Cecha | TCP | UDP |
|---|---|---|
| połączenie | nawiązywane przed wymianą danych (uzgodnienie trójetapowe) | brak |
| niezawodność | potwierdzenia, ponowne wysyłanie, kolejność | brak gwarancji, datagramy mogą zginąć lub zamienić się kolejnością |
| jednostka danych | strumień bajtów (granice porcji nie są zachowane) | datagram (granice zachowane) |
| narzut i opóźnienie | większe | małe |
| typowe zastosowania | WWW (HTTP/HTTPS), poczta, przesyłanie plików, SSH | DNS, strumieniowanie dźwięku i obrazu, gry sieciowe, VoIP |
Gniazda w Pythonie
socket.socket(AF_INET, SOCK_STREAM) tworzy gniazdo TCP, a SOCK_DGRAM — UDP. Serwer TCP wywołuje bind (przypisanie adresu i portu), listen i accept (czekanie na klienta); klient łączy się metodą connect. Port 0 oznacza „wybierz wolny port” — prawdziwy numer odczytujemy z getsockname(). Gniazdo UDP używa sendto i recvfrom; adres odbiorcy podaje się przy każdym wysłaniu.
Wnioski z zadania 3: dwie porcje wysłane przez TCP (ab, cd) odbiorca może odczytać razem jako abcd — protokół nie zna granic wiadomości, dlatego aplikacje dodają własne (np. długość lub znak końca). Dwa datagramy UDP trafiają do odbiorcy jako dwie oddzielne wiadomości. Próba połączenia TCP z portem, na którym nikt nie nasłuchuje, kończy się natychmiast odrzuceniem, a wysłanie datagramu UDP na taki port „udaje się” — nadawca nie dostaje żadnego potwierdzenia.
Znane usługi mają przypisane numery portów: HTTP — 80, HTTPS — 443, DNS (domain) — 53 (UDP, a przy dużych odpowiedziach także TCP).
Zadania
Zadanie 1. TCP. Uruchom serwer TCP na 127.0.0.1 (port 0), który odsyła dane wielkimi literami. Połącz się klientem, wyślij witaj i wypisz odpowiedź.
Zadanie 2. UDP. Uruchom serwer UDP odsyłający dane od tyłu. Wyślij datagram witaj bez nawiązywania połączenia i wypisz odpowiedź.
Zadanie 3. Różnice. (a) Wyślij przez TCP dwie porcje ab i cd i odczytaj dane odbiorcą. (b) Wyślij dwa datagramy UDP i odbierz je osobno. (c) Spróbuj połączyć się przez TCP i wysłać datagram UDP na zamknięty port.
Dla chętnych: sprawdź numery portów usług HTTP, HTTPS i DNS funkcją socket.getservbyname.
Rozwiązanie i wyniki
import socket
import threading
def serwer_tcp(gniazdo):
polaczenie, _ = gniazdo.accept()
with polaczenie:
dane = polaczenie.recv(1024)
polaczenie.sendall(dane.upper())
def serwer_udp(gniazdo):
dane, adres = gniazdo.recvfrom(1024)
gniazdo.sendto(dane[::-1], adres)
# Zadanie 1 - TCP: polaczenie, wymiana danych, zamkniecie
serwer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serwer.bind(("127.0.0.1", 0))
serwer.listen(1)
port_tcp = serwer.getsockname()[1]
threading.Thread(target=serwer_tcp, args=(serwer,), daemon=True).start()
klient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
klient.connect(("127.0.0.1", port_tcp))
klient.sendall(b"witaj")
print("TCP odpowiedz:", klient.recv(1024).decode())
klient.close()
serwer.close()
# Zadanie 2 - UDP: datagram bez nawiazywania polaczenia
serwer = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
serwer.bind(("127.0.0.1", 0))
port_udp = serwer.getsockname()[1]
threading.Thread(target=serwer_udp, args=(serwer,), daemon=True).start()
klient = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
klient.sendto(b"witaj", ("127.0.0.1", port_udp))
odpowiedz, _ = klient.recvfrom(1024)
print("UDP odpowiedz:", odpowiedz.decode())
klient.close()
serwer.close()
# Zadanie 3 - roznice
# a) TCP to strumien bajtow: dwie porcje moga zostac odczytane razem
serwer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serwer.bind(("127.0.0.1", 0))
serwer.listen(1)
port = serwer.getsockname()[1]
klient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
klient.connect(("127.0.0.1", port))
polaczenie, _ = serwer.accept()
klient.sendall(b"ab")
klient.sendall(b"cd")
odebrane = b""
while len(odebrane) < 4:
odebrane += polaczenie.recv(4 - len(odebrane))
print("TCP odebrane razem:", odebrane.decode())
klient.close()
polaczenie.close()
serwer.close()
# b) UDP zachowuje granice datagramow
odbiorca = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
odbiorca.bind(("127.0.0.1", 0))
nadawca = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
nadawca.sendto(b"ab", odbiorca.getsockname())
nadawca.sendto(b"cd", odbiorca.getsockname())
print("UDP datagramy:", odbiorca.recvfrom(1024)[0].decode(), odbiorca.recvfrom(1024)[0].decode())
nadawca.close()
odbiorca.close()
# c) polaczenie z zamknietym portem
zamkniety = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
zamkniety.bind(("127.0.0.1", 0))
port_zamkniety = zamkniety.getsockname()[1]
zamkniety.close()
proba = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
proba.connect(("127.0.0.1", port_zamkniety))
print("TCP: polaczono")
except ConnectionRefusedError:
print("TCP: polaczenie odrzucone")
proba.close()
wysylajacy = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
wysylajacy.sendto(b"x", ("127.0.0.1", port_zamkniety))
print("UDP: wyslano bez bledu")
wysylajacy.close()
Program wypisze:
TCP odpowiedz: WITAJ
UDP odpowiedz: jatiw
TCP odebrane razem: abcd
UDP datagramy: ab cd
TCP: polaczenie odrzucone
UDP: wyslano bez bledu
Odpowiedź jatiw to napis witaj odczytany od tyłu. Zadanie dla chętnych (rozszerzenie.py) wypisuje:
80 443 53
Numer portu serwera jest za każdym razem inny (wybiera go system), więc nie pojawia się w wynikach.
Typowe błędy
| Objaw | Co sprawdzić |
|---|---|
ConnectionRefusedError | Nikt nie nasłuchuje na podanym porcie albo serwer jeszcze się nie uruchomił; uruchom serwer przed klientem. |
Program „wisi” na recv lub accept | Druga strona nic nie wysłała; ustaw limit czasu (settimeout) lub sprawdź kolejność wywołań. |
OSError: address already in use | Port jest zajęty; użyj portu 0 albo zamknij poprzedni program. |
| Dane TCP odczytane „skleiły się” | To normalne: TCP to strumień bajtów; dodaj własny separator lub długość wiadomości. |
| Datagram UDP zaginął | UDP nie gwarantuje dostarczenia; w sieci lokalnej zdarza się rzadko, w internecie — regularnie. |
| Zapora blokuje połączenie | Pierwsze uruchomienie serwera może wywołać pytanie zapory systemu; połączenia na 127.0.0.1 nie opuszczają komputera. |
Pytania kontrolne i punktacja
- Który protokół wybierzesz do rozmowy wideo w czasie rzeczywistym i dlaczego?
- Co oznacza, że TCP przesyła „strumień bajtów”?
- Jaki numer portu ma HTTPS?
Odpowiedzi: UDP — ważniejsza jest mała zwłoka niż odzyskanie każdego zagubionego fragmentu. Że odbiorca nie widzi granic między wysłanymi porcjami; mogą zostać odczytane razem. 443.
Proponowana punktacja (razem 10 punktów): zadanie 1 — serwer TCP 1 pkt, klient i odpowiedź 2 pkt; zadanie 2 — serwer UDP 1 pkt, klient i odpowiedź 1 pkt; zadanie 3 — dwie porcje TCP 1 pkt, dwa datagramy UDP 1 pkt, zamknięty port 1 pkt; dobór protokołu w pytaniach 1 pkt. Zadanie dla chętnych nie jest wymagane do pełnej oceny.
Materiały do pobrania
Rozpakuj ZIP przed otwarciem plików. Materiały są dostępne bez logowania; publiczne rozwiązanie służy nauce i samodzielnemu sprawdzeniu, a nie poufnemu sprawdzianowi.
Co dalej?
Następny krok to model OSI i enkapsulacja danych oraz analiza ruchu narzędziami typu Wireshark. Ten materiał rozwija dział o warstwie transportowej w kursie Sieci w TablicaPro, gdzie znajdziesz prezentacje, ćwiczenia i testy do prowadzenia lekcji.
Źródła techniczne: moduł socket w dokumentacji Pythona oraz TCP i UDP w Wikipedii. Zobacz też lekcję o adresach prywatnych.
Poprowadź kolejne lekcje Sieci
Pełny kurs: prezentacje, ćwiczenia i testy do pracy z klasą.
Kup dostęp do kursu Sieci →