Вопрос-Ответ

Python socket not receiving without sending

Сокет Python не получает без отправки

Я скопировал пример echo server из документации python, и он работает нормально. Но когда я редактирую код, чтобы он не отправлял данные обратно клиенту, метод socket.recv() не возвращает результат при повторном вызове.

import socket

HOST = ''
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)

conn, addr = s.accept()
print('Connected by', addr)

while True:
data = conn.recv(1024)
if not data: break
conn.sendall(b'ok')
conn.close()

В исходной версии из документации python цикл while немного отличается:

while True:
data = conn.recv(1024)
if not data: break
conn.sendall(data)

Код клиента:

import socket

HOST = 'localhost'
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall(b'Hello, world')
data = s.recv(1024)
s.close()
print('Received', repr(data))
Переведено автоматически
Ответ 1

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

С вашим изменением правила изменились. Теперь сервер продолжает получать и отбрасывать данные до тех пор, пока клиент не закроет входящую сторону соединения. Затем сервер отправляет "ok" и закрывает сокет.

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

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

сервер

import socket

HOST = ''
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen(1)

conn, addr = s.accept()
print('Connected by', addr)

while True:
data = conn.recv(1024)
if not data: break
conn.sendall(b'ok')
conn.shutdown(socket.SHUT_WR)
conn.close()

клиент

import socket

HOST = 'localhost'
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall(b'Hello, world')
s.shutdown(socket.SHUT_WR)
data = b''
while True:
buf = s.recv(1024)
if not buf:
break
data += buf
s.close()
print('Received', repr(data))
Ответ 2

Количество операций приема и отправки должно совпадать, потому что они блокируют. Это блок-схема для вашего кода:

Server listen 
Client connect
Server receive (this waits until a message arrives at the server) [1]
Client send 'Hello world' (received by [1])
Server receive (because there was data received) [2]
Client receive [3]

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

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

2023-12-17 08:50 python