【发布时间】:2023-03-31 10:46:01
【问题描述】:
我的问题类似于 python - How select.select() works? 。但是,那里的解决方案对我不起作用,因为我没有打开()我的文件。相反,它是一个套接字。我在documentation 中找不到任何方法将其设置为无缓冲。
我有一个 glib 主循环(它使用 select),我在其中注册了要读取的套接字。因为 socket.recv() 需要我指定一个接收缓冲区大小,所以读取的字节数少于套接字读取的字节数并不少见。只要内核缓冲它们就可以了; select 仍会将套接字标记为“准备读取”。但显然 Python 也有一个缓冲区。对于大文件,在数据流的末尾附近,recv() 将读取其中的一部分,其余部分将由 Python 缓冲,并且 select 不再在我的套接字上触发,直到发送新数据。此时,“丢失”的数据在新数据之前被接收;没有数据丢失。
我的问题是:我该如何解决这个问题?有没有办法在套接字上禁用 Python 的缓冲区?如果没有,有没有办法检查缓冲区是否为空,所以我可以确保在回调之前我不会从回调中返回?
编辑:
如评论中所述,Python 不会向套接字添加额外的缓冲区,因此这不是问题所在。我无法为该问题创建一个最小示例。但是,似乎它可能与使用 ssl 套接字有关。我忘记了我使用了加密连接;禁用加密似乎可以解决这个问题,但我不能接受。所以上面的问题仍然存在,注意缓冲区可能是在 ssl 模块中实现的。
显示问题的示例代码:
#!/usr/bin/python
import glib
import socket
import ssl
def cb (fd, cond):
print ('data: %s' % repr (s.read (1)))
return True
s = ssl.wrap_socket (socket.create_connection (('localhost', 1234)))
glib.io_add_watch (s.fileno (), glib.IO_IN, cb)
glib.MainLoop ().run ()
然后运行一个服务器
openssl s_server -accept 1234 -key file.key -cert file.crt
运行 python 程序将建立连接。发送多于一个字节的数据将使程序只打印第一个字节;当发送更多字节时,首先读取剩余的块,然后是第一个新字节,然后再次等待。这很容易理解:只要ssl缓冲区有数据,新字节就不会从内核缓冲区中读取,所以select会继续报。
【问题讨论】:
-
我刚刚检查了
Modules/socketmodule.c, lines from2485下的代码,其中实现了sock_recv,我可以向您保证python 不 做任何缓冲。它会创建一个恰好大小的缓冲区,大小与您使用recv指定的大小相同,并且对系统调用recv的调用会跟踪剩余字节并要求 个字节,而不是更多(见this loop) -
我发现只有在使用 ssl 套接字时才会发生这种情况,并添加了显示问题的示例代码。
-
我的解决方案是增加缓冲区大小。我的代码中有
recv(4096),但数据实际上是4726字节。将其更改为recv(64 * 1024)解决了缓冲与select()的问题。 -
@Lekensteyn:我认为这是一种解决方法,而不是解决方案。 ;-) 如果您事先知道数据的大小,它会起作用,但看起来非常脆弱。如果您的数据包大小稍后更改并且您忘记更新它,您将很难弄清楚为什么它不再起作用。由于问题和解决方案现在已知(并且很简单),我建议使用它。
-
@Lekensteyn:我说的是docs.python.org/2.7/library/ssl.html。我刚刚提交了一个添加它的错误报告,感谢您的建议。
标签: python sockets select ssl buffer