簡體   English   中英

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

[英]Using python sockets to receive large http requests

我正在使用 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。 而不是我試過的單一接收

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

但是我從來沒有用 Firefox 或 safari 收到過 0 字節的數據塊,盡管python 套接字怎么說我應該。

我能做些什么來解決這個問題?

不幸的是,您無法在 TCP 級別解決此問題 - HTTP 定義了自己的連接管理,請參閱RFC 2616 這基本上意味着您需要解析流(至少是標頭)以確定何時可以關閉連接。

在此處查看相關問題 - https://stackoverflow.com/search?q=http+connection

日亞

首先我想強調一下之前的答案所說的

不幸的是,您無法在 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'] ?!

首先, recvheaders HTTP/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然后繼續遍歷剩余的每一行,並從Header: Value構建[key,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 ,這種結構就會起作用。如果您已經做到了這一步 WOW,感謝您抽出寶貴時間,希望這對您有所幫助!

TLDR; 如果您想知道帶有套接字的 http 消息的長度,請編寫一個 http 解析器

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM