3、Python Socket编程入门:socket模块介绍、创建UDP Socket、绑定端口、发送与接收数据
好,咱们正式开始动手写代码了。
前面两章聊了UDP的原理和广播地址,说白了都是理论。这一章,咱们直接进Python,把UDP Socket跑起来。我个人习惯是,学网络编程一定要先动手,哪怕只是打印个"Hello World",也比看十遍文档强。
3.1 socket模块:Python的网络基石
Python里做网络通信,绕不开的就是socket模块。它是操作系统网络接口的封装,你想想看,操作系统提供了底层的socket API,Python用这个模块帮你包装好,你直接调就行了。
导入方式很简单:
import socket
就这一行,你就拥有了创建网络连接的能力。我在项目中遇到过不少新手,一上来就装各种第三方库,其实标准库的socket模块已经能搞定90%的需求了。
3.2 创建UDP Socket:选对类型很重要
创建Socket的时候,需要指定两个参数:地址族和套接字类型。
- 地址族:
socket.AF_INET表示IPv4,目前99%的场景都用这个。 - 套接字类型:
socket.SOCK_DGRAM表示数据报套接字,也就是UDP。
代码长这样:
import socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
嗯,这里要注意,如果你不小心用了SOCK_STREAM,那就变成TCP了,后面收发数据的方式完全不一样。我曾经帮一个同事排查问题,他折腾了半天收不到数据,结果发现创建的是TCP Socket——方向错了,努力白费。
3.3 绑定端口:给Socket一个固定的门牌号
Socket创建好之后,它就像一个没有地址的信箱。你要用它收信,得先给它绑定一个端口。绑定端口用bind()方法:
udp_socket.bind(('0.0.0.0', 8888))
这里有两个参数:
- IP地址:
'0.0.0.0'表示监听所有网络接口。如果你只想监听本机,可以用'127.0.0.1'。 - 端口号:
8888是随便选的,只要不跟系统服务冲突就行。1024以下的端口需要管理员权限,建议用1024以上的。
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1),告诉系统端口可以重用。
3.4 发送数据:把消息扔到网络上
UDP发送数据用sendto()方法。它需要两个参数:数据本身和目标地址。
message = 'Hello, UDP!'
udp_socket.sendto(message.encode(), ('192.168.1.100', 8888))
注意几点:
- 数据必须是字节流,字符串需要先
encode()。 - 目标地址是一个元组,包含IP和端口。
- UDP不保证送达,发了就发了,没有回执。
说白了,sendto就像往邮筒里扔了一封信,你也不知道对方收没收到。我刚开始学的时候总觉得不靠谱,后来做项目多了才发现,很多场景下这种"尽力而为"的方式反而效率更高。
3.5 接收数据:等着消息上门
接收数据用recvfrom()方法。它需要指定一次最多接收多少字节:
data, addr = udp_socket.recvfrom(1024)
print(f'收到来自 {addr} 的消息:{data.decode()}')
recvfrom返回两个值:
- data:接收到的字节数据。
- addr:发送方的IP和端口。
这里有个坑:recvfrom是阻塞的。也就是说,如果没有数据到来,程序会停在这里一直等。为什么会这样?因为操作系统不知道你什么时候会收到数据,干脆让你等着。
udp_socket.settimeout(5.0)。这样5秒内没收到数据就会抛出socket.timeout异常,你可以自己处理。
3.6 完整示例:一个简单的UDP收发程序
把上面的知识点串起来,写一个完整的例子。这个程序既发又收,适合用来测试:
import socket
# 创建UDP Socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定端口
udp_socket.bind(('0.0.0.0', 8888))
# 发送数据
message = '你好,UDP!'
udp_socket.sendto(message.encode(), ('127.0.0.1', 8888))
print(f'已发送:{message}')
# 接收数据
data, addr = udp_socket.recvfrom(1024)
print(f'收到来自 {addr} 的消息:{data.decode()}')
# 关闭Socket
udp_socket.close()
这个例子是在本机自己给自己发消息。你运行后,会先打印"已发送",然后打印"收到来自 ('127.0.0.1', 8888) 的消息:你好,UDP!"。嗯,自己跟自己玩,虽然简单,但能验证整个流程是通的。
3.7 本章知识体系
下面这张图帮你理清UDP Socket编程的核心流程:
核心要点回顾:
- 创建UDP Socket用
socket.SOCK_DGRAM - 绑定端口用
bind(),接收方必须绑定 - 发送用
sendto(),接收用recvfrom() - 数据需要编码为字节流
recvfrom是阻塞的,可以设置超时
好了,这一章的内容就到这里。你可以在自己的电脑上把代码敲一遍,感受一下UDP通信的整个过程。下一章我们会聊UDP广播的具体实现,到时候这些基础代码会直接派上用场。