Szukaj…


Wprowadzenie

Wiele języków programowania używa gniazd do komunikacji między procesami lub między urządzeniami. W tym temacie wyjaśniono prawidłowe użycie modułu gniazd w Pythonie w celu ułatwienia wysyłania i odbierania danych za pośrednictwem popularnych protokołów sieciowych.

Parametry

Parametr Opis
gniazdo.AF_UNIX Gniazdo UNIX
gniazdo.AF_INET IPv4
gniazdo.AF_INET6 IPv6
gniazdo.SOCK_STREAM TCP
gniazdo.SOCK_DGRAM UDP

Wysyłanie danych przez UDP

UDP to protokół bezpołączeniowy. Wiadomości do innych procesów lub komputerów są wysyłane bez nawiązywania jakiegokolwiek połączenia. Nie ma automatycznego potwierdzenia, czy wiadomość została odebrana. UDP jest zwykle używany w aplikacjach wrażliwych na opóźnienia lub w aplikacjach wysyłających transmisje w całej sieci.

Poniższy kod wysyła komunikat do procesu nasłuchującego na porcie 66 hosta lokalnego przy użyciu protokołu UDP

Zauważ, że nie ma potrzeby „zamykania” gniazda po wysłaniu, ponieważ UDP jest bezpołączeniowy .

from socket import socket, AF_INET, SOCK_DGRAM
s = socket(AF_INET, SOCK_DGRAM)
msg = ("Hello you there!").encode('utf-8')  # socket.sendto() takes bytes as input, hence we must encode the string first.
s.sendto(msg, ('localhost', 6667)) 

Odbieranie danych przez UDP

UDP to protokół bezpołączeniowy. Oznacza to, że uczestnicy wysyłający wiadomości nie wymagają ustanowienia połączenia przed wysłaniem wiadomości. socket.recvfrom zwraca zatem krotkę ( msg [wiadomość, którą otrzymało gniazdo], addr [adres nadawcy])

Serwer UDP wykorzystujący wyłącznie moduł socket :

from socket import socket, AF_INET, SOCK_DGRAM
sock = socket(AF_INET, SOCK_DGRAM)
sock.bind(('localhost', 6667))

while True:
    msg, addr = sock.recvfrom(8192)  # This is the amount of bytes to read at maximum
    print("Got message from %s: %s" % (addr, msg))

Poniżej znajduje się alternatywna implementacja wykorzystująca socketserver.UDPServer :

from socketserver import BaseRequestHandler, UDPServer

class MyHandler(BaseRequestHandler):
    def handle(self):
        print("Got connection from: %s" % self.client_address)
        msg, sock = self.request
        print("It said: %s" % msg)
        sock.sendto("Got your message!".encode(), self.client_address) # Send reply

serv = UDPServer(('localhost', 6667), MyHandler)
serv.serve_forever()

Domyślnie sockets blokują. Oznacza to, że wykonanie skryptu będzie czekać, aż gniazdo odbierze dane.

Wysyłanie danych przez TCP

Przesyłanie danych przez Internet jest możliwe dzięki wielu modułom. Moduł gniazd zapewnia niski poziom dostępu do podstawowych operacji systemu operacyjnego odpowiedzialnych za wysyłanie lub odbieranie danych z innych komputerów lub procesów.

Poniższy kod wysyła ciąg bajtów b'Hello' do serwera TCP nasłuchującego na porcie 6667 na hoście lokalnym hosta i zamyka połączenie po zakończeniu:

from socket import socket, AF_INET, SOCK_STREAM
s = socket(AF_INET, SOCK_STREAM)
s.connect(('localhost', 6667))  # The address of the TCP server listening
s.send(b'Hello')
s.close()

Wyjście gniazda jest domyślnie blokowane, co oznacza, że program będzie czekał na połączenie i wysyła połączenia, aż akcja zostanie „zakończona”. W przypadku połączenia oznacza to, że serwer faktycznie przyjmuje połączenie. W przypadku wysyłania oznacza to tylko, że system operacyjny ma wystarczającą ilość miejsca w buforze, aby ustawić w kolejce dane do wysłania później.

Gniazda należy zawsze zamykać po użyciu.

Wielowątkowy serwer gniazd TCP

Po uruchomieniu bez argumentów program uruchamia serwer gniazd TCP, który nasłuchuje połączeń z 127.0.0.1 na porcie 5000 . Serwer obsługuje każde połączenie w osobnym wątku.

Po uruchomieniu z argumentem -c program łączy się z serwerem, odczytuje listę klientów i drukuje ją. Lista klientów jest przesyłana jako ciąg JSON. Nazwę klienta można podać, przekazując argument -n . Po podaniu różnych nazw można zaobserwować wpływ na listę klientów.

client_list.py

import argparse
import json
import socket
import threading

def handle_client(client_list, conn, address):
    name = conn.recv(1024)
    entry = dict(zip(['name', 'address', 'port'], [name, address[0], address[1]]))
    client_list[name] = entry
    conn.sendall(json.dumps(client_list))
    conn.shutdown(socket.SHUT_RDWR)
    conn.close()

def server(client_list):
    print "Starting server..."
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind(('127.0.0.1', 5000))
    s.listen(5)
    while True:
        (conn, address) = s.accept()
        t = threading.Thread(target=handle_client, args=(client_list, conn, address))
        t.daemon = True
        t.start()

def client(name):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('127.0.0.1', 5000))
    s.send(name)
    data = s.recv(1024)
    result = json.loads(data)
    print json.dumps(result, indent=4)

def parse_arguments():
    parser = argparse.ArgumentParser()
    parser.add_argument('-c', dest='client', action='store_true')
    parser.add_argument('-n', dest='name', type=str, default='name')
    result = parser.parse_args()
    return result

def main():
    client_list = dict()
    args = parse_arguments()
    if args.client:
        client(args.name)
    else:
        try:
            server(client_list)
        except KeyboardInterrupt:
            print "Keyboard interrupt"

if __name__ == '__main__':
    main()

Dane wyjściowe serwera

$ python client_list.py
Starting server...

Dane wyjściowe klienta

$ python client_list.py -c -n name1
{
    "name1": {
        "address": "127.0.0.1", 
        "port": 62210, 
        "name": "name1"
    }
}

Bufory odbiorcze są ograniczone do 1024 bajtów. Jeśli reprezentacja ciągu JSON listy klientów przekroczy ten rozmiar, zostanie obcięta. Spowoduje to zgłoszenie następującego wyjątku:

ValueError: Unterminated string starting at: line 1 column 1023 (char 1022)

Surowe gniazda w systemie Linux

Najpierw wyłącz automatyczne sumowanie kontrolne karty sieciowej:

sudo ethtool -K eth1 tx off

Następnie wyślij pakiet, używając gniazda SOCK_RAW:

#!/usr/bin/env python
from socket import socket, AF_PACKET, SOCK_RAW
s = socket(AF_PACKET, SOCK_RAW)
s.bind(("eth1", 0))

# We're putting together an ethernet frame here, 
# but you could have anything you want instead
# Have a look at the 'struct' module for more 
# flexible packing/unpacking of binary data
# and 'binascii' for 32 bit CRC
src_addr = "\x01\x02\x03\x04\x05\x06"
dst_addr = "\x01\x02\x03\x04\x05\x06"
payload = ("["*30)+"PAYLOAD"+("]"*30)
checksum = "\x1a\x2b\x3c\x4d"
ethertype = "\x08\x01"

s.send(dst_addr+src_addr+ethertype+payload+checksum)


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow