У меня может быть фундаментальное непонимание термина «привязка» здесь, но меня смущает использование MulticastSocket и его конструкторы. Они не делают того, что я понимаю, что они должны делать, должны делать, поэтому любой, кто может помочь мне прояснить мое недоразумение, будет признателен.
Во-первых, чего я пытаюсь достичь. Я попытался написать короткую программу, которая создает MulticastSocket, связывает его (то есть прослушивает) на конкретном сетевом адаптере, а затем присоединяется к определенной группе многоадресной рассылки. Я пробовал следующий (клиентский) код, который работает нормально, я могу многоадресно передать ему пакет без тайм-аута многоадресного сокета.
public class Main {
public static final int DEFAULT_MULTICAST_PORT = 5555;
public static final String multicastGroup = "225.4.5.6";
public static final String adapterName = "eth0";
public static final int MAX_PACKET_SIZE = 65507;
CharBuffer charBuffer = null;
Charset charset = Charset.defaultCharset();
CharsetDecoder decoder = charset.newDecoder();
static ByteBuffer message = ByteBuffer.allocateDirect(MAX_PACKET_SIZE);
static boolean loop = true;
static byte[] buffer = new byte[MAX_PACKET_SIZE];
public static void main(String[] args) {
try {
//MulticastSocket mSocket = new MulticastSocket(new InetSocketAddress("192.168.2.23", DEFAULT_MULTICAST_PORT));
MulticastSocket mSocket = new MulticastSocket(DEFAULT_MULTICAST_PORT);
mSocket.setReuseAddress(true);
mSocket.setSoTimeout(5000);
NetworkInterface nic = NetworkInterface.getByName(adapterName);
mSocket.joinGroup(new InetSocketAddress(multicastGroup, DEFAULT_MULTICAST_PORT),NetworkInterface.getByName(adapterName));
DatagramPacket p = new DatagramPacket(buffer, MAX_PACKET_SIZE);
while (loop){
try{
mSocket.receive(p);
System.out.println("Packet Received.");
} catch (SocketTimeoutException ex){
System.out.println("Socket Timed out");
}
}
} catch (IOException ex){
System.err.println(ex);
}
}
}
К сожалению, как только я изменяю конструктор MulticastSocket на MulticastSocket(SocketAddress bindaddr)
, он перестает работать. Кажется, я могу использовать только конструктор привязки к порту, чтобы он работал, так что именно он привязывается при вызове этого конструктора, поскольку на данном этапе я не указал сетевой адаптер. (Я знаю, что позже присоединюсь к группе с определенным NetworkInterface, но как я могу быть уверен, что во время вызова конструктора он не привязывается к ЛЮБОМУ адаптеру?)
Я также мог присоединиться к группе, не указывая адаптер, тогда я не знаю, к какому адаптеру он привязан.
Может ли кто-нибудь объяснить, что на самом деле делает привязка к порту only и возможно ли прослушивание только на определенном NetworkInterface?
Обновлено №1 **
Прочитав ответы и обсудив это с коллегой по работе, я понял, что такое Java MulticastSocket:
- MulticastSocket () создает многоадресный сокет, привязанный к порту, выбранному случайным образом (базовой ОС хоста, привязанной к подстановочному адресу 0.0.0.0, то есть ВСЕМ сетевым картам. Однако вызов этого конструктора с null создает несвязанный MulticastSocket. В этом сценарии вызов метода bind (SocketAddress) привязывается к IP-адресу и порту.
- MulticastSocket (int port) создает сокет многоадресной рассылки, привязанный к определенному порту, но на КАЖДОМ IP-адресе.
- MulticastSocket (SocketAddress sa) создает сокет многоадресной рассылки, привязанный к указанному IP-адресу (который может быть ЛЮБЫМ IP-адресом, даже недопустимым адресом многоадресной рассылки) и портом.
Использование варианта 2 означает, что потенциально любой пакет, отправленный на указанный порт, независимо от его фактического назначения, будет передан в MulticastSocket. Я говорю потенциально, потому что многоадресные пакеты будут приходить только в том случае, если группа была присоединена (но другие пакеты, предназначенные для адресов, не относящихся к многоадресной рассылке, будут приходить при условии совпадения номера порта?)
Используя вариант 3, я могу привязаться к IP-адресу и только пакетам, чье место назначения совпадает, будут приходить в сокет. С этой опцией было бы вполне возможно выполнить привязку к IP-адресу определенного сетевого интерфейса, однако многоадресные пакеты не будут получены, потому что они не будут предназначены для определенного IP-адреса сетевой карты (вот почему я никогда не видел, чтобы они приходили на пример кода). Также можно выполнить привязку к допустимому адресу многоадресной рассылки, но в этом сценарии только определенные пакеты, место назначения которых совпадает с привязанным адресом многоадресной рассылки, будут приходить в сокет, независимо от вызовов joinGroup()
.
Теперь вызовы joinGroup()
не делают ничего для самого сокета, но выдают IGMP-запрос к базовой сетевой системе, чтобы гарантировать, что маршрутизаторы, сама ОС и т. Д. Действительно начинают маршрутизацию указанной многоадресной рассылки. пакетирует оборудование и сетевой стек и, наконец, сам Java MulticastSocket.
** Обновление 2 ** Цитата из "Сетевое программирование UNIX", Стивенс, Феннер, Рудофф:
Чтобы получить дейтаграмму многоадресной рассылки, процесс должен присоединиться к группе многоадресной рассылки, а также должен привязать сокет UDP к номеру prot, который будет использоваться в качестве номера порта назначения для дейтаграмм, отправленных в группу. Эти две операции различны и требуются обе. Присоединение к группе сообщает IP-уровню хоста и уровню канала данных о получении многоадресных дейтаграмм, отправленных в эту группу. Привязка порта - это то, как приложение указывает UDP, что оно хочет получать дейтаграммы, отправленные на этот порт. Некоторые приложения также связывают многоадресный адрес с сокетом в дополнение к порту. это предотвращает доставку в сокет любых других дейтаграмм, которые могут быть получены для этого порта на другие одноадресные, широковещательные или многоадресные адреса.
Думаю, это все объясняет.
** Обновление 3 ** Просто хотел опубликовать протестированный мной код, и в комментариях объясняется, что происходит с каждым из них.
/**
* This first creates an UNBOUND Multicast Socket and then binds to
* a port (but accepting the wildcard IP 0.0.0.0.
* The Following WORKS:
*/
/*MulticastSocket mSocket = new MulticastSocket(null);
mSocket.bind(new InetSocketAddress(DEFAULT_MULTICAST_PORT));
mSocket.setReuseAddress(true);
mSocket.setSoTimeout(5000);
NetworkInterface nic = NetworkInterface.getByName(adapterName);
mSocket.joinGroup(InetAddress.getByName(multicastGroup));
*/
/**
* The following creates a a network socket and binds in the constructor
* to a local adapter and port. Consequently it DOES not work because
* it only allows destination ips that match the bound address & port
* even though the desired group is joined.
*/
/*MulticastSocket mSocket = new MulticastSocket(new InetSocketAddress("192.168.2.23", DEFAULT_MULTICAST_PORT));
mSocket.setReuseAddress(true);
mSocket.setSoTimeout(5000);
NetworkInterface nic = NetworkInterface.getByName(adapterName);
mSocket.joinGroup(InetAddress.getByName(multicastGroup));*/
/**
* The following binds to the same multicast group this is 'joined' later
* and this works correctly. However if the join() is NOT called, no packets
* arrive at the socket, as expected.
*/
/*MulticastSocket mSocket = new MulticastSocket(new InetSocketAddress(multicastGroup, DEFAULT_MULTICAST_PORT));
mSocket.setSoTimeout(5000);
NetworkInterface nic = NetworkInterface.getByName(adapterName);
// Comment out the following line and it no longer workds correctly.
mSocket.joinGroup(InetAddress.getByName(multicastGroup));*/
/**
* The following binds to a a specified port on 0.0.0.0 and joins
* a specific Multicast group on a specific adapter. This must mean that the IGMP must occur
* on the specified adapter.
*
* ** This will ALSO receive packets addressed DIRECTLY to the ip 192.168.2.23 with the same
* port as DEFAULT_MULTICAST_POR ***ONLY!!***
*/
MulticastSocket mSocket = new MulticastSocket(DEFAULT_MULTICAST_PORT);
mSocket.setReuseAddress(true);
mSocket.setSoTimeout(5000);
NetworkInterface nic = NetworkInterface.getByInetAddress(InetAddress.getByName("192.168.2.23"));
mSocket.joinGroup(new InetSocketAddress(multicastGroup, DEFAULT_MULTICAST_PORT),NetworkInterface.getByName(adapterName));
/**
* The following binds to a specific address and port (i.e. adapter address)
* and then ONLY accepts UDP packets with destination equal to that IP.
*/
/*MulticastSocket mSocket = new MulticastSocket(new InetSocketAddress("192.168.2.23", DEFAULT_MULTICAST_PORT));
mSocket.setReuseAddress(true);
mSocket.setSoTimeout(5000);
NetworkInterface nic = NetworkInterface.getByInetAddress(InetAddress.getByName("192.168.2.23"));*/