2

UDP パケットを複数の受信者に送信する方法を実装しようとしています。setReuseAddress(true)これは、受信する DatagramSocket インスタンスで実行可能な設定であるべきだと思いました。

私の問題は、特定の状況ではローカル コンピューターへの通信を制限する必要があることです。つまり、localhost インターフェイス (以下のデモ コードでは useLocalhost=true) です。このような場合、突然、最初の受信側ソケットのみが着信パケットを取得し、他の 2 つの受信側ソケットには何も表示されなくなります。

これを Windows (oracle 64 ビット) と Linux (OpenJDK 64 ビット) でテストしたため、次の 3 つの可能性しかありません。

  1. これは意図された既知の動作です (そして、私はメカニズム全体を理解していません - 別名「脳のバグ」)
  2. Java JRE にバグがあります
  3. 私のコードにバグがあります。

誰かがそのトピックに関する経験があり、問題がどこにあるかを特定するのを手伝ってもらえますか?

これを示す最小限の実例を以下に示します。実際の外部ホストからのネットワーク パケットをシミュレートするためにブロードキャスト アドレスを使用していることに注意してください。

すべてがうまくいけば、最後に 3 行が表示されます (この順序または別の順序で):

Thread-0 - packet received
Thread-1 - packet received
Thread-2 - packet received

 

public static void main(String[] args) throws Exception {

    boolean useLocalhost = true;

    InetSocketAddress addr;
    String sendPacketTo = "192.168.1.255"; // we use broadcast so that packet comes from an real external address
    if (useLocalhost)
        sendPacketTo = "localhost"; // does not work (only listener 1 received packet)

    addr = new InetSocketAddress(15002);

    new MyThread(addr).start(); // Datagram socket listener 1
    new MyThread(addr).start(); // Datagram socket listener 2
    new MyThread(addr).start(); // Datagram socket listener 3

    DatagramSocket so = new DatagramSocket();
    so.setBroadcast(true); // does not change anything
    so.connect(new InetSocketAddress(sendPacketTo, 15002));
    so.send(new DatagramPacket("test".getBytes(), 4));
    Thread.sleep(1000);
    System.exit(0);
}

public static class MyThread extends Thread {

    DatagramSocket socket;

    public MyThread(InetSocketAddress addr) throws SocketException {
        super();
        setDaemon(true);
        socket = new DatagramSocket(null);
        socket.setReuseAddress(true);
        socket.setBroadcast(true); // does not change anything
        socket.bind(addr);
        System.out.println("Listener started: " + socket.getLocalAddress());
    }

    public void run() {
        byte[] buf = new byte[10];
        DatagramPacket p = new DatagramPacket(buf, buf.length);
        try {
            socket.receive(p);
            System.out.println(Thread.currentThread().getName() + " - packet received");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
4

2 に答える 2

1

192.168.1.255 はブロードキャスト アドレスであるため、UDP ブロードキャストのルールに従ってデータグラムがブロードキャストされます。127.0.0.1 はユニキャスト アドレスなので、パケットはユニキャストです。そのため、異なる動作が得られます。

@DavidSchwartz がコメントしたように、コードは混在しています。たとえば、ブロードキャスト アドレスへの接続にはあまり意味がなく、それにバインドすることもありません。あなたが探しているのはマルチキャストだと思います。

于 2013-02-18T16:59:28.513 に答える
0

localhost でマルチキャストを使用できます。ただし、これを機能させるにはいくつかの注意が必要です。

例: lo0 (127.0.0.1) en0 (192.168.0.111) en1 (10.1.0.111)

  1. インターフェースごとに 2 つの別個のソケット (1 つは受信用、もう 1 つは送信用)。上記の例では、これは合計 6 つのソケットを作成することを意味します。
  2. マルチキャスト UDP パケットを送信するソケットを bind() しないでください。
  3. マルチキャスト UDP パケットを受信するソケットは常に bind() してください。bind() を呼び出した後は、setsockopt() やマルチキャスト ソケットの再構成を試みないでください。代わりに、ケーブルのプラグが抜かれたり差し込まれたりしたためにマシンのインターフェイスが変更された場合は、すべての送信/受信マルチキャスト ソケットを破棄して再作成してください。

サンプル コード: iMulticastSocketInterfaceIPAddress は、3 つのインターフェイスのいずれかになります。

     /* use setsockopt() to request that the kernel join a multicast group */
     struct ip_mreq mreq;
     mreq.imr_multiaddr.s_addr=inet_addr( "239.192.0.133" );
     myAddress.sin_addr.s_addr = mreq.imr_multiaddr.s_addr;         
     mreq.imr_interface.s_addr=( htonl(iMulticastSocketInterfaceIPAddress) );
     theErr = setsockopt( CFSocketGetNative( mSocketBroadcast ) ,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
于 2014-01-02T19:11:21.947 に答える