Python SSL-数据大于1400时recv被偏移

时间:2018-09-02 16:46:18

标签: python-3.x ssl buffer

我正在Python中使用ssl模块,并且似乎在缓冲区中遇到了一个小问题。

我具有以下过程来处理来自套接字的数据,并且我还基于this问题使用了挂起添加了while循环,但是并没有解决问题。我也加大了缓冲区的大小,无济于事。

RECV_BUFFER = 131072
def handle(client_socket):
    try:
        rxdata = client_socket.recv(RECV_BUFFER)
        if rxdata:
                print("Rx: " + rxdata.decode())
                while(client_socket.pending()):
                    rxdata = client_socket.recv(RECV_BUFFER)
                    sys.stdout.write(rxdata.decode())
    except Exception as e:
        print("Exception: " + str(e))

出于测试目的,我设置了用户输入,以便可以直接进行测试。 GET /返回“ Hello World”,而GET /other返回一个长字符串。每次缓冲区溢出时,返回值都会偏移一,如下所示。

Command>GET /
Tx: GET /
Rx: HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
X-Cloud-Trace-Context: a65f614b75674fa723b7d69c1af03a0e;o=1
Date: Sun, 02 Sep 2018 16:00:19 GMT
Server: My Frontend
Content-Length: 12

Hello World!
Command>GET /other
Tx: GET /other
Rx: HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
X-Cloud-Trace-Context: 90033f7e308e07508106359c3e7c76d1
Date: Sun, 02 Sep 2018 16:00:23 GMT
Server: My Frontend
Content-Length: 1924

This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. T
Command>GET /
Tx: GET /
Rx: his is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. This is something else. End.
Command>GET /other
Tx: GET /other
Rx: HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
X-Cloud-Trace-Context: 160b0cd5f80982bf1e7ab7dd5d94996d
Date: Sun, 02 Sep 2018 16:00:26 GMT
Server: My Frontend
Content-Length: 12

Hello World!

这是怎么回事,应该如何解决?

2 个答案:

答案 0 :(得分:1)

我不确定您要做什么,但是我认为您的服务器基本上是这样工作的:

  1. 读取命令(一行)。
  2. 立即发送完整回复。

鉴于您正在使用pending,它仅检查SSL套接字中是否仍然还有解密的数据,我想是您假设如果数据是由服务器在单个sent中发送的,则客户端也会立即读取它。但这种情况并非如此。实际发生的事情是这样的:

  1. 服务器发送大量时间,例如20000字节。
  2. 在SSL级别上,至少有两个SSL记录,因为单个记录的大小只能为16384。因此,假设它将记录16384,其余记录(3616字节)。
  3. li>
  4. ssl_socket.revc(RECV_BUFFER)至少将从底层TCP连接读取尽可能多的数据,因为它需要具有完整的SSL记录。然后它将解密SSL记录并返回最多RECV_BUFFER个字节的解密数据。
  5. ssl_socket.pending()会告诉您SSL套接字中是否还有未读的解密数据。它不会检查基础TCP套接字处是否有可用数据。如果SSL套接字中仍有数据,则下一个ssl_socket.recv(...)将从这些数据中返回,但不会尝试从基础TCP套接字中读取更多数据。仅当SSL套接字中不再有已解密但未读取的数据可用时,recv才会从基础TCP套接字中读取更多内容-但是在这种情况下,pending将返回false,因此您将永远不会尝试读取更多数据。

这意味着可能会发生,只有第一条SSL记录被读取,解密并返回到您的recv内部。因此,如果您发送下一条命令,您将不会获得新的响应,但是实际上您将从前一个请求中读取剩余的响应数据。

为了修正代码,您需要修正您的假设:SSL需要像数据流一样对待,而不要像消息协议(对于TCP一样)对待。这意味着您不能假定消息已被完整读取,并且消息将被完整返回,或者在SSL对象中它至少已经被完整存储。相反,您要么需要预先知道响应的大小(例如在响应前面加上一个长度),要么需要有一些明确的标记以确认响应已结束并读取直到该标记。

答案 1 :(得分:0)

这是我确定的最终解决方案。我觉得这是以前发布的更正确的解决方案。通过指定True作为第二个参数,它还具有剥离标题或保留位置的选项:

def handle(client_socket, raw=False):
    data = client_socket.recv()
    reCL = re.search('Content-Length: (\d+)', data.decode(), re.MULTILINE)
    contentLength = int(reCL.group(1))
    contentLengthEndChar = reCL.end()+4
    dataSize = contentLength 
    if raw == True: dataSize += contentLengthEndChar
    sslRecordPending = math.ceil(dataSize / 16384) - 1 #SSL records left; not used
    socket_active = True
    rxdata = b''
    if raw == True: rxdata = data[:contentLengthEndChar]
    rxdata += data[contentLengthEndChar:]
    while True:
        try:
            if len(rxdata) == dataSize: break
            rxdata += client_socket.recv()
        except socket.timeout:
            break
    return rxdata.decode()