2013-07-04 163 views
2

我嘗試通過廣播實現UDP客戶端服務器檢測。這個想法如下:我有一個服務器,它被綁定到端口12344和一個綁定到端口12345的客戶端。現在,客戶端發送一個廣播包到255.255.255.255 12344.服務器應該回復這個包其他包到IPClient:12345。Java nio udp廣播

該實現使用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
InetSocketAddress hostAddress = new InetSocketAddress("255.255.255.255", 12344); 
// ... 
channel.connect(hostAddress); 

的問題是在這裏。您無法連接到廣播地址,並且無論如何都沒有意義。廣播地址不會發送給您,您正在發送給它。服務器從它自己的綁定地址發送給你。只要刪除這一行。您將不得不使用DatagramChannel.send()而不是write(),因爲您未連接。

1

接受的答案是不正確的。你可以在「連接」通道到廣播地址,當你第一次設置:

channel.socket().setBroadcast(true); 

當然UDP是一種無連接的協議,而是一個「連接」 DatagramChannel有一些好處,其中之一是使用的能力write(ByteBuffer[])方法。

+0

'setBroadcast(true)'不構成對廣播地址的「連接」。這看起來像我在我的答案中引用的代碼,這不起作用。 – EJP

+0

我的意思是你在設置'setBroadcast(true)'之後可以使用上面提到的代碼,然後你可以像連接的套接字一樣使用套接字。顯然它並沒有真正連接到廣播地址,但是你可以使用'write()'。 – br1