UDP(User Datagram Protocol,用户数据报传输协议)位于 TCP/IP 协议的传输层, 比 TCP 简单许多,号称无连接不可靠传输协议。
传送数据不需要建立连接,只须知道对方的 IP 地址和端口号, 就可以直接发送数据包,但是因为其直接建立在不可靠的 IP 协议之上,对方能不能收到就不知道了, 在网络质量不好的情况下,使用 UDP 协议时丢包现象十分严重。
尽管有这些缺点,它也有 TCP 没有的优点,它简单速度快,资源占用少,也可以子网广播, 对于不要求可靠到达的数据,就可以使用 UDP 协议,比如视频、音频的传输。
由于 UDP 的特点,它也非常适合那些只发送一次字节占较小的请求,然后就完成工作,不再继续进行发送或接受数据的操作。比如 DNS 协议就是基于 UDP 的。
socket 简单版
client 端
- 建立 UDP socket 对象
- 直接发送信息到指定地址
- 接收服务端回信
示例如下:
#!/usr/bin/env python
import socket
ADDR = ('127.0.0.1', 6666)
MAX = 65535
MESSAGE = 'hello world!'
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
print('Send to server:', MESSAGE)
s.sendto(MESSAGE.encode('utf-8'), ADDR)
data, addr = s.recvfrom(MAX)
print('The Server reply:', data.decode('utf-8'))
server 端
- 建立 UDP socket 对象
- 绑定端口、地址
- 循环接受、回复客户端信息
示例如下:
#!/usr/bin/env python
import socket
ADDR = ('127.0.0.1', 6666)
MAX = 65535
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(ADDR)
while True:
data, addr = s.recvfrom(MAX)
print('the client at', addr, 'say:', data.decode('utf-8'))
replay = 'Your data was {} bytes'.format(len(data))
s.sendto(replay.encode('utf-8'), addr)
socket 改进版
由于 UDP 是不可靠传输协议,当客户端向服务端发送数据后,有可能发生以下情况:
- 数据在传输过程中丢失,没有到达服务端
- 数据达到服务端,服务端回复信息在传输过程丢失
- 服务端进程没有运行,客户端发送消息不会被处理和回复
- 数据由于网络速度过慢不能及时收到回复
上面前三种情况,都会使简单版客户端就会发生阻塞(recvfrom 函数)进而导致程序不会终止, 我们需要一种机制来改变以上各种情况的发生。
简单版客户端 socket 运行时可以接收任何地方发送过来的数据, 因此客户端 socket 也需要一种过滤机制,把那些不是服务端发送过来的消息过滤掉, 只处理指定服务器发送过来的数据。
因此 client 端代码需要改进以避免以上情况发生。
client 端
- 建立 UDP socket 对象
- 将 socket 与指定地址 connect
- 直接发送信息
- 接收回信
注: socket connect 时只是将指定地址存在 socket 对象中,并没有发送接受任何数据, 以便于此后的 send 函数默认发送地址和 recv 的默认接收地址。
示例如下:
#!/usr/bin/env python
import socket
ADDR = ('127.0.0.1', 6666)
MAX = 65535
MESSAGE = 'hello world!'
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(ADDR)
delay = 0.1
data = None
while True:
print('Send to server:', MESSAGE)
s.send(MESSAGE.encode('utf-8'))
s.settimeout(delay)
try:
data = s.recv(MAX)
except socket.timeout:
delay *= 2
if delay > 2.0:
raise RuntimeError('I think the server is shutdown')
else:
break
if data:
print('The Server reply:', data.decode('utf-8'))
socket 广播
注意广播地址主机标识段 host id 为全1。
client 端
#!/usr/bin/env python
import socket
ADDR = ('192.168.0.255', 6666)
MAX = 65535
MESSAGE = 'Broadcast message'
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.sendto(MESSAGE.encode('utf-8'), ADDR)
server 端
#!/usr/bin/env python
import socket
ADDR = ('', 6666)
MAX = 65535
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(ADDR)
while True:
data, addr = s.recvfrom(MAX)
print('the client at', addr, 'say:', data.decode('utf-8'))