我不明白像print语句这样的东西是如何影响套接字行为的。以下代码在windows上运行,来自idle和cmd。
server.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)client.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发布于 2019-09-19 04:00:13
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,如果所有数据都已发送,则立即禁用它。
https://stackoverflow.com/questions/57999637
复制相似问题