Python Socket编程基础:怎么实现TCP与UDP通信

沿途有光
沿途有光
发布时间:2026-11-12| 11人浏览过
原创

详细讲解Python中Socket网络编程的基础知识与具体实现方法,包含TCP与UDP客户端服务端的完整代码步骤、通信时序、关键机制与核心差异对比。

网络编程 Python内置模块 传输层通信 阅读时间:约 10 分钟

Socket(套接字)是计算机网络通信的底层基石,它为上层软件提供了访问操作系统网络协议栈的统一编程接口。在 Python 中,通过内置的标准库 socket,开发者无需安装任何第三方依赖,即可直接实现主机间的数据传输。本文将直接从底层通信流程展开,分别讲解如何在 Python 中完整实现基于面向连接的 TCP 通信 与基于无连接的 UDP 通信,剖析两者的具体操作步骤、可运行示例与核心差异,帮助你在实际项目中准确选用并编写稳定的网络交互程序。

Python Socket编程基础:怎么实现TCP与UDP通信
图 0:Python Socket 网络通信全景模型(TCP 与 UDP)

一、Socket 编程的核心概念与协议选择

在网络体系中,一台主机通过 IP 地址标识身份,而主机内部运行的各种网络程序则通过“端口号”(Port)进行区分。一个 Socket 本质上就是由IP 地址与端口号组成的逻辑端点。应用程序向 Socket 写入字节流或数据报,操作系统内核负责把数据打包并发送到网络线缆中;反向接收时,操作系统再依据目的端口把数据推送到对应的 Socket 缓冲区中供程序读取。

在 Python 中,我们使用 socket.socket(family, type) 函数来创建一个套接字对象。这个函数最常用的两个参数决定了通信的寻址协议与传输行为:

  • 地址族(family):最常用的是 socket.AF_INET,代表 IPv4 地址寻址(如 127.0.0.1);若使用 IPv6 地址,则指定为 socket.AF_INET6。
  • 套接字类型(type):
    • socket.SOCK_STREAM:对应 TCP 协议(流式套接字)。它提供顺序到达、无丢失、无重复且面向连接的可靠字节流服务。
    • socket.SOCK_DGRAM:对应 UDP 协议(数据报套接字)。它提供无连接、独立数据报文传输服务,不保证顺序也不保证送达,但首部极小、转发高效。
Python Socket编程基础:怎么实现TCP与UDP通信
图 1:Socket 抽象层在系统协议栈中的位置与协议分流

如图 1 所示,Socket API 位于应用层与内核传输层之间。无论是构建高可靠的 Web 数据交互,还是追求极低延迟的音视频传输,程序内部的调用起点都是这个套接字对象。下面我们分别进入 TCP 和 UDP 的具体实现步骤与完整代码。

二、实现基于 TCP 的面向连接通信

TCP(Transmission Control Protocol)是面向连接的传输协议。在两个通信实体传输数据之前,必须先通过底层“三次握手”建立一条端到端的虚电路;通信结束后,再通过“四次挥手”拆除连接。这意味着 TCP 通信严格区分服务端(Server)与客户端(Client)角色。

1. TCP 服务端实现步骤

  1. 创建套接字:指定 AF_INET 与 SOCK_STREAM。
  2. 设置地址复用(推荐):调用 setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)。这样当服务端重启时,即使原端口处于 TIME_WAIT 状态也能立即重新绑定,避免抛出“Address already in use”异常。
  3. 绑定地址与端口:调用 bind((host, port)),参数为一个元组。
  4. 开启监听:调用 listen(backlog),其中 backlog 指定系统允许暂存的未完成连接队列的最大长度。
  5. 接受连接:调用 accept()。该方法会一直阻塞,直到有客户端连入。成功连接后,它返回一个由两个元素组成的元组:(conn, client_address)。其中 conn 是专门代表该客户端连接的全新套接字,随后的数据收发全部通过它进行;原监听套接字继续负责等待其它客户端。
  6. 收发数据:调用 conn.recv(bufsize) 接收字节数据,调用 conn.sendall(bytes_data) 发送数据。
  7. 关闭连接:处理完毕后依次调用 conn.close() 和 server_socket.close() 释放系统文件描述符。
