在Java中,创建具有相同参数的套接字时(忽略此代码将抛出ConnectException
的事实):
Socket s1 = new Socket("127.0.0.1", 7575);
Socket s2 = new Socket("127.0.0.1", 7575);
s1
是否等于s2
?
我的意思是,关闭s1
会对s2
产生同样的效果吗?
如果没有,我怎么能关闭一个我没有引用的套接字(只有创建它的ip和端口)。
感谢。
答案 0 :(得分:3)
s1
和s2
引用两个不同的Socket
个实例,因此关闭一个实例对另一个实例没有直接影响。
由于两个套接字连接到同一个地址和端口,你不会得到ConnectException
。只要目标服务器能够接受它们,就没有什么能阻止您将两个套接字连接到同一目标地址和端口。当您仅指定目标地址/端口对时,操作系统会自动从ephemeral port range(编号大于1024的某个位置)为套接字分配本地端口。重要的是(remote address, remote port, local address, local port)
组合(称为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元组是完全已知的,并且可以确定其唯一性。 This SO question有更详细的信息。
这是一个简单的测试类,用于演示当多个套接字连接到同一目标主机和端口时会发生什么:
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
首先你可以看到套接字唯一需要的只是4元组的一部分是不同的。即使我们从127.0.0.1连接到127.0.0.1端口55555,源端口是唯一的这一事实就足够了。
当JVM需要请求操作系统执行操作时,套接字由它们的引用变量标识,这些引用变量对应于套接字描述符,它们无法混淆。
当你没有对它进行引用时强制关闭套接字是不可能的。我能想到的唯一方法就是调用底层操作系统的本机库(通过JNI)或系统实用程序(使用Runtime.getRuntime().exec(command)
或类似)。您需要通过其4元组识别连接,然后请求将其关闭,但您需要具有提升权限才能执行此操作。
答案 1 :(得分:1)
答案是否定的。这不会像你期待的那样表现。在任何给定时间只能建立一个到端口的连接(不同的异步调用,行为稍有不同)。
如果您单步检查并检查,则应该发现s1设置为在断开s2时连接。
至于如何关闭一个你没有引用的套接字,它不会那样运行。您必须创建Socket的实例并在程序执行期间维护它。如果引用丢失或丢失,我相信你必须重新创建Socket对象。