Android开发板:局域网UDP通信

广州定昌电子gzdcsmt.com
2026-06-18

1. 方案概述


在工业控制、物联网网关、安防监控等场景中,Android 开发板与局域网服务器(或 PC)之间的数据交互对实时性要求极高。相较于 TCP,UDP 协议具有无连接、无握手开销、首部开销小等天然优势,是局域网内高速数据传输、设备发现(OSD/ONVIF 探测)的绝佳选择。

本方案提供了一个完整的 UDP 客户端 Demo,配合标准 PC 端测试软件,帮助您快速评估我司开发板的网络吞吐与低延迟特性。


2. Android 客户端核心实现 (生产级 Demo)


Android 的网络操作必须在子线程中执行,且由于 Android 6.0+ 的权限管理,需要在 AndroidManifest.xml 中声明权限。


Step 1: 权限配置 (AndroidManifest.xml)


<!-- 允许访问网络状态和进行 network 连接 -->

<uses-permission android:name="android.permission.INTERNET" />

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<!-- 如果需要支持 UDP 组播/广播,可选添加 -->

<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />


Step 2: 核心通信类 UdpClientManager.java


这是一个完整的、开箱即用的 UDP 管理类,采用了线程池设计,避免频繁创建线程,且支持单例模式。

package com.example.udpdemo;


import android.util.Log;

import java.io.IOException;

import java.net.DatagramPacket;

import java.net.DatagramSocket;

import java.net.InetAddress;

import java.net.SocketException;

import java.net.UnknownHostException;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;


public class UdpClientManager {

  private static final String TAG = "UdpClientManager";

 

  private DatagramSocket mSocket;

  private ExecutorService mThreadPool;

  private boolean isListening = false;

  private OnUdpMessageListener mListener;


  public interface OnUdpMessageListener {

      void onMessageReceived(String message, String hostAddress, int port);

      void onError(Exception e);

  }


  public UdpClientManager(OnUdpMessageListener listener) {

      this.mListener = listener;

      this.mThreadPool = Executors.newFixedThreadPool(2);

      initSocket();

  }


  private void initSocket() {

      try {

          if (mSocket == null || mSocket.isClosed()) {

              mSocket = new DatagramSocket();

              Log.d(TAG, "UDP Socket 初始化成功, 本地端口: " + mSocket.getLocalPort());

          }

      } catch (SocketException e) {

          Log.e(TAG, "Socket 初始化失败", e);

          if (mListener != null) mListener.onError(e);

      }

  }


  public void sendMessage(final String msg, final String destIp, final int destPort) {

      mThreadPool.execute(new Runnable() {

          @Override

          public void run() {

              try {

                  initSocket();

                  byte[] bytes = msg.getBytes("UTF-8");

                  InetAddress address = InetAddress.getByName(destIp);

                  DatagramPacket packet = new DatagramPacket(bytes, bytes.length, address, destPort);

                  mSocket.send(packet);

                  Log.d(TAG, "已发送数据到 " + destIp + ":" + destPort + " -> " + msg);

              } catch (UnknownHostException e) {

                  Log.e(TAG, "未知主机 IP", e);

              } catch (IOException e) {

                  Log.e(TAG, "发送 IO 异常", e);

              }

          }

      });

  }


  public void startReceiveThread() {

      if (isListening) return;

      isListening = true;

     

      mThreadPool.execute(new Runnable() {

          @Override

          public void run() {

              byte[] buffer = new byte[1024 * 2];

              while (isListening) {

                  try {

                      initSocket();

                      DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

                      Log.d(TAG, "开始阻塞监听来自 PC 的 UDP 回应...");

                      mSocket.receive(packet);

                     

                      String recvStr = new String(packet.getData(), 0, packet.getLength(), "UTF-8");

                      String hostIP = packet.getAddress().getHostAddress();

                      int port = packet.getPort();

                     

                      Log.d(TAG, "收到数据来自 [" + hostIP + ":" + port + "]: " + recvStr);

                     

                      if (mListener != null) {

                          mListener.onMessageReceived(recvStr, hostIP, port);

                      }

                  } catch (IOException e) {

                      Log.e(TAG, "接收异常(可能 Socket 已关闭)", e);

                  }

              }

          }

      });

  }


