ここでバインディングという用語について根本的な誤解があるかもしれませんが、 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 を持つグループに参加することはわかっていますが、コンストラクターの呼び出し中にどのアダプターにもバインドされていないことをどのように確認できますか?)
アダプターを指定せずにグループに参加することもできますが、それがどのアダプターにバインドされているかわかりません。
ポートへのバインディングが実際に行うことだけを説明できる人はいますか?特定の NetworkInterface でのみリッスンすることは可能ですか?
更新 #1 **
これまでの返信を読み、これについて同僚と話し合った結果、Java MulticastSocket に関する私の理解は次のとおりです。
- MulticastSocket() は、ランダムに選択されたポートにバインドされたマルチキャスト ソケットを作成します (ワイルドカード アドレス 0.0.0.0、つまりすべてのネットワーク カードにバインドされたホストの基になる OS によって)。ただし、このコンストラクタをnullで呼び出すと、バインドされていない MulticastSocket が作成されます。このシナリオでは、`bind を呼び出します(SocketAddress) メソッドは IP とポートにバインドします。
- MulticastSocket(int port) は、特定のポートにバインドされたマルチキャスト ソケットをすべての IP アドレス上に作成します。
- MulticastSocket(SocketAddress sa) は、指定された IP アドレス (無効なマルチキャスト アドレスであっても、任意の IP アドレスである可能性があります) およびポートにバインドされたマルチキャスト ソケットを作成します。
オプション 2 を使用すると、実際の宛先に関係なく、指定されたポートに送信されたすべてのパケットが MulticastSocket に渡される可能性があることを意味します。グループに参加している場合にのみマルチキャストパケットが到着する可能性があるためです(ただし、ポート番号が一致する場合、マルチキャスト以外のアドレス宛ての他のパケットは到着しますか?)
オプション 3 を使用すると、IP アドレスにバインドでき、宛先が一致するパケットのみがソケットに到着します。このオプションを使用して、特定のネットワーク インターフェイスの IP にバインドすることは完全に可能ですが、ネットワーク カードの特定の IP アドレス宛てではないため、マルチキャスト パケットは受信されません (これが、マルチキャスト パケットがネットワーク カードに到着するのを見たことがない理由です)。コード例)。有効なマルチキャスト アドレスにバインドすることもできますが、このシナリオでは、 への呼び出しに関係なく、宛先がバインドされたマルチキャスト アドレスと一致する特定のパケットのみがソケットに到着します。joinGroup()
joinGroup()
ソケット自体には何もせず、基盤となるネットワーク システムに IGMP リクエストを発行して、ルーターや OS 自体が実際に指定されたマルチキャスト パケットのルーティングを開始し、ハードウェアを経由してネットワーク スタックを経由し、最後に Java MulticastSocket に到達するようにするための呼び出し自体。
** Update 2 ** 「UNIX ネットワーク プログラミング」、Stevens、Fenner、Rudoff からの引用:
マルチキャスト データグラムを受信するには、プロセスがマルチキャスト グループに参加する必要があり、グループに送信されるデータグラムの宛先ポート番号として使用される prot 番号に UDP ソケットをバインドする必要もあります。2 つの操作は別個のものであり、両方が必要です。グループに参加すると、ホストの IP 層とデータリンク層が、そのグループに送信されたマルチキャスト データグラムを受信するように指示されます。ポートのバインドは、アプリケーションがそのポートに送信されたデータグラムを受信することを UDP に指定する方法です。一部のアプリケーションは、ポートに加えて、マルチキャスト アドレスもソケットにバインドします。これにより、そのポートで他のユニキャスト、ブロードキャスト、またはマルチキャスト アドレスに受信される可能性のある他のデータグラムがソケットに配信されなくなります。
それがすべてを説明していると思います。
** Update 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"));*/