使用python套接字接收大型http请求

时间:2010-10-21 13:55:54

标签: python http sockets soap

我正在使用python套接字来接收Web样式和soap请求。我的代码是

import socket
svrsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = socket.gethostname();
svrsocket.bind((host,8091))
svrsocket.listen(1)
clientSocket, clientAddress = svrsocket.accept()
message = clientSocket.recv(4096)

然而,我收到的一些肥皂要求是巨大的。 650k巨大,这可能会成为几个Mb。而不是我试过的单一recv

message = ''
while True:
  data = clientSocket.recv(4096)
  if len(data) == 0:
   break;
  message = message + data

但是我从来没有收到过使用firefox或safari的0字节数据块,尽管python socket how to说我应该这样做。

我能做些什么来解决这个问题?

2 个答案:

答案 0 :(得分:1)

不幸的是,您无法在TCP级别上解决此问题 - HTTP定义了自己的连接管理,请参阅RFC 2616。这基本上意味着您需要解析流(至少是标题)以确定何时可以关闭连接。

请在此处查看相关问题 - https://stackoverflow.com/search?q=http+connection

答案 1 :(得分:1)

Hiya

首先,我想加强先前的回答

不幸的是,您无法在TCP级别解决此问题

这是真的,你不能。但是,您可以在tcp套接字上实现http解析器。这就是我想在这里探索的。 让我们开始吧

问题和期望的结果

现在,我们正在努力寻找数据流的终点。我们希望流以固定的结尾结束,但是现在我们知道 HTTP并未定义任何消息后缀

但是,我们继续前进。

我们现在可以问一个问题:“我们能事先知道消息的长度吗?”答案是肯定的!有时候...

您看到HTTP/1.1定义了一个名为Content-Length的标头,正如您所期望的,它具有我们想要的内容长度;但阴影中还有其他东西:Transfer-Encoding: chunked。除非您真的想了解它,否则我们将暂时远离它。

解决方案

这是一个解决方案。您一开始不会知道其中的一些功能,但是如果您坚持使用我,我将进行解释。好吧...深吸一口气。

假设conn是到所需HTTP服务器的套接字连接

...

    rawheaders = recvheaders(conn,end=CRLF)
    headers = dict_headers(io.StringIO(rawheaders))
    l_content = headers['Content-Length']

    #okay. we've got content length by magic

    buffersize = 4096
    while True:
        if l_content <= 0: break

        data = clientSocket.recv(buffersize)
        message += data
        
        l_content -= len(data)

...

如您所见,我们进入循环已经知道Content-Lengthl_content

在迭代时,我们通过从clientSocket.recv(buff)中减去l_content的长度来跟踪剩余内容。

当我们读取的数据至少与l_content一样时,我们就完成了

if l_content <= 0: break

沮丧

注意:接下来,我将给出伪代码,因为代码可能有点密集

所以现在您要问的是rawheaders = recvheaders(conn),什么是headers = dict_headers(io.StringIO(rawheaders))
而我们如何得到headers['Content-Length']?!

对于初学者来说,recvheadersHTTP/1.1规范没有定义消息后缀,但确实定义了一些有用的东西:http headers的后缀!后缀为CRLF\r\n。这意味着当我们阅读CRLF时便知道何时收到标题。所以我们可以写一个类似

的函数
def recvheaders(sock):
    rawheaders = ''
    until we read crlf:
        rawheaders = sock.recv()
    return rawheaders

下一步,解析标头。

def dict_header(ioheaders:io.StringIO):
    """
    parses an http response into the status-line and headers
    """
    #here I expect ioheaders to be io.StringIO
    #the status line is always the first line
    status = ioheaders.readline().strip()
    headers = {}
    for line in ioheaders:
        item = line.strip()
        if not item:
            break
        //headers look like this 
        //'Header-Name' : 'Value'
        item = item.split(':', 1)
        if len(item) == 2:
            key, value = item
            headers[key] = value
    return status, headers

在这里,我们阅读status line,然后继续遍历其余所有行 并从[key,value]用{p>建立Header: Value

    item = line.strip()
    item = item.split(':', 1)
    # We do split(':',1) to avoid cases like
    # 'Header' : 'foo:bar' -> ['Header','foo','bar']
    # when we want ---------> ['Header','foo:bar']
    

然后我们获取该列表并将其添加到headers字典

    #unpacking
    #key = item[0], value = item[1]
    key, value = item
    header[key] = value

BAM,我们已经创建了标题表

headers['Content-Length']从那里掉出来。

所以

只要您可以保证始终收到Content-Length,此结构就会起作用 如果您已经做到了这一点,那么感谢您抽出宝贵的时间,希望对您有所帮助!

TLDR;如果您想知道带有套接字的HTTP消息的长度,请编写一个HTTP解析器