Recv: примеры (PYTHON)
recv(bufsize, flags): bytesОсновные сведения о методе recv
Метод recv объекта сокета в Python используется для получения данных из сетевого соединения. Этот метод применяется в клиент-серверных приложениях, сетевых утилитах и системах обмена сообщениями, где необходимо читать информацию, отправленную удаленной стороной.
Сигнатура метода: socket.recv(bufsize[, flags]).
Аргументы:
- bufsize: целое число, определяющее максимальное количество байтов для приема за один вызов. Это обязательный аргумент.
- flags (необязательный): целое число, управляющее поведением операции приема. Значение по умолчанию - 0. Флаги могут комбинироваться с помощью побитового ИЛИ (|). Распространенные флаги:
- socket.MSG_PEEK: просмотр данных в буфере без их удаления.
- socket.MSG_WAITALL: ожидание полного заполнения буфера до достижения заданного размера (может не поддерживаться на всех системах).
- socket.MSG_DONTWAIT: неблокирующий режим операции (эквивалентно установке socket.setblocking(0)).
Возвращаемое значение: Метод возвращает объект bytes, содержащий полученные данные. Если соединение закрыто корректно, возвращается пустой bytes объект (b''). В случае ошибки возникает исключение (например, socket.timeout, ConnectionResetError).
Простые примеры использования
Пример 1: Базовый прием данных
import socket
# Создание сокета и подключение
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('example.com', 80))
sock.send(b'GET / HTTP/1.0\r\n\r\n')
# Прием данных с буфером 4096 байт
data = sock.recv(4096)
print(f'Получено {len(data)} байт')
sock.close()Получено 1460 байт
Пример 2: Использование флага MSG_PEEK
import socket
# Создание пары соединенных сокетов
sock1, sock2 = socket.socketpair()
sock1.send(b'Hello World')
# Просмотр данных без их извлечения из буфера
peek_data = sock2.recv(1024, socket.MSG_PEEK)
print('Просмотренные данные:', peek_data)
# Фактический прием данных (те же данные)
actual_data = sock2.recv(1024)
print('Фактические данные:', actual_data)
sock1.close()
sock2.close()Просмотренные данные: b'Hello World' Фактические данные: b'Hello World'
Пример 3: Использование флага MSG_WAITALL
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('example.com', 80))
sock.send(b'GET / HTTP/1.0\r\n\r\n')
# Ожидание получения ровно 100 байт (может зависнуть)
try:
sock.settimeout(5.0)
data = sock.recv(100, socket.MSG_WAITALL)
print(f'Получено ровно {len(data)} байт')
except socket.timeout:
print('Таймаут: не удалось получить достаточно данных')
finally:
sock.close()Получено ровно 100 байт
Похожие функции в Python
В модуле socket существуют другие методы для приема данных, которые отличаются поведением:
- recv_into(buffer): Принимает данные непосредственно в предоставленный буфер (объект, поддерживающий buffer protocol). Эффективен для минимизации выделения памяти при многократных вызовах.
- recvfrom(bufsize[, flags]): Используется с датаграммными сокетами (SOCK_DGRAM). Возвращает кортеж (данные, адрес_отправителя).
- recvmsg() и recvmsg_into(): Позволяют получать вспомогательные данные (ансиллярные сообщения) вместе с основными, что полезно для расширенных протоколов.
Выбор метода зависит от типа сокета (потоковый или датаграммный) и требований к производительности. recv_into предпочтителен для высоконагруженных приложений.
Типичные ошибки и их решение
1. Бесконечная блокировка: Вызов recv без таймаута на сокете, где данные никогда не поступят.
import socket
sock = socket.socket()
sock.bind(('127.0.0.1', 0))
sock.listen(1)
conn, addr = sock.accept() # Ждет подключения
# Если клиент не отправит данные, recv заблокируется навсегда
# data = conn.recv(1024) # Риск бесконечной блокировкиРешение: Установить таймаут.
conn.settimeout(10.0)
try:
data = conn.recv(1024)
except socket.timeout:
print('Таймаут приема данных')2. Неверная интерпретация пустого результата: Пустой bytes объект (b'') означает закрытие соединения, а не просто отсутствие данных.
data = sock.recv(1024)
if not data: # Верно
print('Соединение закрыто')
# if data == b'': # Аналогичная проверка3. Предположение о полном приеме сообщения: recv может вернуть меньше данных, чем указано в bufsize.
# ОШИБОЧНЫЙ ПОДХОД
sock.send(b'HelloWorld') # Отправлено 10 байт
# На принимающей стороне:
data = sock.recv(10) # Может вернуть только часть, например, 5 байт
print(data) # Возможно, b'Hello'Решение: Прием в цикле до получения нужного количества байт.
def recv_all(sock, n):
data = b''
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
raise ConnectionError("Соединение прервано")
data += packet
return dataИзменения в последних версиях Python
Основное поведение метода recv остается стабильным на протяжении многих версий Python 3. Однако, есть связанные изменения:
- В Python 3.5 улучшена поддержка неблокирующих сокетов и введены более четкие исключения (например, BlockingIOError).
- В Python 3.7 добавлена возможность использования socket.MSG_DONTWAIT как альтернатива вызову setblocking(False).
- В Python 3.10 и выше продолжаются оптимизации внутренней реализации сетевых операций, но API метода recv не меняется.
Рекомендуется использовать последние версии Python для улучшенной производительности и безопасности сетевых операций.
Расширенные примеры использования
Пример 1: Прием данных переменной длины с заголовком
import socket
import struct
def receive_message(sock):
# Сначала принимаем 4 байта, указывающие длину сообщения
raw_len = recv_all(sock, 4)
if not raw_len:
return None
msg_len = struct.unpack('>I', raw_len)[0]
# Принимаем само сообщение
return recv_all(sock, msg_len)
def recv_all(sock, n):
data = b''
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
return None
data += packet
return data
# Пример использования на сервере
server_sock = socket.socket()
server_sock.bind(('localhost', 12345))
server_sock.listen()
conn, addr = server_sock.accept()
message = receive_message(conn)
print(f'Получено сообщение длиной {len(message)}')
conn.close()Получено сообщение длиной 128
Пример 2: Неблокирующий прием с селектором
import socket
import selectors
import types
sel = selectors.DefaultSelector()
def accept(sock, mask):
conn, addr = sock.accept()
conn.setblocking(False)
sel.register(conn, selectors.EVENT_READ, read)
def read(conn, mask):
try:
data = conn.recv(1024, socket.MSG_DONTWAIT)
if data:
print(f'Получены данные: {data}')
else:
sel.unregister(conn)
conn.close()
except BlockingIOError:
pass # Данных пока нет
server = socket.socket()
server.bind(('localhost', 12346))
server.listen()
server.setblocking(False)
sel.register(server, selectors.EVENT_READ, accept)
# Основной цикл событий
while True:
events = sel.select(timeout=1)
for key, mask in events:
callback = key.data
callback(key.fileobj, mask)Пример 3: Использование recv_into для снижения нагрузки на сборщик мусора
import socket
import time
# Создаем буфер один раз и переиспользуем его
buffer = bytearray(4096)
mv = memoryview(buffer)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('example.com', 80))
sock.send(b'GET / HTTP/1.0\r\n\r\n')
total_received = 0
while True:
# Принимаем данные непосредственно в memoryview
nbytes = sock.recv_into(mv[total_received:])
if nbytes == 0:
break
total_received += nbytes
if total_received >= 4096:
print('Буфер заполнен')
break
print(f'Всего получено {total_received} байт')
# Данные доступны в buffer[:total_received]
sock.close()Всего получено 1460 байт
Аналоги в других языках программирования
JavaScript (Node.js): Метод socket.read() или событие 'data' на потоке.
// Node.js пример
const net = require('net');
const client = net.createConnection({ port: 8080 }, () => {
console.log('Подключено');
});
client.on('data', (data) => {
console.log(`Получено: ${data}`);
});Java: Методы read() класса InputStream, связанного с сокетом.
// Java пример
Socket socket = new Socket("example.com", 80);
InputStream in = socket.getInputStream();
byte[] buffer = new byte[1024];
int bytesRead = in.read(buffer);
System.out.println("Получено " + bytesRead + " байт");
socket.close();Go: Метод Read() для соединения net.Conn.
// Go пример
conn, err := net.Dial("tcp", "example.com:80")
if err != nil { log.Fatal(err) }
buffer := make([]byte, 1024)
n, err := conn.Read(buffer)
fmt.Printf("Получено %d байт\n", n)
conn.Close()C#: Метод Socket.Receive().
// C# пример
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect("example.com", 80);
byte[] buffer = new byte[1024];
int received = socket.Receive(buffer);
Console.WriteLine($"Получено {received} байт");
socket.Close();В отличие от Python, во многих языках используется интерфейс потока ввода-вывода, а не прямой метод сокета.