gpt4 book ai didi

java - "java.net.BindException: Address already in use"尽管设置了 SO_REUSEADDR

转载 作者:行者123 更新时间:2023-12-01 18:37:39 25 4
gpt4 key购买 nike

我已经编写了这个简单的 NIO 服务器,但是当多次运行时,我会得到这个异常:

Exception in thread "main" java.lang.IllegalStateException: java.net.BindException: Address already in use
at test.Server.start(Server.java:38)
at test.Server.main(Server.java:93)

我在调用绑定(bind)之前设置了 setReuseAddress(true)。我也尝试在 ServerSocketChannel 上调用 setOption(StandardSocketOptions.SO_REUSEADDR, true) 但它仍然是相同的。

有人可以指出为什么会发生这种情况吗?

这是代码:

package test;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Server {

private ServerSocketChannel ssc;
private ServerSocket serverSocket;
private Selector accept;
private ExecutorService executor = Executors.newSingleThreadExecutor();

void start(final CountDownLatch cdl) {
try {
this.accept = Selector.open();

ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.setOption(StandardSocketOptions.SO_REUSEADDR, true);

InetSocketAddress isa = new InetSocketAddress("127.0.0.1", 9123);
serverSocket = ssc.socket();
serverSocket.setReuseAddress(true);
serverSocket.bind(isa);
ssc.register(accept, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
throw new IllegalStateException(e);
}
executor.submit(new Runnable() {
@Override
public void run() {
try {
if (cdl != null) {
cdl.countDown();
}
while (true) {
accept.select();
if (Thread.currentThread().isInterrupted()) {
return;
}
Set<SelectionKey> readyKeys = accept.selectedKeys();
Iterator<SelectionKey> i = readyKeys.iterator();
while (i.hasNext()) {
SelectionKey sk = i.next();
if (sk.isValid() && sk.isAcceptable()) {
accept(sk);
}
i.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}

private void accept(final SelectionKey sk) throws IOException {
ServerSocketChannel ssc = (ServerSocketChannel) sk.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
sc.register(accept, SelectionKey.OP_READ);
System.out.println("Connection accepted from: "
+ sc.getRemoteAddress());
}
});
}

void stop() {
try {
executor.shutdown();
executor.awaitTermination(10, TimeUnit.SECONDS);
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public static void main(String[] args) throws InterruptedException {
Server s = new Server();
CountDownLatch cdl = new CountDownLatch(1);
s.start(cdl);
cdl.await();
Client.connect();
s.stop();
}
}

class Client {
static void connect() {
try {
new Socket("127.0.0.1", 9123);
} catch (IOException e) {
e.printStackTrace();
}
}
}

最佳答案

您不能对同一适配器和端口号进行两次不同的代码调用。这就是 TCP/IP 堆栈的工作方式。如果这样做,堆栈如何知道哪个进程获得连接? SO_REUSEADDR 与此无关。

来自What exactly does SO_REUSEADDR do?

This socket option tells the kernel that even if this port is busy (in the TIME_WAIT state), go ahead and reuse it anyway. If it is busy, but with another state, you will still get an address already in use error. It is useful if your server has been shut down, and then restarted right away while sockets are still active on its port. You should be aware that if any unexpected data comes in, it may confuse your server, but while this is possible, it is not likely.

换句话说,如果您已经关闭了套接字,但它仍在等待连接静止(接收 FIN/ACK 或超时),您可以立即再次获取它。您永远不能让两个进程同时连接到同一个端点。

关于java - "java.net.BindException: Address already in use"尽管设置了 SO_REUSEADDR,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21210761/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com