3

I try to implement a UDP client server detection via broadcast. The idea is the following: I have a server, which is bound to port 12344 and a client which is bound to port 12345. Now, the client sends a broadcast package to 255.255.255.255 12344. The server should reply to this package with a other package to IPClient:12345.

The implementation uses Java nio.

The problem is, that on windows, the server gets the packages but cannot(?) send an answer (I don't see the answer in wireshark).

I have the following example code:

Client

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();
}

}

Server example

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);
  }
}
}
morpheus05
  • 4,772
  • 2
  • 32
  • 47

2 Answers2

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

The problem is here. You can't connect to the broadcast address, and in any case it doesn't make sense. The broadcast address isn't sending to you, you are sending to it. The server is sending to you from its own bind-address. Just remove this line. You will have to use DatagramChannel.send() rather than write(), as you are unconnected.

user207421
  • 305,947
  • 44
  • 307
  • 483
1

The accepted answer is not true. You can "connect" the channel to the broadcast address when you first set:

channel.socket().setBroadcast(true);

Of course UDP is a connectionless protocol, but a "connected" DatagramChannel has some benefits, one of them is the ability to use the write(ByteBuffer[]) method.

br1
  • 425
  • 4
  • 9
  • `setBroadcast(true)` does not constitute a "connect" to the broadcast address. That would look like the code I quoted in my answer, which doesn't work. – user207421 Oct 29 '15 at 22:17
  • What I meant is that the code you mentioned above is possible after you set `setBroadcast(true)` and then you can use the socket like a connected socket. Obviously it's not really connected to the broadcast address, but you can use `write()`. – br1 Nov 06 '15 at 13:21