我正在使用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说我应该这样做。
我能做些什么来解决这个问题?
答案 0 :(得分:1)
不幸的是,您无法在TCP级别上解决此问题 - HTTP定义了自己的连接管理,请参阅RFC 2616。这基本上意味着您需要解析流(至少是标题)以确定何时可以关闭连接。
请在此处查看相关问题 - https://stackoverflow.com/search?q=http+connection
答案 1 :(得分:1)
首先,我想加强先前的回答
不幸的是,您无法在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]
用{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解析器