Python';s SimpleHTTPServer在一个线程中启动,该线程为';不要关闭港口
我有以下代码:Python';s SimpleHTTPServer在一个线程中启动,该线程为';不要关闭港口,python,multithreading,simplehttpserver,Python,Multithreading,Simplehttpserver,我有以下代码: import os from ghost import Ghost import urlparse, urllib import SimpleHTTPServer import SocketServer import sys, traceback from threading import Thread, Event from time import sleep please_die = Event() # this is my enemy httpd = None P
import os
from ghost import Ghost
import urlparse, urllib
import SimpleHTTPServer
import SocketServer
import sys, traceback
from threading import Thread, Event
from time import sleep
please_die = Event() # this is my enemy
httpd = None
PORT = 8001
address = 'http://localhost:'+str(PORT)+'/'
search_dir = './category'
def main():
"""
basic run script routine,
FIXME: is supossed to exits gracefully
"""
thread = Thread(target = simpleServe)
try:
thread.start()
run()
except KeyboardInterrupt:
print "Shutdown requested"
except Exception:
traceback.print_exc(file=sys.stdout)
shutdown()
sys.exit(0)
def shutdown():
global httpd
global please_die
print "Shutting down"
# A try - except for the shutdown routine
try:
please_die.wait() # how do you do?
httpd.shutdown() # Please! I whant to run you multiple times.
print "Have you died?"
except Exception:
traceback.print_exc(file=sys.stdout)
def path2url(path):
"""
constructs an url from a relative path / concatenates the global address
variable with the path given
"""
global address
return urlparse.urljoin(address, urllib.pathname2url(path))
def simpleServe():
global httpd, PORT
please_die.set() # Attaching the event to this thread
# Start the service
Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
httpd = SocketServer.TCPServer(("", PORT), Handler)
print "serving at port", PORT
# And loop infinetly in the hope that I can stop you later
httpd.serve_forever()
def run():
global search_dir;
ghost = Ghost() # the webkit facade
with ghost.start() as session:
session.set_viewport_size(2560, 1600) # "retina" size
for directory, subdirectories, files in os.walk(search_dir):
for file in files:
path = os.path.join(directory, file)
urlPath = path2url(path)
process(session, urlPath);
def process(session, urlPath):
page, resources = session.open(urlPath)
assert page.http_status == 200
# ... other asserts here
if __name__ == '__main__':
main()
其思想是创建一个脚本,启动一个“简单http服务器”,对其执行一些请求,然后退出
第一次运行时没有任何问题:
...
127.0.0.1 - - [31/Jul/2015 13:16:17] "GET /category/52003.html HTTP/1.1" 200 -
127.0.0.1 - - [31/Jul/2015 13:16:17] "GET /category/52003.html HTTP/1.1" 200 -
127.0.0.1 - - [31/Jul/2015 13:16:17] "GET /category/52003.html HTTP/1.1" 200 -
127.0.0.1 - - [31/Jul/2015 13:16:17] "GET /static/img/glyphicons-halflings.png HTTP/1.1" 200 -
Shutting down
Have you died?
第二次启动时崩溃,称:
地址已在使用中
如果我杀死了所有python进程,脚本就会再次运行,因此我假设我使用的线程是错误的,但我找不到在哪里
更新
忘了提了,
我的操作系统是:
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 15.04
Release: 15.04
Codename: vivid
我使用的python是:
$ python --version
Python 2.7.9
$netstat-putelan | grep 8001打印件:
$ netstat -putelan | grep 8001
(Not all processes could be identified, non-owned process info
cp 0 0 127.0.0.1:34691 127.0.0.1:8001 TIME_WAIT 0 0 -
tcp 0 0 127.0.0.1:8001 127.0.0.1:34866 TIME_WAIT 0 0 -
tcp 0 0 127.0.0.1:34798 127.0.0.1:8001 TIME_WAIT 0 0 -
tcp 0 0 127.0.0.1:8001 127.0.0.1:34588 TIME_WAIT 0 0 -
tcp 0 0 127.0.0.1:34647 127.0.0.1:8001 TIME_WAIT 0 0 -
tcp 0 0 127.0.0.1:34915 127.0.0.1:8001 TIME_WAIT 0 0 -
tcp 0 0 127.0.0.1:34674 127.0.0.1:8001 TIME_WAIT 0 0 -
tcp 0 0 127.0.0.1:34451 127.0.0.1:8001 TIME_WAIT 0 0 -
tcp 0 0 127.0.0.1:8001 127.0.0.1:34930 TIME_WAIT 0 0 -
tcp 0 0 127.0.0.1:8001 127.0.0.1:34606 TIME_WAIT 0 0 -
tcp 0 0 127.0.0.1:34505 127.0.0.1:8001 TIME_WAIT 0 0 -
tcp 0 0 127.0.0.1:34717 127.0.0.1:8001 TIME_WAIT 0 0 -
tcp 0 0 127.0.0.1:8001 127.0.0.1:34670 0 0 127.0.0.1:8001 127.0.0.1:34626
...
我无法发布整个序列(由于stackoverflow的post限制)。其余同34***端口与8001端口混合,顺序一致 我看到了TCPServer源代码:
def server_bind(self):
"""Called by constructor to bind the socket.
May be overridden.
"""
if self.allow_reuse_address:
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind(self.server_address)
self.server_address = self.socket.getsockname()
允许\u重用\u绑定前应设置地址。所以试试这个:
SocketServer.TCPServer.allow_reuse_address=True
httpd = SocketServer.TCPServer(("", PORT), Handler)
正如@LFJ所说,这可能是由于
TCPServer
的allow\u reuse\u address
属性造成的
httpd = SocketServer.TCPServer(("", PORT), Handler, bind_and_activate=False)
httpd.allow_reuse_address = True
try:
httpd.server_bind()
httpd.server_activate()
except:
httpd.server_close()
raise
等效代码:
SocketServer.TCPServer.allow_reuse_address = True
https = SocketServer.TCPServer(("", PORT), Handler)
让我们解释一下原因
启用TCPServer.allow_reuse_address
时,会在套接字上添加一个选项:
class TCPServer:
[...]
def server_bind(self):
if self.allow_reuse_address:
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
[...]
什么是socket.SO\u REUSEADDR
事实上,它允许重用套接字绑定地址。如果另一个进程在套接字未侦听时尝试绑定,则将允许该进程使用此套接字绑定地址
您需要启用该功能的原因是您没有正确关闭TCPServer
。要正确关闭它,必须运行shutdown
方法,该方法将永远关闭由server\u启动的线程
,然后通过调用server\u close
方法正确关闭套接字
def shutdown():
global httpd
global please_die
print "Shutting down"
try:
please_die.wait() # how do you do?
httpd.shutdown() # Stop the serve_forever
httpd.server_close() # Close also the socket.
except Exception:
traceback.print_exc(file=sys.stdout)
服务器关闭后,您不会清理它。这意味着您将留下闲置的套接字资源,而操作系统不会在进程结束后立即清理这些资源 在调用
httpd.serve\u forever()
之后,需要在finally块中调用httpd.server\u close()。此调用告诉操作系统释放可能与给定服务器实例关联的所有资源
try:
httpd.serve_forever()
finally:
httpd.server_close()
您只需在启动服务器之前添加import signal try:os.kill(%d,signal.SIGTERM)%PORT
,即可在所需端口上终止活动。您可以发布命令netstat-putelan | grep 8001
的输出吗?我仍在检查,我会通知您的know@Andersson不我想要一个优雅的关闭,而不是一个野蛮的武力杀死在启动时,端口不知何故配置哇,你明白了!:)提醒我不要试图正确解释事情的原因和后果。
def shutdown():
global httpd
global please_die
print "Shutting down"
try:
please_die.wait() # how do you do?
httpd.shutdown() # Stop the serve_forever
httpd.server_close() # Close also the socket.
except Exception:
traceback.print_exc(file=sys.stdout)
try:
httpd.serve_forever()
finally:
httpd.server_close()