前言

平时我们熟知的微信、QQ这类聊天软件,底层网络方案各有不同。很多初学者第一想法:直接用UDP写聊天工具,代码简单、不用建立连接、速度快。

但是很多人写完Demo后就踩坑:消息偶尔收不到、重复收到消息、消息顺序错乱。
原因很简单:UDP是无连接、不可靠传输协议。内核不保证报文送达、不保证有序、丢包不会通知发送方。

本文先实现一套原生UDP点对点聊天Demo(服务端+客户端,可以互相收发文本消息),模拟简易聊天窗口。再结合代码讲解原生UDP聊天存在的致命缺陷,衔接上一篇《如何让UDP实现可靠传输》的知识点,带大家理解:商用聊天软件不会直接裸UDP通信。

环境:Python3.x,无需额外安装第三方库,使用内置socket模块。
演示效果:两个终端,互相发送文字,实现双向聊天。

一、基础知识点:UDP通信特点回顾

  1. 无连接:通信前不需要握手,发送数据直接指定IP和端口;
  2. 尽最大努力交付:没有确认、没有重传,丢包直接消失;
  3. 支持双向通信:同一个Socket既可以收消息,也可以发消息;
  4. 面向报文:不会拆分合并数据包;
  5. 优点:开销小、延迟低;缺点:不可靠。

⚠️ 重要:TCP分为服务端/客户端;UDP模式更灵活,下面示例我们采用对等模式(P2P),两端代码几乎一致,只要填写对方IP端口,互相收发消息。

二、Python UDP双向聊天完整代码

文件名:udp_chat.py

import socket
import threading

class UDPChat:
    def __init__(self, local_port: int, remote_ip: str, remote_port: int):
        # 创建UDP socket
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        # 绑定本机端口,固定端口方便对方连接
        self.sock.bind(("0.0.0.0", local_port))
        self.remote_ip = remote_ip
        self.remote_port = remote_port
        print(f"✅ UDP聊天启动成功!本机端口:{local_port}")
        print(f"🎯 目标地址:{self.remote_ip}:{self.remote_port}")
        print("💡 输入消息发送,exit退出程序\n")

    def receive_msg(self):
        """持续接收消息的循环,单独线程运行"""
        while True:
            try:
                data, addr = self.sock.recvfrom(1024)
                msg = data.decode("utf-8")
                print(f"\n【对方】{msg}")
                print("你:", end="")
            except Exception as e:
                print("接收消息异常:", e)
                break

    def send_msg(self):
        """读取控制台输入,发送消息"""
        while True:
            content = input("你:")
            if content.lower() == "exit":
                print("🔚 退出聊天程序")
                self.sock.close()
                break
            # 发送文本消息
            self.sock.sendto(content.encode("utf-8"), (self.remote_ip, self.remote_port))

    def start(self):
        # 开启子线程负责接收消息(阻塞recvfrom不能阻塞输入)
        recv_thread = threading.Thread(target=self.receive_msg, daemon=True)
        recv_thread.start()
        # 主线程负责发送消息
        self.send_msg()

if __name__ == "__main__":
    # ========== 配置区域,两端修改此处参数 ==========
    # A机器配置(机器A)
    # 本机端口 8888,对方IP为机器BIP,对方端口9999
    chat = UDPChat(local_port=8888, remote_ip="127.0.0.1", remote_port=9999)

    # B机器配置(机器B,启动时注释上面,启用下面这行)
    # chat = UDPChat(local_port=9999, remote_ip="127.0.0.1", remote_port=8888)

    chat.start()

三、运行测试步骤

本地测试(同一台电脑开启两个终端)

  1. 终端1(A端),使用配置:
chat = UDPChat(local_port=8888, remote_ip="127.0.0.1", remote_port=9999)
  1. 终端2(B端)切换配置:
chat = UDPChat(local_port=9999, remote_ip="127.0.0.1", remote_port=8888)
  1. 分别运行 python udp_chat.py,互相输入文字即可聊天。

局域网/公网注意事项

  1. 局域网通信填写对方内网IP;
  2. 公网通信需要端口映射、关闭防火墙;
  3. 云服务器务必在安全组开放对应UDP端口。

运行效果图文字示例:

✅ UDP聊天启动成功!本机端口:8888
🎯 目标地址:127.0.0.1:9999
💡 输入消息发送,exit退出程序

你:哈喽,测试UDP聊天
【对方】收到啦!
你:

四、Demo暴露出原生UDP做聊天软件的4个致命问题

1. 消息丢失(最严重)

当网络波动、路由器拥塞时,sendto执行成功仅仅代表数据交给系统内核,无法确认对方收到消息
就像微信发消息对方收不到,且你完全不知情。

TCP会等待ACK,超时自动重传;原生UDP没有任何机制。

2. 消息乱序

发送顺序:消息1 → 消息2 → 消息3
网络转发原因,接收顺序可能变成:消息3 → 消息1 → 消息2
聊天场景会出现对话语序错乱,严重影响体验。

3. 消息重复

网络延迟引发路由重复投递,同一条消息多次弹出。

4. 无法感知对方是否离线

UDP无连接,你持续发送消息,对方下线后本地不会得到任何通知。不知道对方在线状态。

五、思考:真正的聊天软件如何解决?

方案分三类:

  1. 基于TCP:微信早期部分业务、传统IM,依靠TCP天然可靠;缺点握手延迟,网络切换容易断连。
  2. 可靠UDP(应用层封装):在UDP之上增加序列号、ACK确认、超时重传(KCP、QUIC)。很多游戏、实时语音、部分新式IM采用该方案。
  3. 混合方案:信令消息TCP可靠传输,媒体语音视频使用优化后的UDP。

我们上一篇博客讲解的【可靠UDP实现方案】,正好可以改造这个聊天Demo:
增加序列号、ACK应答、超时重传,就能解决消息丢失、乱序、重复问题。

六、拓展优化思路(你可以自行改造代码)

  1. 给每条消息增加唯一序列号;
  2. 接收端回复ACK确认收到消息;
  3. 发送端未收到ACK则启动重传;
  4. 增加心跳包,检测对方在线状态;
  5. 增加乱序缓冲区,保证消息有序展示;
  6. 增加用户名、消息时间戳;
  7. 增加简单异常断线处理。

七、总结

这份代码实现了极简UDP点对点文本聊天,几分钟就能跑通,直观感受到UDP双向通信。
但是一定要分清:Demo能跑 ≠ 可以生产使用。裸UDP天生缺乏可靠性机制,直接拿来做聊天工具会出现消息丢失等各类问题。

如果追求低延迟同时需要消息可靠,不要直接裸写UDP,两种选择:

  • 方案一:直接使用QUIC / KCP成熟可靠UDP库;
  • 方案二:参考可靠UDP原理,在应用层自主实现确认、重传、有序机制。

配套延伸阅读:《如何让UDP实现可靠传输:原理、问题与自研方案实践》

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