CodeGym /Kursy /Python SELF PL /Moduł socket

Moduł socket

Python SELF PL
Poziom 24 , Lekcja 1
Dostępny

7.1 Co to są sockety?

Kopmy głębiej. Najpierw nauczyliśmy się pracować z request, potem z http.client, a potem z proxy. Co dalej? A dalej zajrzymy wszystkim tym bibliotekom pod maskę…

Socket (dosłownie — gniazdko) — to punkt w sieci, przez który dane są wysyłane i odbierane. Socket można przedstawić jako końcowy punkt dwukierunkowego kanału komunikacji między dwoma programami, działającymi na jednej lub różnych maszynach.

Sockety obsługują różne protokoły sieciowe, ale najczęściej używane są dwa:

  • TCP (Transmission Control Protocol): Niezawodny protokół, który zapewnia ustanowienie połączenia, sprawdzenie integralności danych i ich prawidłową kolejność.
  • UDP (User Datagram Protocol): Protokół, nie zorientowany na połączenie, który nie gwarantuje dostarczenia danych, ale jest szybszy i efektywniejszy dla niektórych aplikacji.

Do identyfikacji socketu używane są adres IP (identyfikujący urządzenie w sieci) i port (identyfikujący konkretne aplikacje lub usługę na urządzeniu).

Ważne! Adres IP i port jednoznacznie identyfikują program w sieci. To jak adres domu i numer mieszkania. Adres domu (adres IP) — to adres twojego komputera w sieci, a port — to numer mieszkania, który program używa do otrzymywania i wysyłania wiadomości.

Więcej o tym, co to jest adres IP i port, możesz dowiedzieć się w wykładach, poświęconych budowie sieci.

Główne etapy pracy programu z socketami:

  1. Tworzenie socketu: Program tworzy socket, określając typ protokołu (TCP lub UDP).
  2. Powiązanie z adresem: Socket jest powiązany z adresem IP i numerem portu, aby być dostępnym dla połączeń lub wysyłania/odbierania danych.
  3. Nasłuchiwanie i ustanowienie połączenia (dla TCP):
    • Nasłuchiwanie: Socket po stronie serwera zostaje przełączony w tryb nasłuchiwania, czekając na przychodzące połączenia.
    • Ustanowienie połączenia: Klient inicjuje połączenie z serwerem. Serwer przyjmuje połączenie, tworząc nowy socket do komunikacji z klientem.
  4. Wymiana danych: Dane są przesyłane między klientem a serwerem. W przypadku TCP dane są przesyłane w niezawodnym porządku.
  5. Zamknięcie połączenia: Po zakończeniu wymiany danych połączenie się zamyka.

Zalety używania socketów:

  • Elastyczność: Sockety pozwalają aplikacjom wymieniać dane niezależnie od ich lokalizacji i platformy.
  • Wydajność: Sockety zapewniają szybki i efektywny sposób przesyłania danych, zwłaszcza w przypadku używania UDP.
  • Niezawodność (w przypadku TCP): Protokół TCP zapewnia niezawodne dostarczanie danych ze sprawdzeniem integralności i odzyskaniem utraconych pakietów.

7.2 Tworzenie serwera socket

Praca z socketami w Pythonie odbywa się za pomocą wbudowanego modułu socket, który zapewnia interfejs do niskopoziomowego programowania sieciowego.

Dzięki socketom można stworzyć socket-server — aplikację/obiekt, który będzie otrzymywać żądania od klientów i odpowiadać im. A także socket-client — aplikację/obiekt, który będzie wysyłać żądania do socket-serwera i otrzymywać od niego odpowiedzi.

Aby stworzyć socket-server, trzeba wykonać trzy czynności:

  1. Stworzyć obiekt socket-serwera.
  2. Powiązać (bind) go z jakimś IP i portem.
  3. Włączyć tryb nasłuchiwania (listen) przychodzących wiadomości.

Tak będzie wyglądał ten kod:


import socket

# Tworzenie socketu
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
# Powiązanie socketu z adresem i portem
server_socket.bind(('localhost', 12345))
    
# Nasłuchiwanie przychodzących połączeń
server_socket.listen(5)
print("Serwer oczekuje na połączenie...")

