在 SSL 套接字上带有部分 recv() 的 python 的 select() 的行为

2022-01-19 00:00:00 python sockets ssl network-programming

问题描述

我创建了一个 SSL 套接字(服务器端)并将该套接字放入一个 select() 队列中.当套接字准备好"读取时,select() 会正确返回.

I have created a SSL socket (server side) and place the socket into a select() queue. The select() properly returns when the socket "is ready" to read.

然后我再接收 (1024) 个字节.在某些情况下,这将获得所有数据,在其他情况下可能不会.

I then recv(1024) bytes. Under some circumstances this will get all the data, in others it may not.

但是,如果套接字缓冲区中仍有数据(因为我没有全部接收),并且我再次将同一个套接字传递给 select(),它将 不会即使我知道那里有数据,也返回为准备好"阅读.

However, if there is still data in the socket buffer (because I didn't recv() it all), and I pass that same socket into select() again, it will not be returned as being "ready" to read even though I know there is data there.

我想我的问题实际上是从 select() 的角度确认准备阅读"的真正含义以及处理此问题的最佳方法是什么.考虑到我正在使用 select(),继续 recv() 直到 EWOULDBLOCK 看起来有点骇人听闻.

I suppose my question is really to confirm what "ready to be read" really means from select()'s perspective and what the best way to handle this would be. Continuing to recv() until EWOULDBLOCK seems sort of hack-ish given that I'm using select().

我是不是想错了?我意识到我可以使用更大的 recv 缓冲区,但总是有可能读取的内容比 recv 可以提取的更多——那么从 select() 中处理这个问题的正确"方法是什么?

Am I thinking about this incorrectly? I realize I could use a larger recv buffer, but there is always the possibility that there would be more to read than recv can pull -- so what is the "right" way to handle this coming out of a select()?

提前致谢.

正如评论中所指出的,我没有提到这是一个 SSL 服务器,显然 select() 在使用包装套接字时表现不同.

As noted in the comments, I neglected to mention that this is an SSL server and apparently select() behaves differently when using wrapped sockets.


解决方案

select 从操作系统内核的角度来看,只关心套接字的就绪情况.那就是检查一个套接字是否为 recv 做好了准备,而 select 只检查套接字缓冲区中是否有数据.但是使用 SSL 就不同了,因为它涉及到用户空间缓冲.

select cares only about the readiness of a socket from the view of the OS kernel. That is checking if a socket is ready for recv with select only checks if there are data in the socket buffer. But with SSL it is different since user space buffering is involved.

即使您只从 SSL 套接字读取几个字节,它也需要读取包含加密数据的完整 SSL 记录,解密完整记录,然后它才能返回您请求的几个字节.其余数据将缓存在用户空间中以供下次读取.但是,完整的 SSL 记录会从 OS 套接字缓冲区中删除,这意味着 select 可能不会向您显示仍有可用数据.

Even if you read only a few bytes from a SSL socket it needs to read the full SSL record which contains the encrypted data, decrypt the full record and then it can return the few bytes you've requested. The rest of the data will be buffered in user space for the next read. But, the full SSL record is removed from the OS socket buffer which means that select might not show you that there are still data available.

有两种方法可以解决这个问题.一种方法是使用 pending 方法找出仍有数据缓冲在用户空间中.另一种是始终以大块的形式 recv ,这样就不会在用户空间中缓冲任何数据.由于 SSL 记录的最大大小为 16k,并且每个 recv 只处理单个 SSL 记录(openssl SSL_read) 总是可以调用大小至少为 16384 的 recv.

There are two ways to work around this. One way is to use the pending method to find out of there are still data buffered in user space. The other is to always recv in huge chunks so that no data will be buffered in user space. Since the maximum size of a SSL record is 16k and each recv handles only a single SSL record (implementation detail in openssl SSL_read) it would work to always call recv with a size of at least 16384.

相关文章