Java 使用同一主机创建套接字
在Java中,当创建具有相同参数的套接字时(忽略此代码将引发Java 使用同一主机创建套接字,java,sockets,networking,Java,Sockets,Networking,在Java中,当创建具有相同参数的套接字时(忽略此代码将引发ConnectException): s1是否等于s2? 我的意思是,关闭s1会对s2产生同样的影响吗 如果没有,我如何关闭一个没有引用的套接字(只有创建它时使用的ip和端口) 谢谢。答案是否定的。这不会像你预期的那样。在任何给定时间只能建立一个到端口的连接(不包括行为稍有不同的异步调用) 如果您仔细检查代码并进行检查,您应该会发现s1设置为已连接,而s2设置为已断开连接 至于如何关闭一个没有引用的套接字,它不会以这种方式工作。您必须创
ConnectException
):
s1
是否等于s2
?
我的意思是,关闭s1
会对s2
产生同样的影响吗
如果没有,我如何关闭一个没有引用的套接字(只有创建它时使用的ip和端口)
谢谢。答案是否定的。这不会像你预期的那样。在任何给定时间只能建立一个到端口的连接(不包括行为稍有不同的异步调用) 如果您仔细检查代码并进行检查,您应该会发现s1设置为已连接,而s2设置为已断开连接
至于如何关闭一个没有引用的套接字,它不会以这种方式工作。您必须创建套接字实例,并在整个程序执行过程中维护它。如果引用丢失或已处理,我相信您必须重新创建套接字对象。
s1
和s2
引用两个不同的套接字
实例,因此关闭其中一个不会对另一个产生直接影响
但是,仅仅因为两个套接字连接到同一地址和端口,您不会得到连接异常。只要目标服务器能够接受,就不会阻止您将两个套接字连接到相同的目标地址和端口。当您仅指定目标地址/端口对时,操作系统会自动从为套接字分配一个本地端口(编号在1024以上)。重要的是(远程地址、远程端口、本地地址、本地端口)
组合(称为4元组)是唯一的;这就是当套接字需要将传入的数据包与其对应的套接字进行匹配时,底层TCP/IP堆栈如何区分套接字,以及服务器如何能够在同一端口上接受多个连接
如果你要做:
Socket s1 = new Socket();
s1.bind(new InetSocketAddress("127.0.0.1", 7070));
s1.connect(new InetSocketAddress("127.0.0.1", SERVER_PORT));
Socket s2 = new Socket();
s2.bind(new InetSocketAddress("127.0.0.1", 7070));
s2.connect(new InetSocketAddress("127.0.0.1", SERVER_PORT));
然后,您将在第二个套接字的绑定尝试中得到一个BindException
,因为只有服务器套接字才允许创建绑定到同一源端口的套接字。这是因为在accept()
期间,4元组是完全已知的,并且可以确定其唯一性。有更多的细节
下面是一个简单的测试类,演示当多个套接字连接到同一目标主机和端口时会发生什么:
public class ConnectTest {
static ServerSocket serverSock;
static List<Socket> acceptedSockets = Collections.synchronizedList(new ArrayList<Socket>());
static final int SERVER_PORT = 55555;
static class Server implements Runnable {
@Override
public void run() {
try {
serverSock = new ServerSocket();
serverSock.bind(new InetSocketAddress("127.0.0.1", 55555));
while (true)
{ acceptedSockets.add(serverSock.accept()); }
} catch (IOException e) { e.printStackTrace(); }
}
}
public static void main(String[] args) throws Exception {
new Thread(new Server()).start();
List<Socket> clientSockets = new ArrayList<Socket>();
// open 10 sockets to the same target address/port
for (int i = 0; i < 10; i++) {
Socket s = new Socket("127.0.0.1", 55555);
System.out.println("Local address: " + s.getLocalSocketAddress() +
" Remote address: " + s.getRemoteSocketAddress());
clientSockets.add(s);
}
// now close half
for (Socket s : clientSockets.subList(0, 5))
s.close();
// now list them again
System.out.println("\n\n Socket list after some closed:");
for (Socket s : clientSockets) {
if (s.isClosed()) {
System.out.println("* Socket Closed *");
} else {
System.out.println("Local address: " + s.getLocalSocketAddress() +
" Remote address: " + s.getRemoteSocketAddress());
}
}
}
首先,您可以看到,要使套接字唯一,所需的只是使4元组的一个部分不同。尽管我们从127.0.0.1连接到127.0.0.1端口55555,但源端口是唯一的这一事实就足够了
套接字由它们的引用变量标识,这些引用变量对应于套接字描述符。当JVM需要请求操作系统执行操作时,它们不会被混淆
我认为,当您没有对套接字的引用时,强制关闭套接字是不可能的。我能想到的唯一方法是调用底层操作系统的本机库(通过JNI)或系统实用程序(使用Runtime.getRuntime().exec(command)
或类似工具)。您需要通过其4元组来识别连接,然后请求关闭连接,但您需要具有更高的权限才能这样做。如果您可以这样做,(即通过s2
关闭s1
的套接字),那么s1
的用户将非常恼火……感谢您的回答。“在任何给定时间,只能建立一个到端口的连接(不包括行为稍有不同的异步调用)。“这是不正确的,重要的是4元组是唯一的,请参见下文。”“异步调用”是一种转移注意力的手段,它们与此无关。@willcroz承认我对网络编程的理解是有限的。谢谢您的更正。@ibeita此答案不正确。s1和s2都将被连接,除非任一构造函数引发异常。
public class ConnectTest {
static ServerSocket serverSock;
static List<Socket> acceptedSockets = Collections.synchronizedList(new ArrayList<Socket>());
static final int SERVER_PORT = 55555;
static class Server implements Runnable {
@Override
public void run() {
try {
serverSock = new ServerSocket();
serverSock.bind(new InetSocketAddress("127.0.0.1", 55555));
while (true)
{ acceptedSockets.add(serverSock.accept()); }
} catch (IOException e) { e.printStackTrace(); }
}
}
public static void main(String[] args) throws Exception {
new Thread(new Server()).start();
List<Socket> clientSockets = new ArrayList<Socket>();
// open 10 sockets to the same target address/port
for (int i = 0; i < 10; i++) {
Socket s = new Socket("127.0.0.1", 55555);
System.out.println("Local address: " + s.getLocalSocketAddress() +
" Remote address: " + s.getRemoteSocketAddress());
clientSockets.add(s);
}
// now close half
for (Socket s : clientSockets.subList(0, 5))
s.close();
// now list them again
System.out.println("\n\n Socket list after some closed:");
for (Socket s : clientSockets) {
if (s.isClosed()) {
System.out.println("* Socket Closed *");
} else {
System.out.println("Local address: " + s.getLocalSocketAddress() +
" Remote address: " + s.getRemoteSocketAddress());
}
}
}
Local address: /127.0.0.1:43088 Remote address: /127.0.0.1:55555
Local address: /127.0.0.1:43089 Remote address: /127.0.0.1:55555
Local address: /127.0.0.1:43090 Remote address: /127.0.0.1:55555
Local address: /127.0.0.1:43091 Remote address: /127.0.0.1:55555
Local address: /127.0.0.1:43092 Remote address: /127.0.0.1:55555
Local address: /127.0.0.1:43093 Remote address: /127.0.0.1:55555
Local address: /127.0.0.1:43094 Remote address: /127.0.0.1:55555
Local address: /127.0.0.1:43095 Remote address: /127.0.0.1:55555
Local address: /127.0.0.1:43096 Remote address: /127.0.0.1:55555
Local address: /127.0.0.1:43097 Remote address: /127.0.0.1:55555
Socket list after some closed:
* Socket Closed *
* Socket Closed *
* Socket Closed *
* Socket Closed *
* Socket Closed *
Local address: /127.0.0.1:43093 Remote address: /127.0.0.1:55555
Local address: /127.0.0.1:43094 Remote address: /127.0.0.1:55555
Local address: /127.0.0.1:43095 Remote address: /127.0.0.1:55555
Local address: /127.0.0.1:43096 Remote address: /127.0.0.1:55555
Local address: /127.0.0.1:43097 Remote address: /127.0.0.1:55555