SoFunction
Updated on 2025-04-06

Android complete Socket solution

Overall steps and procedures

Let’s talk about the overall steps:

When sending UDP broadcasts, everyone knows that the characteristic of UDP broadcasts is that devices in the entire network segment can receive this message.
The receiver received a UDP broadcast and replied to the sender of UDP with its IP address and the agreed port number.
After the sender has obtained the other party's IP address and port number, he can initiate a TCP request and establish a TCP connection.
Keep a TCP heartbeat. If you find that the other party is gone, repeat 1 step timeout to re-establish the connection.

The overall steps are the same as above, and the following is expanded with code:

Build UDP module

public UDPSocket(Context context) {
   = context;
  int cpuNumbers = ().availableProcessors();
  // Initialize the thread pool according to the number of CPUs  mThreadPool = (cpuNumbers * Config.POOL_SIZE);
  // Record the time when the object is created  lastReceiveTime = ();
  messageReceiveList = new ArrayList<>();
  (TAG, "Create UDP Object");
//  createUser();
 }

First, perform some initialization operations, prepare thread pools, record the initial time of the object, etc.

public void startUDPSocket() {
  if (client != null) return;
  try {
   // Indicates that this Socket listens to data on the set port.   client = new DatagramSocket(CLIENT_PORT);
   (true);
   if (receivePacket == null) {
    // Create packets that accept data    receivePacket = new DatagramPacket(receiveByte, BUFFER_LENGTH);
   }
   startSocketThread();
  } catch (SocketException e) {
   ();
  }
 }

Immediately afterwards, a real UDP Socket end, DatagramSocket, note that the port number CLIENT_PORT passed here means that this DatagramSocket receives messages at this port number.

/**
   * Turn on the thread to send data
   */
 private void startSocketThread() {
  clientThread = new Thread(new Runnable() {
   @Override
   public void run() {
    receiveMessage();
   }
  });
  isThreadRunning = true;
  ();
  (TAG, "Open UDP data receiving thread");
  startHeartbeatTimer();
 }

We all know that the sending and receiving of data in the Socket is to be processed, and the sending and receiving are both blocked and should be placed in the child thread. Here a thread is opened to process the received UDP messages (the previous article of the UDP module was explained in detail, so I won't go into details here)

/**
   * Processing received messages
   */
 private void receiveMessage() {
  while (isThreadRunning) {
   try {
    if (client != null) {
     (receivePacket);
    }
    lastReceiveTime = ();
    (TAG, "receive packet success...");
   } catch (IOException e) {
    (TAG, "UDP packet reception failed! Thread stopped");
    stopUDPSocket();
    ();
    return;
   }
   if (receivePacket == null || () == 0) {
    (TAG, "UDP data cannot be received or the received UDP data is empty");
    continue;
   }
   String strReceive = new String((), (), ());
   (TAG, strReceive + " from " + ().getHostAddress() + ":" + ());
   //Analyze the received json information   notifyMessageReceive(strReceive);
   // Reset the length after receiving UDP data every time.  Otherwise, it may cause the next time the packet is received is truncated.   if (receivePacket != null) {
    (BUFFER_LENGTH);
   }
  }
 }

UDP data is received in the child thread, and the notifyMessageReceive method notifies the message out through the interface.

/**
   * Send heartbeat packet
   *
   * @param message
   */
 public void sendMessage(final String message) {
  (new Runnable() {
   @Override
   public void run() {
    try {
     BROADCAST_IP = ();
     (TAG, "BROADCAST_IP:" + BROADCAST_IP);
     InetAddress targetAddress = (BROADCAST_IP);
     DatagramPacket packet = new DatagramPacket((), (), targetAddress, CLIENT_PORT);
     (packet);
     // Data sending event     (TAG, "Data sent successfully");
    } catch (UnknownHostException e) {
     ();
    } catch (IOException e) {
     ();
    }
   }
  });
 }

Then startHeartbeatTimer starts a heartbeat thread and broadcasts a UDP message every five seconds. Note that getBroadcastAddress is the obtained network segment ip. When sending this UDP message, all devices in the entire network segment can receive it.

