我正在使用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
但是尽管python socket how to说我应该,但是我从来没有用firefox或safari收到0字节数据块。
如何解决这个问题?
[遗憾的是,您无法在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
,然后我们继续遍历其余每行并使用[]从[key,value]
建立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解析器