Python Language
Розетки
Поиск…
Вступление
Многие языки программирования используют сокеты для связи между процессами или между устройствами. В этом разделе объясняется правильное использование модуля сокетов в 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)