当ServerSocket阻塞在accept()时,如何通知另一个线程?

3

我有一对线程在运行 SocketServerSocket

runThreadA() {
     // Connects to B, or fails if B is not yet accepting
     socket.connect();
}

runThreadB() {
    // Blocks until another thread connects
    serverSocket.accept();
}

有没有一种方法可以保证 BA 调用 connect() 之前调用 accept() 并阻塞?


4
你真的想让同一个 JVM 中的两个线程使用套接字相互连接吗?也许你应该考虑使用 BlockingQueue 在线程间共享数据。 - Gray
我看不到其他解决办法。我正在使用SSL套接字和普通套接字进行回环配置,以便获取原始的SSL流并将其发送到第三个套接字上。这并不是特别优雅,但这就是约束条件... - user2424511
请记住,在您的代码调用accept()之前,操作系统将会愉快地接受传入的连接。即使您根本不调用accept(),A也会成功连接。您到底是在尝试解决什么问题?也许您只需要在创建套接字时将线程A与线程B同步。 - nos
@nos 我也是这样认为的,但当我尝试使用任意IP地址进行socket.connect(SocketAddress)时,它会出现java.net.ConnectException: Connection refused错误。 - Katona
在开始连接线程之前,先启动接受线程? - user207421
@EJP那样做不起作用,因为无法保证线程按照那个顺序执行。 - user2424511
3个回答

1

正确编写代码。如果connect()失败,记录问题,等待一段退避时间,然后重试,除非时间过长。即使服务器正在监听,connect()也可能因为其他原因而失败(例如,网络API中的某个层具有完整的缓冲区)。这就是为什么IOException是受控异常,Java编译器要求您明确处理它:可能会发生IOException,但这不是编程错误。

private static final Logger LOG = Logger.getLogger(YourClass.class);

for (int waitTime = 10; waitTime < 10000; waitTime *= 2) {
    try {
        socket.connect();
    } catch (ConnectException ex) {
        LOG.log(Level.WARNING,
                "Connection failed, retrying in " + waitTime + " ms...", ex);
        Thread.sleep(waitTime);
    } catch (IOException ex) {
        throw new RuntimeException("Unexpected IO Exception", ex);
    }
}

即使您使用了一些同步原语,比如屏障或信号,服务器线程仍然会在listen()实际生效之前发出信号。因此,仍然存在不良线程交错的可能性。

1

谢谢,但我认为这两个都不适用于这里,因为我无法访问“accept()”正在使用的锁定。 - user2424511

-1

对啊,设置一个合理的超时时间

 runThreadA() {
    Socket s = new Socket()
    s.connect(endPoint, REASONABLE_TIMEOUT )
 }

这样一来,线程A将尝试连接,但在放弃之前会等待一个合理的时间。

更新

另一种方法是让第二个线程在相同的合理时间内重新尝试。

runThreadA() {
    maxTimeout = currentTime() + REASONABLE_TIMEOUT
    while ( currentTime() < maxTimeout ) { 
       socket.connect()
       if ( isConnected( socket ) ) { 
           break;
       }
       Thread.currentThread.sleep( A_WHILE )
    }
    if ( ! isConnected( socket ) ) { 
        .... too bad, we can't work this way
        exit();
    } 
    continue with the work here...
} 

抱歉,我正在使用不支持超时的套接字实现。 - user2424511
等等,我正在使用的其中一个套接字确实支持超时。谢谢,看起来这对这种情况很有效,但是有没有更通用的解决线程问题的方法? - user2424511
是的,你可以使用循环,但问题在于你不能依赖服务器启动并接受连接所需的时间。最简单的方法是等待并重试(实际上使用较短的超时时间)。 - OscarRyz
不需要设置超时时间。默认超时时间大约为60秒。只有在没有响应时才会应用超时时间。如果主机通过RST主动拒绝连接,因为端口上没有监听任何内容,就像这里的情况一样,连接失败是立即发生的,超时时间不适用。-1 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接