python - 关闭基于 socketserver 的 Python 3 服务器挂起

标签 python python-3.x gevent socketserver

我正在使用 Python 3 中的线程 SocketServer 开发“简单”服务器。

为此,我在实现关闭 时遇到了很多麻烦。我在 Internet 上找到的下面的代码和关机最初工作,但在通过 telnet 从客户端发送一些命令后停止工作。一些调查告诉我它卡在 threading._shutdown ... threading._wait_for_tstate_lock 但到目前为止这还没有响铃。

我的研究告诉我,关于如何在不同的 Python 版本中执行此操作,有大约 42 种不同的解决方案、框架等。到目前为止,我找不到 python3 的工作方法。例如。我喜欢 telnetsrv ( https://pypi.python.org/pypi/telnetsrv/0.4 ) 用于 python 2.7(它使用来自 gevent 的 greenlets)但是这个不适用于 python 3。所以如果有更多的 pythonic,std lib 方法或可靠工作的东西,我很想听听它!

我目前的赌注是 socketserver,但我还不知道如何处理挂起的服务器。我删除了所有日志语句和大部分功能,因此我可以发布这个暴露问题的最小服务器:

# -*- coding: utf-8 -*-
import socketserver
import threading

SERVER = None


def shutdown_cmd(request):
    global SERVER
    request.send(bytes('server shutdown requested\n', 'utf-8'))
    request.close()
    SERVER.shutdown()
    print('after shutdown!!')
    #SERVER.server_close()


class service(socketserver.BaseRequestHandler):
    def handle(self):
        while True:
            try:
                msg = str(self.request.recv(1024).strip(), 'utf-8')
                if msg == 'shutdown':
                    shutdown_cmd(msg, self.request)
                else:
                    self.request.send(bytes("You said '{}'\n".format(msg), "utf-8"))
            except Exception as e:
                pass


class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    pass


def run():
    global SERVER
    SERVER = ThreadedTCPServer(('', 1520), service)
    server_thread = threading.Thread(target=SERVER.serve_forever)
    server_thread.daemon = True
    server_thread.start()
    input("Press enter to shutdown")
    SERVER.shutdown()


if __name__ == '__main__':
    run()

如果能够从处理程序中停止服务器也很棒(请参阅 shutdown_cmd)

最佳答案

shutdown() 按预期工作,服务器已停止接受新连接,但 python 仍在等待事件线程终止。

默认情况下,socketserver.ThreadingMixIn 将创建新线程来处理传入连接,默认情况下,这些线程是非守护线程,因此 python 将等待所有事件的非守护线程终止。

当然,你可以让服务器生成守护线程,这样 python 就不会等待了:

The ThreadingMixIn class defines an attribute daemon_threads, which indicates whether or not the server should wait for thread termination. You should set the flag explicitly if you would like threads to behave autonomously; the default is False, meaning that Python will not exit until all threads created by ThreadingMixIn have exited.

class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    daemon_threads = True

但这不是理想的解决方案,你应该检查为什么线程永远不会终止,通常,服务器应该在没有新数据可用或客户端关闭连接时停止处理连接:

import socketserver
import threading


shutdown_evt = threading.Event()


class service(socketserver.BaseRequestHandler):
    def handle(self):
        self.request.setblocking(False)
        while True:
            try:
                msg = self.request.recv(1024)
                if msg == b'shutdown':
                    shutdown_evt.set()
                    break
                elif msg:
                    self.request.send(b'you said: ' + msg)
                if shutdown_evt.wait(0.1):
                    break
            except Exception as e:
                break


class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    pass


def run():
    SERVER = ThreadedTCPServer(('127.0.0.1', 10000), service)
    server_thread = threading.Thread(target=SERVER.serve_forever)
    server_thread.daemon = True
    server_thread.start()
    input("Press enter to shutdown")
    shutdown_evt.set()
    SERVER.shutdown()


if __name__ == '__main__':
    run()

关于python - 关闭基于 socketserver 的 Python 3 服务器挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48265583/

相关文章:

python - 如何向下滚动网页直到单击特定按钮?

python - 是否有用于 OpenStreetMap Overpass API 的 Python 库?

python - 为什么 boolean 变量没有被分配为true?

python - 计算斐波那契数时的取模问题

Python:从 Gevent Greenlet 获取值(value)

Python - 如何捕获gevent套接字超时异常

python - gevent 无法在 OS X Capitan 上的 python 虚拟环境中安装

python - 计算 Pandas 数据框中 np.nan 的数量

python - Flask style.css 未从 static/css/style.css 加载

Python 编码 base64 不期望正确的结果?