[英]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-Length
為l_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.