Skip to main content

Python 网络编程

Python UDP 编程

本章节主要讲解 UDP 协议简介,UDP 与 TCP 的主要区别,UDP 通信流程,服务端流程,客户端流程,Python UDP Socket 编程,UDP 时间服务器示例,UDP 编程关键知识点,数据报特性,无连接特性等等。

UDP 协议简介

本教程假定你已经学习过 TCP/IP UDP 相关的计算机网络相关知识,这里对这些基础知识只做简单提及。

UDP(用户数据报协议)是一种无连接的传输协议,它提供了简单快速的数据传输机制。与TCP不同,UDP不保证数据的可靠传输,但具有更低的延迟和更少的开销。

UDP 与 TCP 的主要区别

特性 UDP TCP
连接方式 无连接 面向连接
可靠性 不可靠 可靠
速度 快速 相对较慢
数据完整性 不保证 保证
应用场景 实时应用、广播 文件传输、网页浏览

UDP 通信流程

服务端流程

  1. 创建 UDP Socket:使用 SOCK_DGRAM 类型
  2. 绑定地址和端口:指定监听的地址和端口
  3. 接收数据:等待客户端发送数据包
  4. 处理数据:解析接收到的数据
  5. 发送响应:向客户端发送回应(可选)
  6. 继续监听:重复接收数据的过程

客户端流程

  1. 创建 UDP Socket:创建socket对象
  2. 发送数据:直接向目标地址发送数据包
  3. 接收响应:等待服务器的回应(如果需要)
  4. 关闭连接:释放socket资源

Python UDP Socket 编程

Python 使用 socket 模块进行 UDP 编程,关键是使用 socket.SOCK_DGRAM 类型创建数据报套接字。

核心方法

  • sendto(data, address):发送数据到指定地址
  • recvfrom(buffer_size):接收数据,返回数据和发送方地址
  • bind(address):绑定本地地址和端口

UDP 时间服务器示例

服务端代码

import socket
import time
from datetime import datetime

def start_udp_server():
    """启动 UDP 时间服务器"""
    # 创建 UDP socket
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    # 绑定地址和端口
    host = 'localhost'
    port = 9999
    server_address = (host, port)
    server_socket.bind(server_address)
    
    print(f"UDP 服务器启动,监听 {host}:{port}")
    print("等待客户端请求...")
    
    try:
        while True:
            # 接收客户端数据和地址信息
            data, client_address = server_socket.recvfrom(1024)
            
            # 解码接收到的消息
            message = data.decode('utf-8')
            print(f"收到来自 {client_address} 的请求: {message}")
            
            # 根据请求类型提供不同服务
            if message.lower() == 'time':
                # 获取当前时间
                current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                response = f"当前时间: {current_time}"
            elif message.lower() == 'timestamp':
                # 获取时间戳
                timestamp = int(time.time())
                response = f"时间戳: {timestamp}"
            elif message.lower() == 'date':
                # 获取当前日期
                current_date = datetime.now().strftime("%Y-%m-%d")
                response = f"当前日期: {current_date}"
            else:
                response = "未知命令,支持的命令: time, timestamp, date"
            
            # 发送响应给客户端
            server_socket.sendto(response.encode('utf-8'), client_address)
            print(f"已向 {client_address} 发送响应: {response}")
            
    except KeyboardInterrupt:
        print("\n服务器正在关闭...")
    except Exception as e:
        print(f"服务器错误: {e}")
    finally:
        server_socket.close()
        print("UDP 服务器已关闭")

if __name__ == "__main__":
    start_udp_server()

客户端代码

import socket
import time

def start_udp_client():
    """启动 UDP 客户端"""
    # 创建 UDP socket
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    # 设置超时时间(5秒)
    client_socket.settimeout(5.0)
    
    # 服务器地址
    server_host = 'localhost'
    server_port = 9999
    server_address = (server_host, server_port)
    
    print(f"UDP 客户端启动,目标服务器: {server_host}:{server_port}")
    print("支持的命令: time, timestamp, date")
    print("输入 'quit' 退出程序")
    
    try:
        while True:
            # 获取用户输入
            command = input("\n请输入命令: ").strip()
            
            if command.lower() == 'quit':
                break
            
            if not command:
                print("命令不能为空")
                continue
            
            try:
                # 发送命令到服务器
                client_socket.sendto(command.encode('utf-8'), server_address)
                print(f"已发送命令: {command}")
                
                # 接收服务器响应
                response_data, server_addr = client_socket.recvfrom(1024)
                response = response_data.decode('utf-8')
                print(f"服务器响应: {response}")
                
            except socket.timeout:
                print("请求超时,服务器可能未响应")
            except Exception as e:
                print(f"通信错误: {e}")
                
    except KeyboardInterrupt:
        print("\n客户端正在退出...")
    finally:
        client_socket.close()
        print("UDP 客户端已关闭")

if __name__ == "__main__":
    start_udp_client()

UDP 编程关键知识点

1. 数据报特性

# UDP 每次发送都是独立的数据包
data, address = socket.recvfrom(1024)  # 同时获取数据和发送方地址
socket.sendto(data, address)           # 发送数据到指定地址

2. 无连接特性

UDP 不需要建立连接,客户端可以直接向任何地址发送数据,服务端也可以向任何地址发送响应。

3. 超时设置

socket.settimeout(5.0)  # 设置5秒超时

UDP 通信建议设置超时,防止程序无限等待。

4. 缓冲区大小

recvfrom(1024) 中的1024是缓冲区大小,应根据实际数据大小调整。

UDP 应用场景

  • 实时游戏:需要低延迟的数据传输
  • 视频流媒体:可以容忍少量数据丢失
  • DNS 查询:简单的请求-响应模式
  • 网络广播:向多个接收者发送相同数据
  • 物联网设备:资源受限的设备通信

注意事项

  • 数据丢失:UDP不保证数据到达,重要数据需要应用层确认
  • 顺序问题:数据包可能乱序到达
  • 大小限制:UDP数据包有大小限制(理论上64KB,实际建议小于1500字节)
  • 防火墙:确保UDP端口没有被防火墙阻止
  • 错误处理:由于UDP的不可靠性,需要更完善的错误处理机制

UDP编程相比TCP更简单直接,但需要开发者自行处理可靠性问题。选择UDP还是TCP取决于应用的具体需求和对数据可靠性的要求。