【问题标题】:Shutting down Python BasicHTTPServer from another thread从另一个线程关闭 Python BasicHTTPServer
【发布时间】:2015-03-07 18:02:16
【问题描述】:

我有一个脚本,它在线程中设置BasicHTTPServer,以便主脚本可以自动打开指向服务器url 的网络浏览器以下载文件。下载文件后,我想关闭该服务器,但我不知道如何去做。这是我目前所做的一个例子:

def server():
    HandlerClass = SimpleHTTPRequestHandler
    ServerClass = BaseHTTPServer.HTTPServer
    Protocol = 'HTTP/1.0'

    server_address = ('127.0.0.1', 8000)
    HandlerClass.protocol_version = Protocol
    httpd = ServerClass(server_address, HandlerClass)
    httpd.serve_forever()

def download():
    t = threading.Thread(name='server', target=server)
    t.start()
    webbrowser.open('safari-http://127.0.0.1:8000/')

我想在 webbrowser.open() 之后关闭服务器。

谢谢

【问题讨论】:

  • 只是想指出我知道 BaseServer.shutdown() 方法,但我不知道如何在这种情况下实现它。
  • 不工作,很遗憾。表示没有全局变量httpd。将除“httpd.serve_forever()”语句之外的所有内容移到 server() 函数之外只会在调用 download() 时使程序挂起。
  • 错误:“未定义全局名称'self'”
  • httpd.shutdown() 仍然没有效果。

标签: python


【解决方案1】:

这是来自cryptoassets.core project, status server的示例:

class StatusHTTPServer(threading.Thread):

    def __init__(self, ip, port):
        threading.Thread.__init__(self)
        self.running = False
        self.ready = False
        self.ip = ip
        self.port = port

    def run(self):
        self.running = True
        self.ready = True
        self.httpd.serve_forever()
        self.running = False

    def start(self):
        server_address = (self.ip, self.port)
        try:
            self.httpd = HTTPServer(server_address, StatusGetHandler)
        except OSError as e:
            raise RuntimeError("Could not start cryptoassets helper service status server at {}:{}".format(self.ip, self.port)) from e

        threading.Thread.start(self)

    def stop(self):
        if self.httpd and self.running:
            self.httpd.shutdown()
            self.httpd = None

【讨论】:

  • 所以任何实例都将在单独的线程中运行,我可以从任何地方调用 stop() 来停止它?
  • @HeyItsJono:是的,差不多就是这样。
  • 不幸的是,这仍然不起作用。调用 stop() 成功,但服务器本身实际上并未停止,因此 http.shutdown() 由于某种原因无法正常工作。
  • 你能澄清一下“它不工作”吗?是否有异常,回溯,shutdown() 之后的代码会被执行还是shutdown() 永远卡住?
  • 基本上我在 if 语句之前的 stop() 方法中添加了一个 print 语句,以确保在我调用它时调用了 stop。然后我创建了一个 StatusHTTPServer 类的实例“服务器”,然后运行 ​​server.start(),然后运行 ​​threading.Thread(target=server.run)。然后,当我尝试从控制台调用 server.stop() 时,我得到了我添加的打印语句“服务器停止”,但是当我在浏览器中访问 localhost:8000 进行测试时,服务器继续服务请求,这是ip 服务器正在运行。没有错误消息,但它只是继续服务。
【解决方案2】:

我尝试了here 给出的示例。你能检查一下它是否适合你。

runFlag = True
def server(server_class=BaseHTTPServer.HTTPServer,
                   handler_class=BaseHTTPServer.BaseHTTPRequestHandler):
    global runFlag
    server_address = ('127.0.0.1', 8000)
    HandlerClass.protocol_version = Protocol
    httpd = ServerClass(server_address, HandlerClass)
    while runFlag:
        httpd.handle_request()
    httpd.shutdown()

def download():
    t = threading.Thread(name='server', target=server)
    t.start()
    webbrowser.open('https:\\www.google.com')
    global runFlag 
    runFlag = False

【讨论】:

  • 感谢您的帮助,这似乎可以在 runFlag 更改为 False 后服务器停止服务请求,但是当我再次尝试运行脚本时出现错误:[Errno 48]地址已在使用中 表明某些东西仍在占用该地址。所以服务器大部分都停止了,但没有释放地址或端口。知道如何解决这个问题吗?
  • 还有什么方法可以在不使用全局变量的情况下解决这个问题。我读过的所有内容都告诉我要远离他们。
  • @HeyItsJono 抱歉回复晚了,因为我周末不玩电脑。 Error:48这里有解决方案stackoverflow.com/a/19071568/2382792你已经发布了解决方案很好!
【解决方案3】:

因此,在深入研究了许多文章后,我设法找到了一种对我有用的混乱解决方案,它完全关闭了服务器。为此,我合并了以下来源的代码:

UI 是 iOS Python IDE Pythonista 独有的模块,它基本上只是创建按钮“开始”、“停止”和“访问”,它们绑定到各自的 _t 函数。 ui.in_background 装饰器只是让 ui 在后台发生事情时保持响应。 self.httpd.socket.close() 是真正关闭服务器的原因,但它很混乱,并且会向 stdout/err 打印一个丑陋的错误,所以我别无选择,只能通过将 stdout/err 重定向到死类来抑制它,从而删除错误。标准 stdout/err 行为在之后立即恢复。感谢大家花时间和精力帮助我,非常感谢。

import console
import BaseHTTPServer
import SocketServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
import sys
import threading
import webbrowser
from time import sleep
import ui

original_stdout = sys.stdout
original_stderr = sys.stderr


class BasicServer(SocketServer.TCPServer):
    allow_reuse_address = True

class NullifyOutput():
    def write(self, s):
        pass

class ServerThread(threading.Thread):
    def __init__(self, ip, port):
        super(ServerThread, self).__init__()
        self.ip = ip
        self.port = port
        self.HandlerClass = SimpleHTTPRequestHandler
        self.Protocol = 'HTTP/1.0'
        self.server_address = (self.ip, self.port)
        self.HandlerClass.protocol_version = self.Protocol
        try:
            self.httpd = BasicServer(self.server_address, self.HandlerClass)
        except:
            self.port += 1
            self.server_address = (self.ip, self.port)
            self.httpd = BasicServer(self.server_address, self.HandlerClass)
        self.stoptheserver = threading.Event()


    def run(self):
        while not self.stoptheserver.isSet():
            self.httpd.handle_request()


    def join(self, timeout=None):
        self.stoptheserver.set()
        self.httpd.socket.close()
        super(ServerThread, self).join(timeout)


server = ServerThread('127.0.0.1', 8000)

def start_t(sender):
    print server.isAlive()
    if not server.isAlive():
        server.start()


def visit_t(sender):
    webbrowser.open('http://127.0.0.1:' + str(server.port))
    #webbrowser.open('safari-http://127.0.0.1' + str(server.port))
    # Use the safari- prefix to open in safari. You may need to switch to
    # pythonista then back to safari to get the page to load.

@ui.in_background
def stop_t(sender):
    sys.stdout, sys.stderr = NullifyOutput(), NullifyOutput()
    server.join(3)
    sys.stdout, sys.stderr = original_stdout, original_stderr

ui.load_view('SimpleServer').present('sheet')

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-04
    相关资源
    最近更新 更多