  public void close() {

      isListening = false;

      if (mSocket != null && !mSocket.isClosed()) {

          mSocket.close();

          mSocket = null;

      }

      if (mThreadPool != null && !mThreadPool.isShutdown()) {

          mThreadPool.shutdownNow();

      }

      Log.d(TAG, "UDP 连接已关闭, 资源已释放");

  }

}


Step 3: 在 MainActivity.java 中调用


在界面上放置两个输入框(IP 和内容)、一个“发送”按钮、一个文本框(显示接收内容)。

package com.example.udpdemo;


import android.os.Bundle;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;

import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;


public class MainActivity extends AppCompatActivity implements UdpClientManager.OnUdpMessageListener {


  private EditText etTargetIp, etMessage;

  private TextView tvLog;

  private Button btnSend;

  private UdpClientManager udpManager;

  private final int TARGET_PORT = 8888;


  @Override

  protected void onCreate(Bundle savedInstanceState) {

      super.onCreate(savedInstanceState);

      setContentView(R.layout.activity_main);


      etTargetIp = findViewById(R.id.et_target_ip);

      etMessage = findViewById(R.id.et_message);

      tvLog = findViewById(R.id.tv_log);

      btnSend = findViewById(R.id.btn_send);


      udpManager = new UdpClientManager(this);

      udpManager.startReceiveThread();


      btnSend.setOnClickListener(new View.OnClickListener() {

          @Override

          public void onClick(View v) {

              String ip = etTargetIp.getText().toString().trim();

              String msg = etMessage.getText().toString();

              udpManager.sendMessage(msg, ip, TARGET_PORT);

          }

      });

  }


  @Override

  public void onMessageReceived(final String message, final String hostAddress, final int port) {

      runOnUiThread(new Runnable() {

          @Override

          public void run() {

              tvLog.append("\n收到 " + hostAddress + ":" + port + " -> " + message);

          }

      });

  }


  @Override

  public void onError(Exception e) {

      runOnUiThread(new Runnable() {

          @Override

          public void run() {

              tvLog.append("\n发生错误: " + e.getMessage());

          }

      });

  }


  @Override

  protected void onDestroy() {

      super.onDestroy();

      if (udpManager != null) {

          udpManager.close();

      }

  }

}


3. PC 服务端配置(如何配合测试)


为了让板子发送的 UDP 数据有去有回,PC 端需要运行一个 UDP 调试助手。无需客户自己写代码,推荐使用以下主流免安工具:

推荐软件:NetAssist (网络调试助手) 或 SSCOM


软件配置步骤:


1. 协议类型:选择 UDP。

2. 本地 IP 地址:选择您当前 PC 的局域网 IP(例如 192.168.1.100)。

3. 本地端口号:填入 8888(需与 Android 客户端代码中的目标端口一致)。

4. 点击 “连接” 或 “打开” 开始监听。


注意:测试前请确保 Android 开发板与 PC 连接在同一个路由器/交换机下(同网段),并检查 PC 的 Windows 防火墙,确保其允许该调试软件访问网络。


4. 联合调试闭环验证(5分钟测试指南)


1. 硬件准备:用网线将 Android 开发板 和 PC 接入同一交换机(或路由器),确保两端获取到同网段 IP。

2. PC端准备:打开 NetAssist,类型选 UDP,本地端口填 8888,点击“打开”。

3. 板子端运行:打开 Demo App,在目标 IP 输入框中填入 PC 的 IP,点击 “发送”。

4. 效果看点:板子发送后,PC 端 NetAssist 窗口立刻会收到板子传过来的字符串。在 PC 端 NetAssist 的“远程主机”栏填入板子的 IP 和随机生成的本地端口,点击发送,板子界面也将实时刷新显示接收到的内容。


5. 为什么选择我们的板子?


千兆级以太网原生支持:本板搭载原生网卡芯片,PHY 层物理层硬件优化,UDP 吞吐量接近理论物理极限,杜绝因硬件导致的丢包。

极低传输时延:精简的 Linux 内核/Android 系统网络栈定制,减少数据包从内核态到用户态的拷贝时间,非常适合工业高频通信。

全天候稳定性:支持 7×24 小时长时间大流量 UDP 压测不掉线,守护客户关键业务。



分享
下一篇:这是最后一篇
上一篇:这是第一篇