Tutaj socket.AF_INET oznacza, że używamy IPv4 dla protokołu sieciowego, a socket.SOCK_STREAM oznacza, że używamy TCP. Te parametry są najczęściej używane do tworzenia aplikacji sieciowych.

Po przyjściu przychodzącej wiadomości, trzeba wykonać jeszcze cztery czynności:

  1. Nawiązać (accept) połączenie z klientem.
  2. Odebrać (receive) żądanie (dane) od klienta.
  3. Wysłać (send) klientowi odpowiedź — również jakieś dane.
  4. Zamknąć (close) połączenie.

Tak będzie wyglądał ten kod:


# Akceptacja nowego połączenia
client_socket, client_address = server_socket.accept()
print(f"Połączenie nawiązane z {client_address}")

# Odbieranie danych od klienta
data = client_socket.recv(1024)
print(f"Otrzymano: {data.decode('utf-8')}")

# Wysyłanie danych do klienta
client_socket.sendall(b'Hello, client!')

# Zamknięcie połączenia z klientem
client_socket.close()

Metoda sendall() jest używana zamiast send(), ponieważ zapewnia, że wszystkie dane będą wysłane. Metoda send() może wysłać tylko część danych, jeśli bufor się zapełni.

Liczba 1024 w recv(1024) wskazuje maksymalną ilość bajtów, którą można otrzymać za jednym razem. To pomaga kontrolować ilość danych przetwarzanych w jednej operacji.

Kod z ostatniego przykładu zazwyczaj jest wykonywany w nieskończonej pętli — serwer przetwarza żądanie, potem czeka na nowe, potem je przetwarza, i tak bez przerwy.

Jeśli chcesz uruchomić go u siebie lub po prostu zobaczyć pełny przykład, to przedstawiam go tutaj:


import socket

# Tworzenie socketu
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        
# Powiązanie socketu z adresem i portem
server_socket.bind(('localhost', 12345))
        
# Nasłuchiwanie przychodzących połączeń
server_socket.listen(5)
print("Serwer oczekuje na połączenie...")
        
while True:
    # Akceptacja nowego połączenia
    client_socket, client_address = server_socket.accept()
    print(f"Połączenie nawiązane z {client_address}")
        
    # Odbieranie danych od klienta
    data = client_socket.recv(1024)
    print(f"Otrzymano: {data.decode('utf-8')}")
        
    # Wysyłanie danych do klienta
    client_socket.sendall(b'Hello, client!')
        
    # Zamknięcie połączenia z klientem
    client_socket.close()

7.3 Tworzenie klienta socket

Socket-serwer stworzyliśmy, teraz napiszmy swojego socket-klienta, który będzie komunikował się z serwerem i odbierał od niego dane w odpowiedzi.

Aby to zrobić, trzeba wykonać pięć czynności:

  1. Stworzyć obiekt socket-klienta.
  2. Nawiązać połączenie (connect) z adresem IP i portem naszego socket-serwera.
  3. Wysłać (send) wiadomość na serwer.
  4. Odebrać (receive) dane od serwera.
  5. Zamknąć (close) połączenie.

To w rzeczywistości prostsze niż się wydaje. Tak będzie wyglądał ten kod:


import socket

# Tworzenie socketu
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
# Nawiązanie połączenia z serwerem
client_socket.connect(('localhost', 12345))
    
# Wysyłanie danych na serwer
client_socket.sendall(b'Hello, server!')
    
# Odbieranie danych od serwera
data = client_socket.recv(1024)
print(f"Otrzymano od serwera: {data.decode('utf-8')}")
    
# Zamknięcie socketu
client_socket.close()

Jeśli po drugiej stronie nie ma uruchomionego socket-serwera lub połączenie zostało przerwane, to wystąpi wyjątek typu socket.error. Więc nie zapomnij obsługiwać wyjątków.

Na tym dziś kończymy pracę z socketami.

Podczas każdej pracy z siecią znowu i znowu będą pojawiały się hosty, porty, adresy IP, ustanawianie połączeń, nasłuchiwanie żądań i tym podobne. Dlatego zrozumienie tego, jak to działa głęboko wewnątrz, bardzo ułatwi ci życie i pomoże połączyć rozproszone wiedzę w jedną całość.

Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION