3

ブロードキャストを介して UDP クライアント サーバー検出を実装しようとしています。アイデアは次のとおりです。ポート 12344 にバインドされたサーバーとポート 12345 にバインドされたクライアントがあります。ここで、クライアントはブロードキャスト パッケージを 255.255.255.255 12344 に送信します。サーバーはこのパッケージに応答する必要があります。 IPClient:12345 への他のパッケージ。

実装は Java nio を使用します。

問題は、Windows では、サーバーがパッケージを取得するが、応答を送信できない (?) ことです (wireshark に応答が表示されません)。

次のコード例があります。

クライアント

public final class ASyncUDPClient {
public static void main(String[] args) throws IOException {
  InetSocketAddress hostAddress = new InetSocketAddress("255.255.255.255", 12344);
  System.out.println(hostAddress);

  // Create a non-blocking socket channel
  DatagramChannel channel = DatagramChannel.open();
  channel.socket().setBroadcast(true);
  channel.socket().bind(new InetSocketAddress(getAddress(), 12345));
  channel.configureBlocking(false);

  // Kick off connection establishment
  channel.connect(hostAddress);

  ByteBuffer buffer = getBuffer();

  Selector selector = Selector.open();
  channel.write(buffer);
  System.out.println("data send");
  channel.register(selector, SelectionKey.OP_READ);

  while (true) {
    final int select = selector.select();
    System.out.println("select " + select);
    Iterator selectedKeys = selector.selectedKeys().iterator();
    while (selectedKeys.hasNext()) {
      System.out.println("key selected");
      SelectionKey key = (SelectionKey) selectedKeys.next();
      selectedKeys.remove();

      if (!key.isValid()) {
        continue;
      }

      if (key.isReadable()) {
        System.out.println("read");
      } else if (key.isWritable()) {
        System.out.println("write");
      }
    }
  }
}

private static ByteBuffer getBuffer() throws CharacterCodingException {
  return Charset.forName("UTF-8").newEncoder().encode(CharBuffer.wrap("1234"));
}

private static InetAddress getAddress() throws SocketException {
  final Enumeration<NetworkInterface> networkInterfaces =   NetworkInterface.getNetworkInterfaces();
  NetworkInterface networkInterfaceToUse = null;
  while (networkInterfaces.hasMoreElements()) {
    final NetworkInterface networkInterface = networkInterfaces.nextElement();
    if (networkInterface.getDisplayName().contains("Virtual")) continue;
    if (networkInterface.isVirtual()) continue;
    if (networkInterface.isLoopback()) continue;
    if (!networkInterface.isUp()) continue;
    networkInterfaceToUse = networkInterface;
    System.out.println(networkInterfaceToUse);
  }
  return networkInterfaceToUse.getInterfaceAddresses().get(1).getAddress();
}

}

サーバーの例

public class ASyncUDPSvr {

static int BUF_SZ = 1024;
static int port = 12344;

static public void main(String[] args) {
  ASyncUDPSvr svr = new ASyncUDPSvr();
  svr.process();
}

private static InetAddress getAddress() throws SocketException {
  final Enumeration<NetworkInterface> networkInterfaces =    NetworkInterface.getNetworkInterfaces();
  NetworkInterface networkInterfaceToUse = null;
  while (networkInterfaces.hasMoreElements()) {
    final NetworkInterface networkInterface = networkInterfaces.nextElement();
    if (networkInterface.getDisplayName().contains("Virtual")) continue;
    if (networkInterface.isVirtual()) continue;
    if (networkInterface.isLoopback()) continue;
    if (!networkInterface.isUp()) continue;
    networkInterfaceToUse = networkInterface;
    System.out.println(networkInterfaceToUse);
  }
  return networkInterfaceToUse.getInterfaceAddresses().get(1).getAddress();
}

private void process() {
  try {
    Selector selector = Selector.open();
    DatagramChannel channel = DatagramChannel.open();
    InetSocketAddress isa = new InetSocketAddress(getAddress(), port);
    channel.socket().bind(isa);
    channel.configureBlocking(false);
    SelectionKey clientKey = channel.register(selector, SelectionKey.OP_READ);
    clientKey.attach(new Con());
    while (true) {
      try {
        selector.select();
        Iterator selectedKeys = selector.selectedKeys().iterator();
        while (selectedKeys.hasNext()) {
          try {
            SelectionKey key = (SelectionKey) selectedKeys.next();
            selectedKeys.remove();
            if (!key.isValid()) {
              continue;
            }
            if (key.isReadable()) {
              read(key);
              key.interestOps(SelectionKey.OP_WRITE);
            } else if (key.isWritable()) {
              write(key);
              key.interestOps(SelectionKey.OP_READ);
            }
          } catch (IOException e) {
            System.err.println("glitch, continuing... " + (e.getMessage() != null ? e.getMessage() : ""));
          }
        }
      } catch (IOException e) {
        System.err.println("glitch, continuing... " + (e.getMessage() != null ? e.getMessage() : ""));
      }
    }
  } catch (IOException e) {
    System.err.println("network error: " + (e.getMessage() != null ? e.getMessage() : ""));
  }
}

private void read(SelectionKey key) throws IOException {
  DatagramChannel chan = (DatagramChannel) key.channel();
  Con con = (Con) key.attachment();
  con.sa = chan.receive(con.req);
  System.out.println("sender address: " + con.sa + "rcv: " + new   String(con.req.array(), "UTF-8"));
  con.resp = Charset.forName("UTF-8").newEncoder().encode(CharBuffer.wrap("send string"));
}

private void write(SelectionKey key) throws IOException {
  DatagramChannel chan = (DatagramChannel) key.channel();
  Con con = (Con) key.attachment();
  System.out.println("sending data: " + new String(con.resp.array(), "UTF-8") + " to "   + con.sa);
  chan.send(con.resp, con.sa);
  System.out.println("data send");
}

class Con {

  ByteBuffer req;
  ByteBuffer resp;
  SocketAddress sa;

  public Con() {
    req = ByteBuffer.allocate(BUF_SZ);
  }
}
}
4

2 に答える 2

1

受け入れられた答えは真実ではありません。最初に設定したときに、チャネルをブロードキャストアドレスに「接続」できます。

channel.socket().setBroadcast(true);

もちろん、UDP はコネクションレス プロトコルですが、「接続された」DatagramChannel にはいくつかの利点があります。その 1 つは、write(ByteBuffer[])メソッドを使用できることです。

于 2014-03-21T17:02:17.893 に答える