Python Requests拆分TCP数据包

时间:2018-05-07 11:34:49

标签: python tcp python-requests

我正在尝试使用python脚本编写HTTP POST请求。

当用bash的curl尝试它时,一切正常。使用python,使用requestsurllib3 - 库,我收到来自API的错误响应。 POST请求包含标题中的信息以及请求正文中的json。

我注意到,当我使用Wireshark拦截数据包时,curl-request(正在工作)是一个长度为374字节的数据包。 python请求(这里requestsurllib3之间没有区别)被分成2个253和144字节长度的独立数据包。

enter image description here

Wireshark重新组装这些没有问题,它们似乎都包含标题和POST正文中的完整信息。但我试图连接到answeres的API有一个不太有用的“处理请求时出错”。

由于253字节不能成为TCP数据包的限制,这种行为的原因是什么?有办法解决这个问题吗?

编辑:

击:

curl 'http://localhost/test.php' -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36' -H 'Content-Type: application/json' -d '{"key1":"value1","key2":"value2","key3":"value3"}'

蟒:

import requests, json

headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36",
"Content-Type": "application/json"}

data = {"key1":"value1", "key2":"value2", "key3":"value3"}

r=requests.post("http://localhost/test.php", headers=headers, data=json.dumps(data))

3 个答案:

答案 0 :(得分:1)

TCP是一种数据流,而不是一系列消息。将数据流分段为数据包应与数据流的解释无关,无论是发送方还是接收方。如果收件人实际上根据数据包的分段行为不同,则收件人会被破坏。

虽然我已经看到过这样破碎的系统,但我看到更多系统因为不同的原因而不喜欢请求,例如错误的用户代理,缺少接受标头或类似情况。我建议你在得出它必须是数据流的分段之前先检查一下。

至于为什么curl和请求的行为不同:可能curl首先构造完整的请求(标​​题和正文)并发送它,而请求首先构造头并发送它然后发送正文,即执行两次写操作可能会导致分两包。

答案 1 :(得分:0)

尽管对于您遇到的问题并不重要,但是有一种方法可以将要发送的数据强制发送到一个数据包以进行多次发送,即在套接字上使用TCP_CORK选项(不过取决于平台)。

首先创建一个适配器:

from requests.packages.urllib3.connection import HTTPConnection

class HTTPAdapterWithSocketOptions(requests.adapters.HTTPAdapter):
    def __init__(self, *args, **kwargs):
        self.socket_options = kwargs.pop("socket_options", None)
        super(HTTPAdapterWithSocketOptions, self).__init__(*args, **kwargs)

    def init_poolmanager(self, *args, **kwargs):
        if self.socket_options is not None:
            kwargs["socket_options"] = self.socket_options
        super(HTTPAdapterWithSocketOptions, self).init_poolmanager(*args, **kwargs)

然后将其用于您要发送的请求:

s = requests.Session()
options = HTTPConnection.default_socket_options + [ (socket.IPPROTO_TCP, socket.TCP_CORK, 1)]
adapter = HTTPAdapterWithSocketOptions(socket_options=options)
s.mount("http://", adapter)

答案 2 :(得分:0)

可悲的是,确实有非常混乱的系统,如@Steffen Ullrich解释的(即使它们声称是行业标准),它们无法处理碎片化的TCP帧。由于我的应用程序/脚本相当孤立和独立,因此我基于@Roeften的答案使用了更简单的解决方法,该答案将TCP_CORK应用于所有连接。

警告:这种解决方法仅在不承担依赖requests的其他功能的风险时才有意义。

requests.packages.urllib3.connection.HTTPConnection.default_socket_options = [(6,3,1)]