So far, the UDP on our sending end has been completed.

Build TCP module

Next, the TCP module is time to appear. The purpose of UDP sending heartbeat broadcast is to find the IP address and agreed port of the corresponding device, so in the UDP data reception method:

/**
   * Process messages received by udp
   *
   * @param message
   */
 private void handleUdpMessage(String message) {
  try {
   JSONObject jsonObject = new JSONObject(message);
   String ip = (Config.TCP_IP);
   String port = (Config.TCP_PORT);
   if (!(ip) && !(port)) {
    startTcpConnection(ip, port);
   }
  } catch (JSONException e) {
   ();
  }
 }

The purpose of this method is to get the other party's UDP Server end, send me the UDP message, tell me its IP address, and the port number we agreed on in advance.

How to get an IP for a device?

public String getLocalIPAddress() {
  WifiInfo wifiInfo = ();
  return intToIp(());
 }
 private static String intToIp(int i) {
  return (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ((i >> 16) & 0xFF) + "."
    + ((i >> 24) & 0xFF);
 }

Now that I have obtained the other party’s IP and the agreed port number, I can finally open a TCP client.

private boolean startTcpConnection(final String ip, final int port) {
  try {
   if (mSocket == null) {
    mSocket = new Socket(ip, port);
    (true);
    (true);
    (true);
   }
   InputStream is = ();
   br = new BufferedReader(new InputStreamReader(is));
   OutputStream os = ();
   pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os)), true);
   (TAG, "tcp created successfully...");
   return true;
  } catch (Exception e) {
   ();
  }
  return false;
 }

When the TCP client is successfully established, we can send and receive messages through the TCP Socket.

Detail processing

Next is some details, such as our UDP heartbeat. When TCP is successfully established, we need to stop UDP heartbeat:

if (startTcpConnection(ip, (port))) {// Try to establish a TCP connection     if (mListener != null) {
      ();
     }
     startReceiveTcpThread();
     startHeartbeatTimer();
    } else {
     if (mListener != null) {
      (.CREATE_TCP_ERROR);
     }
    }
   // TCP has successfully established a connection and stopped the heartbeat packet of UDP.   public void stopHeartbeatTimer() {
    if (timer != null) {
     ();
     timer = null;
    }
 }

Heartbeat protection for TCP connections:

/**
   * Start heartbeat
   */
 private void startHeartbeatTimer() {
  if (timer == null) {
   timer = new HeartbeatTimer();
  }
  (new () {
   @Override
   public void onSchedule() {
    (TAG, "timer is onSchedule...");
    long duration = () - lastReceiveTime;
    (TAG, "duration:" + duration);
    if (duration > TIME_OUT) {//If you don’t receive my heartbeat packet for more than fifteen seconds, you will think that the other party is not online.     (TAG, "tcp ping timeout, the other party has been offline");
     stopTcpConnection();
     if (mListener != null) {
      (.PING_TCP_TIMEOUT);
     }
    } else if (duration > HEARTBEAT_MESSAGE_DURATION) {//If he does not receive my heartbeat packet for more than two seconds, he will send another one.     JSONObject jsonObject = new JSONObject();
     try {
      (, );
     } catch (JSONException e) {
      ();
     }
     sendTcpMessage(());
    }
   }
  });
  (0, 1000 * 2);
 }

First, every two seconds, I will send a ping packet to the other party to see if the opponent is there. If I haven’t replied to me for more than 15 seconds, it means that the other party has been disconnected and close the TCP end on my side. Enter the onFailed method.

@Override
    public void onFailed(int errorCode) {// tcp exception handling     switch (errorCode) {
      case .CREATE_TCP_ERROR:
       break;
      case .PING_TCP_TIMEOUT:
       ();
       tcpSocket = null;
       break;
     }
    }

When the TCP connection timed out, I restarted UDP's broadcast heartbeat, looking for the device waiting to connect. Go to the next step cycle.

For details such as the format of data transmission, this is related to the business. Just make your own decision.

You can also open different thread channels according to your business model, whether it is CPU-intensive or IO-intensive. This involves the knowledge of threads.

Source code sharing:/itsMelo/AndroidSocket