为什么我的Python线程中的socket并不总是正常关闭(即如果我多次运行程序)

1 投票
1 回答
526 浏览
提问于 2025-04-17 23:32

我有以下这段代码(里面有很多调试信息)。如果我在短时间内连续运行几次,常常会出现“地址正在使用”的错误。不过,这个错误并不是每次都会发生。

socket.error: [Errno 98] Address already in use

当我没有遇到错误时,输出结果是这样的:

 trying to recv ('127.0.0.1', 38041)
 recved
 from client -->  None genuine without this seal!
 shutting down con
 closing con
 done closing con
 shutting down
 running server shutdown
 running server close
 done closing
 done shutting down

以下是我的代码:

import socket
from threading import Thread
from time import sleep
from subprocess import Popen, PIPE, STDOUT


def shutdown_thread(t):
    print "shutting down"
    t.shutdown()
    while t.isAlive():
        sleep(.1)
    print "done shutting down"


def send_with_netcat(msg):
    nc = Popen(
        ['nc', '127.0.0.1', '8000'],
        stdin=PIPE,
        stdout=PIPE,
        stderr=STDOUT)

    result = nc.communicate(msg)
    return result


class Server(Thread):
    is_shutdown = False

    def __init__(self):
        super(Server, self).__init__()
        self.start_server()

    def start_server(self):
        self.server = socket.socket(
            socket.AF_INET,
            socket.SOCK_STREAM)

        # "A socket is a 5 tuple (proto, local addr, local port,
        # remote addr, remote port).  SO_REUSEADDR just says that you
        # can reuse local addresses.  The 5 tuple still must be
        # unique!"
        self.server.setsockopt(
            socket.SOL_SOCKET,
            socket.SO_REUSEADDR,
            1)

        self.server = socket.socket(
            socket.AF_INET,
            socket.SOCK_STREAM)

        # bind the socket
        # ERROR HAPPENS HERE, HOW TO DEAL DEAL WITH OR AVOID?
        self.server.bind(('127.0.0.1', 8000))
        self.server.listen(5)

    def shutdown(self):
        self.is_shutdown = True

    def run(self):
        while not self.is_shutdown:
            con, addr = self.server.accept()
            print "trying to recv", addr
            buf = con.recv(128)
            print "recved"
            if len(buf) > 0:
                print "from client --> ", buf

            print "shutting down con"
            con.shutdown(socket.SHUT_RDWR)
            print "closing con"
            con.close()
            print "done closing con"
            sleep(.1)

        print "running server shutdown"
        self.server.shutdown(socket.SHUT_RDWR)
        print "running server close"
        self.server.close()
        print "done closing"

if __name__ == '__main__':
    s = Server()
    s.start()
    send_with_netcat("None genuine without this seal!")
    shutdown_thread(s)
    sleep(5)

如果你觉得在GitHub上看起来更清晰,可以在这里查看同样的代码。

奇怪的是,当我试图发这个帖子时,花了比预期更多的时间。这会不会是因为我的机器上有很多连接在随机化客户端的nc连接呢?

感谢你对这个问题的任何解释。

1 个回答

2

在你的 start_server() 方法里,你首先在 self.server 中打开了一个套接字,然后你给这个套接字设置了 SO_REUSEADDR 选项。接着你又调用了一次 socket.socket(),这就把你之前的 setsockopt() 设置给覆盖掉了。试着删除第二次的 socket.socket() 调用,这样就能让 SO_REUSEADDR 选项生效了。

撰写回答