在工业控制、AI视觉、智能安防、边缘计算、物联网网关等领域,通常需要Android开发板承担设备联网实时数据交互任务。在局域网环境下,数据传输最常使用的通信协议主要是UDP(User Datagram Protocol)和TCP(Transmission Control Protocol)。两种协议虽然都基于IP网络运行,但在通信机制、性能表现以及应用场景方面存在较大差异。下面本文简单聊聊局域网UDP通信与TCP通信各自原理,及对比分析。

UDP和TCP通信协议原理差异

UDP是一种无连接通信协议,数据发送前不需要建立连接,发送端可以直接向目标设备发送数据包。接收端收到数据后直接进行处理,中间无需进行确认应答。对于Android开发板而言,UDP通信通常通过DatagramSocket接口实现,开发简单、系统资源占用较低。其主要特点包括:无需建立连接、发送效率高、网络开销小、延迟低、支持广播和组播等,由于协议本身没有重传机制,因此UDP能够减少大量握手和确认过程带来的时间消耗。

1782868215388124.jpg

TCP属于面向连接协议,在数据传输前需要进行三次握手建立连接,数据发送过程中采用确认应答机制,确保数据准确到达目标设备。Android系统中一般使用Socket接口实现TCP通信。TCP主要具备:建立可靠连接、自动重传丢失数据、保证数据完整性、数据顺序等,这些机制提升了通信可靠性,但同时增加了网络开销和系统资源消耗。

UDP与TCP功能对比

对比项目

UDP通信

TCP通信

连接方式

无连接

面向连接

建立连接时间

三次握手

传输延迟

极低

较高

数据可靠性

不保证

保证

丢包重传

不支持

自动重传

数据顺序

不保证

保证

广播功能

支持

不支持

组播功能

支持

不支持

CPU占用率

较低

较高

网络利用率

相对较低

开发复杂度

中等

简单

适用数据类型

实时数据流

关键业务数据

实时传输场景UDP的优势

工业相机图像传输、PLC数据采集、AGV机器人调度、NVR视频流转发、设备状态同步等应用,对网络实时性和稳定性提出了较高要求。在这些应用场景下的局域网环境中,Android开发板采用UDP数据传输方案相较于TCP具有以下显著优势:

1. 极低传输延迟

TCP的三次握手机制在每次通信建立时都会引入至少一个RTT往返时延的额外开销。对于频繁、小数据量的交互场景,这部分开销占比尤为可观。UDP无需握手,数据可以直接封装发送,首部开销要远小于TCP,也就意味着更低的传输延迟。在工业高频通信中,这种差异可能直接决定系统能否满足毫秒级的响应要求。

2. 更高的吞吐量

TCP的拥塞控制和确认重传机制虽然保证了可靠性,但也限制了传输速率。在丢包发生时,TCP会触发拥塞避免算法,大幅降低发送窗口,导致吞吐量大幅降低。UDP则不受此限制,可以持续在线程发送数据,充分发挥网络的物理带宽。

3. 更低的资源消耗

TCP协议栈需要在内核维护连接状态表、发送窗口、接收窗口等复杂数据结构,每个连接都占用一定的内存和CPU资源。UDP则是无状态的,资源消耗极低,适合需要同时与多个设备通信的场景,如设备发现(OSD/ONVIF探测)

Android平台UDP通信核心实现DEMO

在Android开发板上实现UDP通信,需注意网络操作必须在子线程中执行,并在AndroidManifest.xml中声明INTERNET权限。以下是一个生产级的UDP客户端管理类核心设计示例代码,基于定昌电子的Android开发板硬件,以及PC服务器NetAssist软件:

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 连接已关闭, 资源已释放");

    }

}

该段Demo是核心通信类UdpClientManager.java实现采用了线程池和单例模式,支持同时收发,并提供了完整的回调接口,可直接集成到生产项目中。

联合调试时,PC端运行NetAssist等UDP调试工具,监听8888端口,即可与开发板完成双向通信闭环验证

定昌电子Android开发板UDP通信优势

除了通信协议选型之外,硬件平台本身的网络性能同样关键。上述核心实现板子是一块瑞芯微RK3588的安卓主板,定昌电子的(型号是DC_M588,是核心板+底板),查阅信息后,发现其在网络通信方面具备很多优势:

1782868447300943.jpg

①首先千兆以太网原生支持。看了官网,定昌大部分安卓板都板载千兆以太网网卡芯片,PHY层物理层硬件优化,UDP吞吐量很高接近理论物理极限。多数工业应用开发板板载CAN长距离工业总线接口。

②极低传输时延与全天候稳定性。定昌电子在Linux内核/Android系统网络栈层面进行了定制优化,减少数据包从内核态到用户态的拷贝时间。工业级型号支持-45℃~85℃宽温工作,可满足7×24小时长时间大流量UDP压测需求,不掉线、不卡顿。

③完整的开发支持。定昌官方还提供一些评估板PCB原理图、开发文档等开源资料,并提供完整的SDK、AI算法平台API及Demo示例,帮助开发者快速完成从评估到量产的全流程。

更多推荐