Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/358.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python';s SimpleHTTPServer在一个线程中启动,该线程为';不要关闭港口_Python_Multithreading_Simplehttpserver - Fatal编程技术网

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()