Java nio udp广播

时间:2013-07-04 14:26:43

标签: java udp nio

我尝试通过广播实现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);
  }
}
}

2 个答案:

答案 0 :(得分:4)

InetSocketAddress hostAddress = new InetSocketAddress("255.255.255.255", 12344);
// ...
channel.connect(hostAddress);

问题出在这里。您无法连接到广播地址,无论如何它都没有意义。您发送给它的广播地址不会发送给您。服务器从其自己的绑定地址发送给您。只需删除此行即可。你必须使用DatagramChannel.send()而不是write(),因为你没有连接。

答案 1 :(得分:1)

接受的答案是不正确的。首次设置时,您可以将频道“连接”到广播地址:

channel.socket().setBroadcast(true);

当然UDP是一种无连接协议,但“连接”DatagramChannel有一些好处,其中之一就是能够使用write(ByteBuffer[])方法。