在ThreadPoolExecutor中,socket.bind() 阻塞但没有“地址已在使用”错误

0 投票
1 回答
24 浏览
提问于 2025-04-12 16:04

我正在写一个使用socket的Python程序,并测试在同一个端口上监听两次,使用的是ThreadPoolExecutor。下面是一个简单的程序,能重现我遇到的问题:

import socket
import time
import concurrent.futures

def listen6666():
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    print("set socket")
    server_socket.bind(("localhost", 6666))
    server_socket.listen()
    print("done")
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(("localhost", 6666))
server_socket.listen()
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    fn = executor.submit(listen6666)
    fn2 = executor.submit(listen6666)
    # listen6666() - "Address already in use"
#fn.result() - "Address already in use"
#fn2.result() - "Address already in use"
time.sleep(10)

我本来期待在server_socket.bindlisten6666中会出现“地址已在使用中”的错误,但它就是没有反应。所以完整的输出是:

$ python test.py 
set socket
set socket
$

如果我在没有executor的情况下调用listen6666(),或者在睡眠之前调用fn.result(),那么“地址已在使用中”的错误确实会出现。我希望在listen6666()中也能出现“地址已在使用中”的错误。

我该怎么解决这个问题呢?

1 个回答

0

你需要在这个线程中处理异常情况。

下面是你代码的一个修改版本,展示了一个“地址已经在使用中”的异常是如何被抛出的。

import socket
import concurrent.futures

ADDRESS = "localhost", 6666

def listen6666(keep=False):
    server_socket = None
    try:
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        print("bind")
        server_socket.bind(ADDRESS)
        print("listen")
        server_socket.listen()
        print("done")
    except Exception as e:
        print(e)
    finally:
        if keep:
            return server_socket
        if server_socket is not None:
            server_socket.close()

server_socket = None
try:
    server_socket = listen6666(True)

    with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
        executor.submit(listen6666)
        executor.submit(listen6666)
except Exception as e:
    print(e)
finally:
    if server_socket is not None:
        server_socket.close()

输出结果:

bind
listen
done
bind
bind
[Errno 48] Address already in use
[Errno 48] Address already in use

撰写回答