首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么套接字行为依赖于打印语句?

为什么套接字行为依赖于打印语句?
EN

Stack Overflow用户
提问于 2019-09-19 03:29:56
回答 1查看 35关注 0票数 1

我不明白像print语句这样的东西是如何影响套接字行为的。以下代码在windows上运行,来自idle和cmd。

server.py:

代码语言:javascript
复制
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:

代码语言:javascript
复制
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')的情况下运行时,我得到

代码语言:javascript
复制
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

如果没有它

代码语言:javascript
复制
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
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-09-19 04:00:13

代码语言:javascript
复制
events = selectors.EVENT_READ | selectors.EVENT_WRITE
...
sel.register(connected_sock, events, data=data)

这使得只要套接字是可写的,就会触发select --不管您是否真的有要用data.outb编写的内容。这意味着只要套接字还没有关闭,它就会触发。

代码语言:javascript
复制
ls.connect((HOST, PORT))
ls.send(b'Hi world')

这将连接到服务器。连接将在操作系统内核中完成,最终将触发程序中的服务器套接字,然后调用accept。但是,在服务器内核中建立连接之后,connect实际上会在客户机中返回,这可能是在调用accept之前。因此,send将向新创建的套接字提供已经具有数据的套接字。

代码语言:javascript
复制
def service_connection(key, event):
    ...
    if event & selectors.EVENT_READ:
        ...
    elif event & selectors.EVENT_WRITE:
        ...

即使套接字是可读和可写的,您的服务器也只能处理套接字中的一个条件。如上所述,它始终是可写的,但您首先会遇到可读性,因为一旦完成accept,来自客户端的数据就已经在服务器端的套接字缓冲区中了。因此,只有在data.outb中已经存在来自前一个EVENT_READ的数据时,才会处理第一个EVENT_WRITE

代码语言:javascript
复制
        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 ),因为客户端在收到数据后立即关闭连接:

代码语言:javascript
复制
ls.recv(1024)
ls.close()

请注意,由于您在同一台计算机上使用客户端和服务器,因此只有这样才能执行此操作。如果它们在不同的机器上,那么网络延迟本身就会增加足够的时间差异,因此您可能会看到不同但可能类似的令人困惑的行为。

处理这种情况的正确方法当然是只有在data.outb中实际有数据时才启用EVENT_WRITE,如果所有数据都已发送,则立即禁用它。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57999637

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档