How to shutdown a timed out http POST using urlopen by urllib2 in Python?
我正在使用Python 2.7.1 urllib2包中的urlopen从Windows XP机器到远程Apache Web服务器(例如Mac OS X的内置Web共享)执行HTTP POST。发送的数据包含一些标识符,数据和校验和,如果发送了所有数据,则服务器以确认响应。数据中的校验和可用于检查一切是否按顺序到达。
问题
通常这很好用,但有时互联网连接不好,通常是因为发送数据的客户端使用wifi或3G连接。这导致互联网连接丢失一段任意时间。 urlopen包含一个超时选项,以确保它不会阻止您的程序,它可以继续。
这就是我想要的,但问题是urlopen不会阻止套接字继续发送超时发生时仍然必须发送的任何数据。我通过尝试向我的笔记本电脑发送大量数据来测试这个(我将在下面显示的代码),我会在两个show activity上看到网络活动,然后我会在笔记本电脑上停止无线,等待直到该功能超时,然后重新激活无线,然后数据传输将继续,但程序将不再监听响应。我甚至试图退出Python解释器,它仍然会发送数据,因此控制权以某种方式传递给Windows。
原因
超时(据我所知)的工作原理如下:
它会检查"空闲响应时间"
([Python-Dev]向urllib2添加套接字超时)
如果将超时设置为3,它将打开连接,启动计数器,然后尝试发送数据并等待响应,如果在接收响应之前的任何时刻计时器用完,则会调用超时异常。请注意,就超时计时器而言,发送数据似乎不算作"活动"。
(urllib2超时但没有关闭套接字连接)
(关闭urllib2连接)
显然它是在某个地方陈述当套接字被关闭/解除引用/垃圾收集时它调用它的'close'函数,它等待在关闭套接字之前发送所有数据。但是还有一个关闭功能,它应该立即停止套接字,防止发送更多数据。
(socket.shutdown vs socket.close)
(http://docs.python.org/library/socket.html#socket.socket.close)
我想要的是
我希望在发生超时时连接为"关闭"。否则,我的客户将无法判断数据是否已正确接收,并且可能会尝试再次发送。我宁愿直接杀死连接并稍后再试,知道数据(可能)没有成功发送(如果校验和不匹配,服务器可以识别这个)。
这是我用来测试它的代码的一部分。 try..except部分还没有像我期望的那样工作,任何帮助也有所值得赞赏。正如我之前所说,我希望程序在引发超时(或任何其他)异常时立即关闭套接字。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | from urllib import urlencode from urllib2 import urlopen, HTTPError, URLError import socket import sys class Uploader: def __init__(self): self.URL ="http://.../" self.data = urlencode({'fakerange':range(0,2000000,1)}) print"Data Generated" def upload(self): try: f = urlopen(self.URL, self.data, timeout=10) returncode = f.read() except (URLError, HTTPError), msg: returncode = str(msg) except socket.error: returncode ="Socket Timeout!" else: returncode = 'Im here' def main(): upobj = Uploader() returncode = upobj.upload() if returncode == '100': print"Success!" else: print"Maybe a Fail" print returncode print"The End" if __name__ == '__main__': main() |
您可以考虑使用与urllib2不同的API。 httplib有点不太愉快,但往往不是太糟糕。但是,它确实可以访问底层套接字对象。所以,你可以这样做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import httplib import socket def upload(host, path, data): conn = httplib.HTTPConnection(host, 80, True, 3) try: conn.request('POST', path, data) response = conn.getresponse() if response.status != 200: # maybe an HTTP error return response.status else: response_data = r.read() return response_data except socket.error: return"Socket Timeout!" finally: conn.sock.shutdown() conn.close() def main(): data = urlencode({'fakerange':range(0,2000000,1)}) returncode = upload("www.server.com","/path/to/endpoint", data) ... |
(免责声明:未经测试)
与urllib2相比,httplib确实有各种限制 - 例如,它不会自动处理重定向等内容。但是,如果您使用它来访问相对固定的API而不是从互联网上下载随机内容,它应该可以正常工作。
老实说,我可能自己也不愿意这样做;我通常满足于让操作系统处理TCP缓冲区但是它想要,即使它的方法并不总是完全最优的......
我找到了一些可能在这个帖子上帮助你的代码:
1 2 3 4 5 6 7 8 9 10 | from urllib2 import urlopen from threading import Timer url ="http://www.python.org" def handler(fh): fh.close() fh = urlopen(url) t = Timer(20.0, handler,[fh]) t.start() data = fh.read() t.cancel() |
事实证明,在正在上传的HTTPConnection上调用.sock.shutdown(socket.SHUT_RDWR)和.close()命令不会停止上载。它将继续在后台运行。在使用urllib2或httplib时,我不知道更可靠/直接的方法来从Python中终止连接。
最后,我们使用urllib2测试了上传而没有超时。这意味着在慢速连接上进行上传(POST)可能需要很长时间,但至少我们知道它是否有效。由于没有超时,urlopen可能会挂起,但我们已经测试了各种错误连接的可能性,并且在所有情况下,urlopen工作或在一段时间后返回错误。
这意味着我们至少会在客户端知道上传成功或失败,并且它不会在后台继续。
您可以使用
停止进程应足以关闭套接字。
如果调用
查看使用Python和urllib2的Source接口以获得一个好方法。