2014-02-09 19 views
1

我的问题类似于python - How select.select() works?。但是,那里的解决方案并不适合我,因为我没有打开()我的文件。相反,它是一个套接字。我找不到任何方法将它设置为在documentation中无缓冲。如何使用选择与Python ssl套接字缓冲?

我有一个glib mainloop(使用select),在那里我注册了socket的读取。由于socket.recv()要求我指定接收缓冲区大小,因此读取比读取套接字更少的字节并不少见。只要内核缓冲它们,这很好; select仍然会将套接字标记为“准备好读取”。但显然Python也有缓冲区。对于接近数据流末尾的大文件,recv()将读取其中的一部分,其余部分将被Python缓存并在我的套接字上选择不再触发,直到发送新数据为止。此时,在新数据之前收到“缺失”数据;没有数据丢失。

我的问题是:我该如何解决这个问题?有没有办法在套接字上禁用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会继续报告它。

+2

我只是检查['Modules/socketmodule.c',从'2485'的行](http://hg.python.org/cpython/file/3a1db0d2747e/Modules/socketmodule.c#l2485 )'sock_recv'被实现的地方,我可以向你保证python不*做任何缓冲。它会创建一个缓冲区,其大小正好是您用recv指定的大小,并且对系统调用recv的调用会跟踪剩余的字节并要求*表示多个字节,而不是更多(参见[this loop]( http://hg.python.org/cpython/file/3a1db0d2747e/Modules/socketmodule.c#l2439)) – Bakuriu

+0

我发现,使用SSL插座时,这是唯一的发生,并添加示例代码,显示问题。 –

+0

我的解决方案是增加缓冲区大小。我在代码中使用了'recv(4096)',但数据实际上是4726字节。将其更改为'recv(64 * 1024)'解决了缓冲和'select()'问题。 – Lekensteyn

回答

1

调查ssl来源,我发现了一个没有记录的函数,它做我想要的:pending()。它可以像这样使用:

#!/usr/bin/python 

import glib 
import socket 
import ssl 

def cb(fd, cond): 
    print('data: %s' % repr(s.read(1))) 
    while(s.pending()): 
     print('more 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() 

这就解决了这个问题。

+0

...为什么你要在函数名和参数之间加空格? Python有一个着名的*官方风格指南(参见[PEP8](http://www.python.org/dev/peps/pep-0008/))。当你的代码应该被其他人读取时(例如,在这个答案中),你至少应该遵循*。 – Bakuriu

+0

我不知道这个风格指南,所以它不像你想象的那么出名(说我的n = 1样本:-P)另外,“官方”部分意味着它是必须的样式在编写代码_for_ Python时使用,以包含在官方发行版中。如果没有,你可以做任何你喜欢的事情。我同意使用共同的风格是一个好主意,但它并不像声音那样强制。 –

+0

PEP8具有更广的范围,那么只是标准库。例如“这里提供的指导原则旨在提高代码的可读性,并使其在各种Python代码中保持一致*”。最后一句是指所有开源项目。事实上,像'pylint'或'pychecker'这样的工具用于做一些静态分析,还包括检查PEP8风格。不遵循PEP8时,PyCharm等IDE会自动显示警告。 PEP8 *是当你想向python社区展示代码时使用的*样式。 – Bakuriu