GP8*_*P89 3 python sockets urllib2 python-2.7
我遇到的问题是urllib2.urlopen/ requests.post是很偶然阻塞永远对socket.recv不退还.
我试图找出为什么会发生这种情况并解决这个问题,但同时我想知道是否有办法防止它永远阻塞?
我已经知道了timeout可选参数urllib2.urlopen,socket.setdefaulttimeout但不幸的是,对于我的用例,超时不是解决方案因为我使用POST上传文件我使用的任何超时值都可能会中断正常的文件上传.
我也看到了一些使用信号的解决方案,但这会产生与使用超时相同的问题(也是问题,因为我不是从主线程中执行此操作).
是否有可能在没有数据通过套接字发送/接收一段时间后才能超时?或者也许有一些方法我可以使用select/poll来防止我遇到的死锁/阻塞?
如果有使用select/poll的解决方案,我将如何将其合并到urllib2.urlopen/ requests.post?
我还有一个想法,如果我可以通过写入类型的接口发送文件数据,所以我控制迭代文件并一次发送块我可能有足够的控制来避免停顿.我不知道如何实现它,所以我问了一个问题:使用file.write接口上传文件
更新
似乎我总是误解了timeoutpython 中的含义,它似乎实际上是空闲超时或读/写超时(可能是我第一次不同意Guido).我一直以为这是响应应该返回的最长时间 - 谢谢@tomasz指出这个!
但是,增加超时参数(这两个测试后urllib2和requests)我已经遇到了一些非常奇怪和微妙的情况下,可能MAC具体,超时工作不正常,我正在越来越倾向于认为是一个错误.我将继续调查并确切地找出问题所在.再次感谢tomasz对此的帮助!
我相信您可以通过调整操作系统级别的TCP设置来摆脱挂起状态,但假设您的应用程序无法在专用(并且可由您维护)的计算机上运行,那么您应该寻求更通用的解决方案.
您询问:
是否可能仅在没有数据通过套接字发送/接收一段时间后才能超时
这正是socket.settimeout(或者传给的urllib2)给你的行为.与基于SIGALRM的超时相反(即使在慢速数据传输期间也会终止),只有在定义的时间段内没有传输数据时,才会发生传递给套接字的超时.如果在此期间内已发送了一些但不是所有数据,则会调用socket.send或socket.recv应返回部分计数,urllib2然后使用后续调用来传输剩余数据.
这样说,如果在多个send调用中执行POST调用,那么你的POST调用仍然可以在上传的某个地方终止,而任何(但不是第一个)调用都会阻塞并超时而不发送任何数据.您给人的印象是,您的应用程序无法正确处理,但我认为它应该是,因为它类似于强制终止进程或只是断开连接.
您是否经过测试并确认socket.settimeout无法解决您的问题?或者你只是不确定行为是如何实现的?如果前者是正确的,请你提供更多细节吗?我很确定你只是设置超时是安全的,因为python只是使用低级BSD套接字实现,其行为如上所述.要为您提供更多参考,请查看setsockopt手册页和/ SO_RCVTIMEO或SO_SNDTIMEO选项.我希望socket.settimeout使用这些功能和选项.
---编辑--- (提供一些测试代码)
所以我能够获得Requests模块并测试行为urllib2.我运行的服务器正在接收每个recv呼叫之间间隔越来越大的数据块.正如预期的那样,当间隔达到指定的超时时,客户端超时.例:
服务器
import socket
import time
listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listener.bind(("localhost", 12346))
listener.listen(1)
sock,_ = listener.accept()
interval = 0.5
while 1:
interval += 1 # increase interval by 1 second
time.sleep(interval)
# Get 1MB but will be really limited by the buffer
data = sock.recv(1000000)
print interval, len(data)
if not data:
break
Run Code Online (Sandbox Code Playgroud)
客户 (请求模块)
import requests
data = "x"*100000000 # 100MB beefy chunk
requests.post("http://localhost:12346", data=data, timeout=4)
Run Code Online (Sandbox Code Playgroud)
客户端 (urllib2模块)
import urllib2
data = "x"*100000000 # 100MB beefy chunk
urllib2.urlopen("http://localhost:12346", data=data, timeout=4)
Run Code Online (Sandbox Code Playgroud)
输出 (服务器)
> 1.5 522832
> 2.5 645816
> 3.5 646180
> 4.5 637832 <--- Here the client dies (4.5 seconds without data transfer)
> 5.5 294444
> 6.5 0
Run Code Online (Sandbox Code Playgroud)
两位客户都提出了例外:
# urllib2
URLError: timeout('timed out',)
# Requests
Timeout: TimeoutError("HTTPConnectionPool(host='localhost', port=12346): Request timed out. (timeout=4)",)
Run Code Online (Sandbox Code Playgroud)
一切都按预期工作!如果没有传递超时作为参数,urllib2也反应良好socket.setdefaulttimeout,但Requests没有.这并不奇怪,因为内部实现根本不需要使用默认值,而只是根据传递的参数覆盖它或使用非阻塞套接字.
我一直用以下方法运行它:
OSX 10.8.3
Python 2.7.2
Requests 1.1.0
Run Code Online (Sandbox Code Playgroud)