Поиск…


Вступление

Многие языки программирования используют сокеты для связи между процессами или между устройствами. В этом разделе объясняется правильное использование модуля сокетов в Python для облегчения отправки и приема данных по общим сетевым протоколам.

параметры

параметр Описание
socket.AF_UNIX Разъем UNIX
socket.AF_INET IPv4
socket.AF_INET6 IPv6
socket.SOCK_STREAM TCP
socket.SOCK_DGRAM UDP

Отправка данных через UDP

UDP - протокол без установления соединения. Сообщения другим процессам или компьютерам отправляются без установления какого-либо соединения. Нет автоматического подтверждения, если ваше сообщение получено. UDP обычно используется в чувствительных к задержкам приложениях или в приложениях, отправляющих широковещательные широковещательные передачи.

Следующий код отправляет сообщение процессу, прослушивающему локальный порт 6667 с использованием UDP

Обратите внимание, что нет необходимости «закрывать» сокет после отправки, потому что UDP является без установления соединения .

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)) 

Получение данных через UDP

UDP - протокол без установления соединения. Это означает, что одноранговые сообщения, отправляющие сообщения, не требуют установления соединения перед отправкой сообщений. socket.recvfrom образом, socket.recvfrom возвращает кортеж ( msg [сообщение, полученное сокетом], addr [адрес отправителя])

UDP-сервер, использующий исключительно модуль 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))

Ниже приведена альтернативная реализация с использованием 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()

По умолчанию блок sockets . Это означает, что выполнение скрипта будет ждать, пока сокет получит данные.

Отправка данных через TCP

Отправка данных через Интернет стала возможной благодаря использованию нескольких модулей. Модуль сокетов обеспечивает низкоуровневый доступ к операциям операционной системы, отвечающим за отправку или получение данных от других компьютеров или процессов.

Следующий код отправляет байтовую строку b'Hello' на TCP-сервер, прослушивающий порт 6667 на локальном хосте и закрывает соединение по завершении:

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()

Выход сокета блокируется по умолчанию, это означает, что программа будет ждать в соединениях и отправлять вызовы до тех пор, пока действие не будет завершено. Для подключения это означает, что сервер фактически принимает соединение. Для отправки это означает только, что операционная система имеет достаточное пространство для хранения очереди для отправки данных позже.

Розетки всегда должны быть закрыты после использования.

Многопоточный сервер TCP Socket

При запуске без аргументов эта программа запускает сервер сокетов TCP, который прослушивает подключения к 127.0.0.1 на порту 5000 . Сервер обрабатывает каждое соединение в отдельном потоке.

При запуске с аргументом -c эта программа подключается к серверу, считывает список клиентов и распечатывает его. Список клиентов переносится как строка JSON. Имя клиента может быть указано путем передачи аргумента -n . Передавая разные имена, можно наблюдать влияние на список клиентов.

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()

Выход сервера

$ python client_list.py
Starting server...

Выход клиента

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

Буферы приема ограничены 1024 байтами. Если строковое представление JSON списка клиентов превышает этот размер, оно будет усечено. Это приведет к возникновению следующего исключения:

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

Необработанные сокеты в Linux

Сначала вы отключите автоматическое контрольное суммирование вашей сетевой карты:

sudo ethtool -K eth1 tx off

Затем отправьте свой пакет, используя гнездо 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
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow