我不明白像打印语句这样的东西会如何影响套接字行为。以下代码在 Windows 上从空闲和 cmd 运行。
服务器.py:
from types import SimpleNamespace
import socket
import selectors
HOST = '127.0.0.1'
PORT = 65433
sel = selectors.DefaultSelector()
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_sock.bind((HOST, PORT))
server_sock.listen()
print('Server listening on', (HOST, PORT))
server_sock.setblocking(False)
sel.register(server_sock, selectors.EVENT_READ, data=None)
def accept_new_connection(server_sock):
connected_sock, addr = server_sock.accept()
connected_sock.setblocking(False)
events = selectors.EVENT_READ | selectors.EVENT_WRITE
data = SimpleNamespace(addr = addr, outb = b'', inb = b'')
sel.register(connected_sock, events, data=data)
print('new connection', addr, 'accepted')
def service_connection(key, event):
connected_sock = key.fileobj
data = key.data
if event & selectors.EVENT_READ:
recv_data = connected_sock.recv(1024)
if data:
data.outb += recv_data
else:
print('closing the connection', data.addr)
sel.unregister(connected_sock)
connected_sock.close()
elif event & selectors.EVENT_WRITE:
if data.outb:
print('echoing', data.outb, 'to', data.addr)
sent = connected_sock.send(data.outb)
data.outb = data.outb[sent:]
print('ANYTHING') # removing this changes socket behaviour
else:
# this executes if print('ANYTHING') is not present
print('nothing to send')
while True:
events = sel.select(timeout=None)
for key, event in events:
if key.data == None:
accept_new_connection(key.fileobj)
else:
service_connection(key, event)
客户端.py:
import socket
HOST = '127.0.0.1'
PORT = 65433
ls = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ls.connect((HOST, PORT))
ls.send(b'Hi world')
ls.recv(1024)
ls.close()
运行这两个程序(首先是服务器,然后是客户端)
产生不同的结果,取决于是否
print('ANYTHING')
存在。
当包含 print('ANYTHING')
运行时,
我明白了
Server listening on ('127.0.0.1', 65433)
new connection ('127.0.0.1', 58675) accepted
echoing b'Hi world' to ('127.0.0.1', 58675)
ANYTHING
没有它
Server listening on ('127.0.0.1', 65433)
new connection ('127.0.0.1', 58678) accepted
echoing b'Hi world' to ('127.0.0.1', 58678)
nothing to send
最佳答案
events = selectors.EVENT_READ | selectors.EVENT_WRITE
...
sel.register(connected_sock, events, data=data)
只要套接字可写,这就会触发 select - 无论您是否确实有东西要在 data.outb
中写入。这意味着只要套接字尚未关闭,它就会始终触发。
ls.connect((HOST, PORT))
ls.send(b'Hi world')
这将连接到服务器。连接将在操作系统内核内部完成,并最终触发程序中的服务器套接字,然后调用 accept
。但是,connect
实际上是在服务器内核中建立连接之后(可能是在调用 accept
之前)在客户端中返回的。因此,send
将为新创建的套接字提供已有的数据。
def service_connection(key, event):
...
if event & selectors.EVENT_READ:
...
elif event & selectors.EVENT_WRITE:
...
即使套接字既可读又可写,您的服务器仅处理套接字上的单个条件。如上所述,它始终是可写的,但首先会遇到可读的情况,因为一旦完成 accept
,来自客户端的数据就已经在服务器端的套接字缓冲区中。因此,只有当 data.outb
中已有前一个 EVENT_READ
的数据时,才会处理第一个 EVENT_WRITE
。
sent = connected_sock.send(data.outb)
data.outb = data.outb[sent:]
print('ANYTHING') # removing this changes socket behaviour
这会将数据发送到客户端,然后给服务器增加一些延迟,因为打印到终端实际上需要一些时间。如果不存在这种延迟(即没有print
),那么选择器将立即再次被调用(因为EVENT_WRITE
总是触发,见上文),并且您的代码将偶然发现空的data.outb
.
如果存在这种延迟,则套接字将被关闭(因此不再触发 EVENT_WRITE
),因为客户端在收到数据后立即关闭连接:
ls.recv(1024)
ls.close()
请注意,只有在同一台计算机上使用客户端和服务器时,才会出现这种情况。如果它们位于不同的计算机上,那么网络延迟会自行增加足够的时间差异,因此您可能会看到不同但可能相似的令人困惑的行为。
处理这种情况的正确方法当然是仅当 data.outb
中确实有数据时才启用 EVENT_WRITE
,并在发送所有数据后立即禁用它。
关于python - 为什么套接字行为依赖于打印语句?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57999637/