java socket UDP 网络发现

Network discovery using UDP Broadcast (Java)

The Problem

I have a Java server and a Java client running on the same network and the applications are not to be used outside a private network (not over internet).

So I used a static IP for the server, but what if I deploy my application? What if the network changes? That means I’ll lose my connection to the server and I’ll have to change the IP on the client side again.

Now that would be stupid. I want the client to “discover” the server on the network and connect with it.


The Solution

Using UDP packets and broadcasting them! This technique however is not optimal, but as long as we stay in one network this shouldn’t be a problem.
UDP packets however are fairly easy to work with. So let’s get started.


Still here? Let’s do this!

Server implementation

First, Let’s create the Java Singleton class that will execute the code on the server-side. This will be multi-threaded of course,  so we’ll also implement “Runnable”.

When we implement Runnable, we also have to override the Run method.

1 public class DiscoveryThread implements Runnable {
2  
3   @Override
4   public void run() {
5   }
6  
7   public static DiscoveryThread getInstance() {
8     return DiscoveryThreadHolder.INSTANCE;
9   }
10  
11   private static class DiscoveryThreadHolder {
12  
13     private static final DiscoveryThread INSTANCE = new DiscoveryThread();
14   }
15  
16 }

Ok, let’s think about this. What do we have to do?

  1. Open a socket on the server that listens to the UDP requests. (I’ve chosen 8888)
  2. Make a loop that handles the UDP requests and responses
  3. Inside the loop, check the received UPD packet to see if it’s valid
  4. Still inside the loop, send a response to the IP and Port of the received packet

That’s it on the server side.

Now, we’ll translate this into code.

1 DatagramSocket socket;
2  
3   @Override
4   public void run() {
5     try {
6       //Keep a socket open to listen to all the UDP trafic that is destined for this port
7       socket = new DatagramSocket(8888, InetAddress.getByName("0.0.0.0"));
8       socket.setBroadcast(true);
9  
10       while (true) {
11         System.out.println(getClass().getName() + ">>>Ready to receive broadcast packets!");
12  
13         //Receive a packet
14         byte[] recvBuf = new byte[15000];
15         DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);
16         socket.receive(packet);
17  
18         //Packet received
19         System.out.println(getClass().getName() + ">>>Discovery packet received from: " + packet.getAddress().getHostAddress());
20         System.out.println(getClass().getName() + ">>>Packet received; data: " new String(packet.getData()));
21  
22         //See if the packet holds the right command (message)
23         String message = new String(packet.getData()).trim();
24         if (message.equals("DISCOVER_FUIFSERVER_REQUEST")) {
25           byte[] sendData = "DISCOVER_FUIFSERVER_RESPONSE".getBytes();
26  
27           //Send a response
28           DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, packet.getAddress(), packet.getPort());
29           socket.send(sendPacket);
30  
31           System.out.println(getClass().getName() + ">>>Sent packet to: " + sendPacket.getAddress().getHostAddress());
32         }
33       }
34     catch (IOException ex) {
35       Logger.getLogger(DiscoveryThread.class.getName()).log(Level.SEVERE, null, ex);
36     }
37   }

A few notes; If you want to use strings as commands (like I do in this example), you have to trim the string before comparing it.

There, that’s it for the server.

Client implementation

Now we have to write the code for the client. Again, let me sketch how we are going to work.

  1. Open a socket on a random port.
  2. Try to broadcast to the default broadcast address (255.255.255.255)
  3. Loop over all the computer’s network interfaces and get their broadcast addresses
  4. Send the UDP packet inside the loop to the interface’s broadcast address
  5. Wait for a reply
  6. When we have a reply, check to see if the package is valid
  7. When it’s valid, get the package’s sender IP address; this is the server’s IP address
  8. CLOSE the socket! We don’t want to leave open random ports on someone else’s computer

On a side note, we don’t close the socket on the server because the server will receive and send UPD packets until the server is closed. Closing the socket on the server means that we won’t be able to discover it any more.

Wow, that was quite a lot. Now let’s put this into code!

1 // Find the server using UDP broadcast
2 try {
3   //Open a random port to send the package
4   c = new DatagramSocket();
5   c.setBroadcast(true);
6  
7   byte[] sendData = "DISCOVER_FUIFSERVER_REQUEST".getBytes();
8  
9   //Try the 255.255.255.255 first
10   try {
11     DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, InetAddress.getByName("255.255.255.255"), 8888);
12     c.send(sendPacket);
13     System.out.println(getClass().getName() + ">>> Request packet sent to: 255.255.255.255 (DEFAULT)");
14   catch (Exception e) {
15   }
16  
17   // Broadcast the message over all the network interfaces
18   Enumeration interfaces = NetworkInterface.getNetworkInterfaces();
19   while (interfaces.hasMoreElements()) {
20     NetworkInterface networkInterface = interfaces.nextElement();
21  
22     if (networkInterface.isLoopback() || !networkInterface.isUp()) {
23       continue// Don't want to broadcast to the loopback interface
24     }
25  
26     for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) {
27       InetAddress broadcast = interfaceAddress.getBroadcast();
28       if (broadcast == null) {
29         continue;
30       }
31  
32       // Send the broadcast package!
33       try {
34         DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, broadcast, 8888);
35         c.send(sendPacket);
36       catch (Exception e) {
37       }
38  
39       System.out.println(getClass().getName() + ">>> Request packet sent to: " + broadcast.getHostAddress() + "; Interface: " + networkInterface.getDisplayName());
40     }
41   }
42  
43   System.out.println(getClass().getName() + ">>> Done looping over all network interfaces. Now waiting for a reply!");
44  
45   //Wait for a response
46   byte[] recvBuf = new byte[15000];
47   DatagramPacket receivePacket = new DatagramPacket(recvBuf, recvBuf.length);
48   c.receive(receivePacket);
49  
50   //We have a response
51   System.out.println(getClass().getName() + ">>> Broadcast response from server: " + receivePacket.getAddress().getHostAddress());
52  
53   //Check if the message is correct
54   String message = new String(receivePacket.getData()).trim();
55   if (message.equals("DISCOVER_FUIFSERVER_RESPONSE")) {
56     //DO SOMETHING WITH THE SERVER'S IP (for example, store it in your controller)
57     Controller_Base.setServerIp(receivePacket.getAddress());
58   }
59  
60   //Close the port!
61   c.close();
62 catch (IOException ex) {
63   Logger.getLogger(LoginWindow.class.getName()).log(Level.SEVERE, null, ex);
64 }

There, that’s it!

I’ve given you pretty much all of the code, so it shouldn’t be easy to implement.

Don’t forget to run your DiscoveryThread!

1 Thread discoveryThread = new Thread(DiscoveryThread.getInstance());
2     discoveryThread.start();

~Michiel De Mey 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值