Создание сокета с тем же хостом - PullRequest
2 голосов
/ 22 мая 2011

В 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 и портом, с которым он был создан).

Спасибо.

Ответы [ 2 ]

3 голосов
/ 23 мая 2011

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) или аналогичные). Вам нужно будет идентифицировать соединение по его четырем кортежам, а затем запросить его закрытие, но для этого вам потребуются повышенные привилегии.

1 голос
/ 22 мая 2011

Ответ - нет.Это не будет вести себя так, как вы ожидаете.Только одно соединение с портом может быть установлено в любой момент времени (исключая асинхронные вызовы, которые ведут себя несколько иначе).

Если вы пошагово просмотрели код и проверили, вы должны обнаружить, что s1 настроен на подключение, а s2отключен.

Что касается того, как закрыть сокет, на который у вас нет ссылки, он не работает таким образом.Вы должны создать экземпляр Socket и поддерживать его на протяжении всего выполнения вашей программы.Если ссылка была утеряна или удалена, я считаю, что вы должны воссоздать объект Socket.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...