2013-03-18 72 views
3

我遇到了一个问题,urllib2.urlopen/requests.post是非常偶然永远阻止socket.recv永远不会返回。如何防止永久阻止urlopen

我想知道为什么会发生这种情况并解决该问题,但同时我想知道是否有防止永久阻止的方法?

我已经知道了urllib2.urlopensocket.setdefaulttimeouttimeout可选参数,但不幸的是我的使用情况下超时是没办法,因为我上载的文件与POST我用将有可能中断正常的文件上传任何超时值。我也看到了一些使用信号的解决方案,但是这会和我使用超时的问题相同(也是因为我没有从主线程执行此操作而出问题)。

只有在没有数据通过套接字发送/接收一定时间的情况下才有可能超时?或者也许有一些方法可以使用select/poll来防止我遇到的死锁/阻塞?

如果有解决方案使用选择/民意调查,我会怎么去把它纳入urllib2.urlopen/requests.post


我也有这个想法,如果我可以通过接口的写入类型发送文件数据,所以我会控制遍历文件,并在同一时间发送块我大概可以有足够的控制,以避免摊位。我不知道如何实现它,虽然如此,我问了一个问题:Upload a file with a file.write interface

UPDATE 好像我一直的在python timeout含义的误解,现在看来,这实际上是一个空闲超时或读/写超时(可能是我第一次disagreed with Guido)。我一直认为这是响应应该返回的最长时间 - 谢谢@tomasz指出这一点!

但添加超时参数(同时用urllib2requests测试)后,我遇到了一些非常奇怪和微妙的情况,可能是特定于mac,超时不能正常工作,我越来越倾向于相信是一个错误。我将继续调查并找出问题所在。再次感谢你对此的帮助!

+0

首先 - *为什么*它永远阻挡? – 2013-03-18 16:58:46

+0

@CodePainters我不知道 - 理想情况下,我会解决实际问题,我会继续尝试,但它可能是一个服务器端问题(我不控制),直到我找到原因, d喜欢设置一些作为后备的东西,以便上传永远不会冻结,并在此期间作为修补程序发布。 – GP89 2013-03-18 17:02:38

回答

5

我相信你可以通过在操作系统级别调整您的TCP设置摆脱挂状态,但假设您的应用程序不会在专用(并由您维护)机器上工作,您应该寻求更通用的解决方案。

你问:

是否有可能只在没有数据发送超时通过插座/收到了一定的时间也许

而这正是行为socket.settimeout(或传递给urllib2的那个)会给你。与基于SIGALRM的超时(即使在数据传输缓慢时会终止)相反,只有在定义的时间段内没有数据传输时,传递到套接字的超时才会发生。如果socket.sendsocket.recv的呼叫应该返回部分计数,如果在此期间某些(但不是全部)数据已传输,并且urllib2然后将使用后续呼叫来传输剩余数据。

说了这样的话,如果POST调用将在多个send调用中执行,并且任何(但不是第一个)调用都会在不发送任何数据的情况下阻塞并超时,那么您的POST调用仍可能在上传中途的某个地方终止。您给人的印象是它不会被您的应用程序正确处理,但我认为它应该,因为它类似于强制终止该流程或者简单地将连接断开。

您是否测试过并确认socket.settimeout不能解决您的问题?或者你只是不确定行为是如何实施的?如果前者是正确的,请你提供更多的细节?我相当肯定你只需设置超时时间是安全的,因为python只是使用低级BSD套接字实现,其行为如上所述。为了给您更多的参考,请看setsockopt手册页和SO_RCVTIMEOSO_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 

客户(请求模块)

import requests 

data = "x"*100000000 # 100MB beefy chunk 
requests.post("http://localhost:12346", data=data, timeout=4) 

客户(urllib2的模块)

import urllib2 

data = "x"*100000000 # 100MB beefy chunk 
urllib2.urlopen("http://localhost:12346", data=data, timeout=4) 

输出(服务器)

> 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 

两个客户提出的异常:按预期工作

# urllib2 
URLError: timeout('timed out',) 

# Requests 
Timeout: TimeoutError("HTTPConnectionPool(host='localhost', port=12346): Request timed out. (timeout=4)",) 

一切!如果不通过超时作为参数,urllib2也反应良好socket.setdefaulttimeout,但Requests没有。这并不令人惊讶,因为内部实现根本不需要使用默认值,并且可以根据传递的参数或使用非阻塞套接字简单地覆盖它。

我一直在使用运行此如下:

OSX 10.8.3 
Python 2.7.2 
Requests 1.1.0 
+0

看起来像一个巨大的一巴掌额头时刻..从一个非常低的值测试'setdefaulttimeout'我可以看到它对上传没有影响(这是上传罚款)。出于某种原因,我认为它会在一段时间之后从一开始就超时。我猜是因为我几乎总是在过去完成Web请求,并且从通话开始到请求结束之间的时间很短,看起来超时是对整个时间的限制操作而不是读/写超时。谢谢你指出! – GP89 2013-03-25 10:50:59

+0

另外,你知道它会提高errno吗? 'errno.ETIMEDOUT'我猜想,但是查找'SO_RCVTIMEO'和'SO_SNDTIMEO',它看起来可能有些不同。 – GP89 2013-03-25 10:51:21

+0

我一直在用'requests.put'测试它,并且设置'socket.setdefaulttimeout'似乎不工作,并且传递'timeout' kwarg意味着我无法上传任何东西 - 我只是不断地收到一个套接字错误和“资源暂时不可用”。任何想法? – GP89 2013-03-25 13:19:48

0

其中一个可能的决定 - 您可以将您的urllib2请求嵌套到具有ALRM信号处理的块中,或者将其放入超时时强制停止的线程。 这将强制停止您的要求通过超时,尽管任何内部urllib2的问题,有关这个案子的老问题: Python: kill or terminate subprocess when timeout

+0

但是这不是OP所需要的:“只有在没有数据通过套接字发送/接收一段时间的情况下才有可能超时? – 2013-03-18 17:22:12

+0

是的,我不能使用信号,因为我没有从主线程上传,我认为它会工作,就像指定一个超时无论如何(这不会对我工作)。而使用线程的想法将与指定超时的效果相同。 – GP89 2013-03-18 17:28:12

+0

老问题:http://stackoverflow.com/questions/5686490/detect-socket-hangup-without-sending-or-receiving在你的情况下似乎很有用 – moonsly 2013-03-18 18:22:01

1

你提到无限期阻塞情况“很偶然”,而你正在寻找一个备用,以避免失败的文件上传时出现这种情况。在这种情况下,我建议使用超时时间来发送邮件,并在超时情况下重试邮件。所有这一切都需要一个简单的for循环,如果发生超时以外的任何事情,就会中断。

当然,您应该在发生这种情况时记录一条警告消息,并监视发生这种情况的频率。你应该尝试找出冻结的根本原因(正如你提到的你打算的那样)。

+0

看起来像我可以使用'超时',你是对的。我一直认为超时是通话所需的最长时间,我认为我不能准确地进行锻炼(如果超过几GB的上传时间,并且用户不得不启动),但似乎我对超时的理解是错误的,它确实充当了我正在寻找的读/写超时! – GP89 2013-03-25 10:59:56

相关问题