Python Socket编程基础:怎么实现TCP与UDP通信
图 2:TCP 客户端与服务端通信时序与 API 调用步骤

2. TCP 通信完整代码示例

我们编写一个完整的回显(Echo)服务端与客户端程序。客户端向服务端发送一段文本,服务端收到后转为大写并回传给客户端。

tcp_server.py(TCP 服务端完整代码)
import socket

def run_tcp_server(host='127.0.0.1', port=8888):
    # 1. 创建 TCP 套接字
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # 2. 允许端口快速重用
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    
    # 3. 绑定 IP 与 端口
    server_socket.bind((host, port))
    
    # 4. 开启监听,排队队列上限设为 5
    server_socket.listen(5)
    print(f"[服务端已启动] 正在监听 {host}:{port},等待客户端连接...")

    try:
        while True:
            # 5. 阻塞等待客户端连接
            conn, client_addr = server_socket.accept()
            print(f"[连接建立] 客户端来自: {client_addr}")
            
            with conn:
                while True:
                    # 6. 接收客户端数据(单次最多读取 1024 字节)
                    data = conn.recv(1024)
                    if not data:
                        # 客户端断开连接时,recv 会返回空字节 b''
                        print(f"[连接断开] 客户端 {client_addr} 已下线")
                        break
                    
                    msg = data.decode('utf-8')
                    print(f"[收到数据] 内容: {msg}")
                    
                    # 加工处理:转为大写回传
                    reply = f"ECHO: {msg.upper()}".encode('utf-8')
                    conn.sendall(reply)
    except KeyboardInterrupt:
        print("
[服务端停止] 收到终止信号,正在关闭服务...")
    finally:
        server_socket.close()

if __name__ == '__main__':
    run_tcp_server()
tcp_client.py(TCP 客户端完整代码)
import socket

def run_tcp_client(host='127.0.0.1', port=8888):
    # 1. 创建 TCP 套接字
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    try:
        # 2. 连接服务端(触发三次握手)
        print(f"[正在连接] 目标服务器: {host}:{port}...")
        client_socket.connect((host, port))
        print("[连接成功] 已与服务器建立 TCP 会话")
        
        # 3. 发送数据
        message = "Hello, Python Socket TCP!"
        print(f"[准备发送] {message}")
        client_socket.sendall(message.encode('utf-8'))
        
        # 4. 接收服务端响应
        response = client_socket.recv(1024)
        print(f"[收到应答] {response.decode('utf-8')}")
        
    except ConnectionRefusedError:
        print(f"[连接失败] 无法连入 {host}:{port},请确认服务端已启动")
    finally:
        # 5. 关闭套接字(触发四次挥手)
        client_socket.close()
        print("[连接关闭] 本地套接字已安全释放")

if __name__ == '__main__':
    run_tcp_client()

3. 运行验证与结果示例

在两个独立的终端窗口中分别运行服务端与客户端程序:

# 终端 1:先启动服务端
python3 tcp_server.py
终端 1 示例输出(服务端):
[服务端已启动] 正在监听 127.0.0.1:8888,等待客户端连接... [连接建立] 客户端来自: ('127.0.0.1', 54218) [收到数据] 内容: Hello, Python Socket TCP! [连接断开] 客户端 ('127.0.0.1', 54218) 已下线
# 终端 2:随后启动客户端
python3 tcp_client.py
终端 2 示例输出(客户端):
[正在连接] 目标服务器: 127.0.0.1:8888... [连接成功] 已与服务器建立 TCP 会话 [准备发送] Hello, Python Socket TCP! [收到应答] ECHO: HELLO, PYTHON SOCKET TCP! [连接关闭] 本地套接字已安全释放
⚠️ TCP 核心注意事项:字节流与“粘包”现象

TCP 是纯粹的字节流协议,在传输层眼中只有无界限的连续二进制字节,并没有“一条消息”的边界概念。如果发送端连续快速调用两次 sendall(),接收端单次 recv() 可能会把两次内容一次性读出;或者单次发送的长数据被拆分成多次 recv() 接收。

在实际生产环境中,若要在 TCP 上传输多条独立业务指令,必须在应用层自定义协议格式,常见做法有两种:固定长度消息头(声明后随数据体长度)或特定分隔符(如 )。

三、实现基于 UDP 的无连接数据报通信

UDP(User Datagram Protocol)是无连接的轻量级传输协议。它不进行三次握手,也不维持连接状态。发送端只需把数据封装进数据报,指定目的 IP 和端口,便直接抛入网络;接收端也只需监听本地端口,等待数据报到达。

1. UDP 服务端与客户端实现步骤

  • UDP 服务端:
    1. 创建套接字:指定 AF_INET 和 SOCK_DGRAM。
    2. 绑定端口:调用 bind((host, port))。由于 UDP 没有连接,不需要调用 listen() 也不需要 accept()。
    3. 接收数据:调用 data, client_addr = s.recvfrom(bufsize)。该方法同时返回数据内容与发信方的地址元组。
    4. 应答数据:调用 s.sendto(reply_bytes, client_addr),把应答直接发回目标地址。
  • UDP 客户端:
    1. 创建套接字:同样指定 SOCK_DGRAM。
    2. 无需调用 connect(),直接调用 s.sendto(data_bytes, (server_host, server_port)) 即可发包。操作系统会在此时自动为客户端分配一个临时本地端口。
    3. 接收应答:调用 data, addr = s.recvfrom(bufsize) 接收服务端的回复。
    4. 关闭套接字:调用 s.close()。
Python Socket编程基础:怎么实现TCP与UDP通信
图 3:UDP 数据报无连接收发交互模型

2. UDP 通信完整代码示例

udp_server.py(UDP 服务端完整代码)
import socket

def run_udp_server(host='127.0.0.1', port=9999):
    # 1. 创建 UDP 套接字 (SOCK_DGRAM)
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    # 2. 绑定端口
    server_socket.bind((host, port))
    print(f"[UDP 服务端就绪] 监听端口 {host}:{port},随时等待接收数据报...")

    try:
        while True:
            # 3. 阻塞接收数据报,同时获取发送方的 IP 和端口
            data, client_addr = server_socket.recvfrom(1024)
            msg = data.decode('utf-8')
            print(f"[收到数据报] 来自 {client_addr}: {msg}")
            
            # 4. 原路回传应答数据报
            reply = f"ACK: Received '{msg}'".encode('utf-8')
            server_socket.sendto(reply, client_addr)
    except KeyboardInterrupt:
        print("
[服务端停止] 正在关闭 UDP 套接字...")
    finally:
        server_socket.close()

if __name__ == '__main__':
    run_udp_server()
udp_client.py(UDP 客户端完整代码)
import socket

def run_udp_client(host='127.0.0.1', port=9999):
    # 1. 创建 UDP 套接字
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # 设置 3 秒超时,防止网络丢包时程序无限期卡住
    client_socket.settimeout(3.0)
    
    target_addr = (host, port)
    message = "Hello, Python Socket UDP!"
    
    try:
        # 2. 直接发送数据报到指定地址,无需 connect
        print(f"[发送数据报] 目标: {target_addr},内容: {message}")
        client_socket.sendto(message.encode('utf-8'), target_addr)
        
        # 3. 接收服务端响应
        data, server_addr = client_socket.recvfrom(1024)
        print(f"[收到应答] 来自 {server_addr}: {data.decode('utf-8')}")
        
    except socket.timeout:
        print(f"[请求超时] 未在 3 秒内收到来自 {target_addr} 的数据报响应(可能丢包)")
    finally:
        # 4. 释放套接字
        client_socket.close()
        print("[客户端退出] 套接字已关闭")

if __name__ == '__main__':
    run_udp_client()

3. 运行验证与结果示例

# 终端 1:启动 UDP 服务端
python3 udp_server.py
终端 1 示例输出(服务端):
[UDP 服务端就绪] 监听端口 127.0.0.1:9999,随时等待接收数据报... [收到数据报] 来自 ('127.0.0.1', 59124): Hello, Python Socket UDP!
# 终端 2:启动 UDP 客户端
python3 udp_client.py
终端 2 示例输出(客户端):
[发送数据报] 目标: ('127.0.0.1', 9999),内容: Hello, Python Socket UDP! [收到应答] 来自 ('127.0.0.1', 9999): ACK: Received 'Hello, Python Socket UDP!' [客户端退出] 套接字已关闭
💡 UDP 核心注意事项:报文边界与单包大小限制

与 TCP 的字节流不同,UDP 天生完整保留报文边界。发送端调用一次 sendto() 发送一个数据报,接收端的一次 recvfrom() 就严格对应读取这一个数据报,绝不会出现多包混杂粘连的情况。

但需要注意,底层以太网链路层的最大传输单元(MTU)通常为 1500 字节。去除 20 字节 IP 首部和 8 字节 UDP 首部后,建议单次发送的 UDP 净荷(Payload)控制在 1472 字节以内。若超过此限制,IP 层会强行分片传输,一旦其中一个分片在网络中丢失,整个 UDP 数据报都会被接收端内核直接丢弃。

四、TCP 与 UDP 的核心差异与技术选型

理解了两种协议的具体代码实现后,关键问题在于:在实际业务开发中,究竟应当如何选择?它们的核心差异不在于谁好谁坏,而在于它们解决的侧重目标不同。

Python Socket编程基础:怎么实现TCP与UDP通信
图 4:TCP 与 UDP 关键特性对比与技术选型矩阵

我们把影响实际开发设计的关键差异总结如下:

特性维度 TCP (SOCK_STREAM) UDP (SOCK_DGRAM) 差异带来的实际开发影响
连接模型 面向连接(需握手和维护连接状态) 无连接(随时发送,无状态维护) TCP 单机并发受限于连接句柄与内存;UDP 服务端可用单个套接字响应海量客户端。
可靠性机制 确认应答、超时重传、保序去重 尽力交付,不保证送达,无重传 TCP 开发者无需操心丢包;UDP 若需要可靠性必须在应用层自行设计 ACK 与重传。
数据传输形态 连续字节流(Byte Stream) 独立数据报(Datagram) TCP 必须自行处理粘包与分包协议;UDP 报文天然独立,天然具备明确的数据边界。
协议开销与延迟 首部 20~60 字节,有握手和拥塞控制 首部固定 8 字节,无握手,即刻发送 UDP 数据传输初始延迟几乎为零,传输开销小,网络利用率极高。
通信模式支持 仅支持一对一单播 支持单播、广播与组播 局域网设备自动发现、一对多通知等场景必须依赖 UDP。

选型依据简明总结:

  • 必须选 TCP 的场景:对数据的准确性、完整性有绝对要求。例如用户登录凭证、金融交易指令、数据库 SQL 查询、文件上传下载、网页 HTTP/RESTful 接口等。即使网络发生波动引发重传,也绝不能丢失任何一个字节。
  • 优先选 UDP 的场景:对实时性要求极高,并且能够容忍个别数据帧丢失的场景。例如实时音视频会议(丢失一帧画面比画面卡住数秒重传更能接受)、多人竞技游戏的角色实时坐标同步、物联网传感器的周期性遥测上报,以及 DNS 查询(单次问答、超时直接重发即可)。

五、总结与高并发进阶指引

本文详细梳理了 Python 底层 Socket 通信的完整实现机制:

  • TCP 依靠 socket() -> bind() -> listen() -> accept() 完成服务端搭建,客户端通过 connect() 建立可靠连接;传输时需要警惕字节流粘包。
  • UDP 依靠 socket(AF_INET, SOCK_DGRAM) 与 recvfrom() / sendto() 实现直接收发;无需握手且自带报文边界,但单包应注意 MTU 限制。

需要特别说明的是,本文展示的基础代码均属于同步阻塞模式(单线程一次只能处理一个客户端)。如果要在生产环境下支撑成千上万个并发连接,可以从以下两种演进路径继续深入:

  1. 多线程/多进程模型:在 TCP 服务端通过 threading.Thread 为每个接入的 conn 分配独立线程,实现多客户端并发处理。
  2. I/O 多路复用与异步编程:使用 Python 标准库的 selectors(底层封装 epoll / kqueue)或现代异步框架 asyncio,以单线程事件循环驱动非阻塞套接字,实现单机数十万级高并发网络通信。

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。