我正在使用Python 2.7.1 urllib2包中的urlopen从Windows XP计算机到远程Apache网络服务器(例如Mac OS X的内置Web共享)进行HTTP POST。发送的数据包含一些标识符,数据和校验和,如果发送了所有数据,则服务器以确认响应。数据中的校验和可用于检查所有内容是否按顺序排列。
通常情况下效果很好,但有时互联网连接不好,通常是因为发送数据的客户端使用wifi或3G连接。这导致互联网连接丢失一段任意时间。 urlopen包含一个超时选项,以确保它不会阻止您的程序,它可以继续。
这就是我想要的,但问题是urlopen不会阻止套接字继续发送超时发生时仍然必须发送的任何数据。我通过尝试向我的笔记本电脑发送大量数据来测试这个(我将在下面显示的代码),我会在两个show activity上看到网络活动,然后我会在笔记本电脑上停止无线,等待直到该功能超时,然后重新激活无线,然后数据传输将继续,但程序将不再监听响应。我甚至试图退出Python解释器,它仍然会发送数据,所以控制权以某种方式移交给Windows。
超时(据我所知)的工作原理如下:
它检查“空闲响应时间”
([Python-Dev] Adding socket timeout to urllib2)
如果将超时设置为3,它将打开连接,启动计数器,然后尝试发送数据并等待响应,如果在接收响应之前的任何时刻计时器用完,则会调用超时异常。请注意,就超时计时器而言,数据的发送似乎不算作“活动”
(urllib2 times out but doesn't close socket connection)
(Close urllib2 connection)
显然,在某个地方声明当套接字被关闭/取消引用/垃圾收集时,它会调用其“关闭”函数,该函数在关闭套接字之前等待发送所有数据。但是还有一个关闭功能,它应该立即停止套接字,防止发送更多数据
(socket.shutdown vs socket.close)
(http://docs.python.org/library/socket.html#socket.socket.close)
我希望在发生超时时连接为“关闭”。否则,我的客户将无法判断数据是否已正确接收,并且可能会尝试再次发送。我宁愿直接杀死连接,稍后再试,知道数据(可能)没有成功发送(如果校验和不匹配,服务器可以识别这个)。
以下是我用来测试此代码的部分代码。 try..except部分还没有像我期望的那样工作,任何帮助也有所值得赞赏。正如我之前所说,我希望程序在引发超时(或任何其他)异常时立即关闭套接字。
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()
答案 0 :(得分:1)
我找到了一些可能对您有帮助的代码on this thread:
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()
答案 1 :(得分:1)
您可以考虑使用与urllib2不同的API。 httplib有点不太愉快,但往往不是太糟糕。但是,它确实可以访问底层套接字对象。所以,你可以这样做:
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缓冲区但它想要的,即使它的方法并不总是完全最优......
答案 2 :(得分:0)
如果调用socket.shutdown
确实是切断超时数据的唯一方法,我认为你需要采取某种猴子修补方式。 urllib2并没有真正为你提供那种细粒度套接字控制的机会。
查看Source interface with Python and urllib2以获得良好的方法。
答案 3 :(得分:0)
您可以使用multiprocessing
生成辅助线程,然后在检测到超时(URLError
异常并且消息“urlopen error timed out”)时将其关闭。
停止进程应足以关闭套接字。
答案 4 :(得分:0)
事实证明,在正在上传的HTTPConnection上调用.sock.shutdown(socket.SHUT_RDWR)和.close()命令不会停止上传。它将继续在后台运行。在使用urllib2或httplib时,我不知道有更多可靠/直接的方法可以从Python中终止连接
最后,我们使用urllib2测试了上传而没有超时。这意味着在慢速连接上进行上传(POST)可能需要很长时间,但至少我们知道它是否有效。由于没有超时,urlopen可能会挂起,但我们已经测试了各种不良连接的可能性,并且在所有情况下,urlopen工作或在一段时间后返回错误。
这意味着我们至少会在客户端知道上传成功或失败,并且它不会在后台继续。