s1
и s2
относятся к двум различным Socket
экземплярам, поэтому закрытие одного не окажет прямого влияния на другое.
Вы не получите ConnectException
только из-за того, что два сокета соединяются с одним и тем же адресом и портом. Ничто не мешает подключить два сокета к одному и тому же целевому адресу и порту, если целевой сервер может их принять. Когда вы указываете только целевую пару адрес / порт, операционная система автоматически назначает локальный порт для сокета из диапазона временных портов (нумерация где-то выше 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-кортеж полностью известен и его уникальность может быть определена. Этот вопрос SO содержит более подробную информацию.
Вот простой тестовый класс, демонстрирующий, что происходит, когда несколько сокетов подключаются к одному и тому же целевому хосту и порту:
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 к порту 12755.0.1 55555, факта, что порт источника уникален, достаточно.
Сокеты идентифицируются по их ссылочным переменным, которые соответствуют дескрипторам сокетов, когда JVM необходимо запросить ОС для выполнения операций, их невозможно перепутать.
Принудительно закрыть сокет, если у вас нет ссылки на него, я думаю, невозможно. Единственный способ сделать это, о котором я могу подумать, - это вызвать собственные библиотеки базовой операционной системы (через JNI) или системные утилиты (используя Runtime.getRuntime().exec(command)
или аналогичные). Вам нужно будет идентифицировать соединение по его четырем кортежам, а затем запросить его закрытие, но для этого вам потребуются повышенные привилегии.