2019年7月30日 / 328次阅读 / Last Modified 2020年6月6日
socket
UDP是无连接的socket通信,UDP通信不需要在交换数据之前,进行像TCP那样的三次握手。UDP通信很直接,socket创建了之后,直接就发数据,也不管对方能不能收到。看起来好像UDP通信一无是处,但是别忘了,DNS服务就是用UDP的53号端口。
这种简单的,无连接的,没有接收确认的UDP通信,也有它适合的应用场景。比如发送语音和视频这类重传无意义的流媒体数据。又比如调试单板,电脑直连一块板子,这个时候就没有必要使用TCP,TFTP就是属于这类。
本文介绍一组UDP服务器和客户端的代码,实际测试通过,并说明服务器的监听地址这个重要细节。
用Python来写UDP服务器代码,非常简单,如下:
import socket
address = ('192.168.1.100', 12345) # 0.0.0.0 may be better
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(address)
while True:
data, addr = s.recvfrom(2048)
print("received:", data.decode(), "from", addr)
# use s.sendto() and addr to send data back
s.close()
我们只需要引入socket模块,创建UDP socket对象s,然后bind绑定地址address。address是一个IP和Port的tuple,IP是一个本地地址。s.recvfrom(2048),2048是一个接收buffer的长度,这个函数是阻塞的,会等待,直到有数据来到监听的Port。以上UDP服务器的代码很简单,在死循环中接收数据,decode成字符串后,打印出来,同时打印发送侧的地址。
用Python来写UDP客户端代码,也很轻松惬意,如下:
import socket
import time
address = ('192.168.1.100', 12345)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
msg = 'I am running in somewhere' # Ubuntu or Windows
s.sendto(msg.encode(), address)
# user s.recvfrom() to get data from server
time.sleep(2)
s.close()
与UDP服务器端代码不同之处,没有了绑定操作。创建UDP对象s后,直接向address发送数据。以上UDP客户端代码,为了测试方便的需要,采用循环发相同的数据,每次暂停2秒。注意发送的数据是byte stream。
UDP服务器代码必须要运行在具有192.168.1.100这个地址的主机内。而UDP客户端代码运行在哪里有点无所谓,只能网络能够通服务器地址即可。
上面的代码,服务器只管收,客户端只管发,因此谁先运行起来也就没关系了,先服务器后客户端,还是先客户端后服务器,都可以。
第一次运行环境:服务器在Ubuntu虚拟机内(IP为192.168.1.100),客户端一个在Ubunut本机,一个在Windows环境。运行起来后,服务器的打印是:
xinlin@ubuntu:~/test$ python3 udpserver.py
received: I am running in Windows from ('192.168.1.199', 58045)
received: I am running in Ubuntu from ('192.168.1.100', 44794)
received: I am running in Windows from ('192.168.1.199', 58045)
received: I am running in Ubuntu from ('192.168.1.100', 44794)
received: I am running in Windows from ('192.168.1.199', 58045)
received: I am running in Ubuntu from ('192.168.1.100', 44794)
received: I am running in Windows from ('192.168.1.199', 58045)
received: I am running in Ubuntu from ('192.168.1.100', 44794)
received: I am running in Windows from ('192.168.1.199', 58045)
received: I am running in Ubuntu from ('192.168.1.100', 44794)
received: I am running in Windows from ('192.168.1.199', 58045)
received: I am running in Ubuntu from ('192.168.1.100', 44794)
一切正常,交替出现接收到两个不同地址的客户端发来数据的信息。显示出来的IP地址不一定是客户端主机的地址,比如192.168.1.199,就是Windows客户端主机的网关地址,网关做了NAT操作,因此端口号也不一定是主机的端口号。
需要说明一下这个重要细节,以上代码UDP服务器绑定的地址是192.168.1.100,这个IP是服务器网卡的地址,绑定这个地址,也意味着只能接收发到这个地址的UDP数据包。
各位可以自己做个测试,如果将服务器的绑定地址修改为127.0.0.1,就算是本机的客户端,发送目的地址为192.168.1.100,服务器也无法收到数据!
这是因为,虽然127.0.0.1跟192.168.1.100是同一台主机,但是UDP服务器只监听127.0.0.1这个地址,没有监听192.168.1.100这个地址,所以发送到192.168.1.100这个地址的UDP数据包会被丢弃。
对于上面说的收不到数据包的情况,只要服务器和客户端都是用127.0.0.1这个地址,就可以了。不过,问题又来了,Windows主机里面的客户端不能使用127.0.0.1这个地址,它只能使用192.168.1.100这个地址。如果服务器监听在127.0.0.1这个地址上,Windows主机的客户端发送的数据,就会被丢弃。
解决这个问题的办法是:让UDP服务器在0.0.0.0这个地址上监听!
绑定0.0.0.0这个地址,表示在主机的所有IP地址上进行监听,包括网卡的192.168.1.100,和内部还回地址127.0.0.1等。这样就解决问题了,本机的客户端向127.0.0.1发送数据,Windows客户端向192.168.1.100发送数据,服务器都能收到。测试如下:
received: I am running in Windows from ('192.168.1.199', 58045)
received: I am running in Ubuntu from ('127.0.0.1', 58433)
received: I am running in Windows from ('192.168.1.199', 58045)
received: I am running in Ubuntu from ('127.0.0.1', 58433)
received: I am running in Windows from ('192.168.1.199', 58045)
received: I am running in Ubuntu from ('127.0.0.1', 58433)
received: I am running in Windows from ('192.168.1.199', 58045)
received: I am running in Ubuntu from ('127.0.0.1', 58433)
received: I am running in Windows from ('192.168.1.199', 58045)
received: I am running in Ubuntu from ('127.0.0.1', 58433)
received: I am running in Windows from ('192.168.1.199', 58045)
received: I am running in Ubuntu from ('127.0.0.1', 58433)
received: I am running in Windows from ('192.168.1.199', 58045)
received: I am running in Ubuntu from ('127.0.0.1', 58433)
UDP服务器打印出来的发送侧地址,看到了127.0.0.1,这就是本机的客户端的发送地址。
以上就是对UDP服务器和客户单通信的介绍。特别注意UDP服务器的绑定地址,它决定了能够从哪个IP地址接收到数据。
-- EOF --
本文链接:https://www.pynote.net/archives/702
《简易UDP服务器和客户端通信代码》有5条留言
前一篇:将图片嵌入Email的两种方法
后一篇:如何判断硬链接(hard link)文件?
Ctrl+D 收藏本页
©Copyright 麦新杰 Since 2019 Python笔记
udp sendto 最大发出数据的长度是60000+,具体是多少就没测试了。recvfrom 接口设定的缓冲区一定要足够大,不然可能就会出现数据丢失。 [ ]
报文分片,是底层IP干的事情。 [ ]
知道端口遍历局域网查找服务器,就是一个for循环。用python发udp广播,我试了2次都没成功。 [ ]
UDP bind后,即可以收,也可以发。 [ ]
UDP监听地址部分的细节,也适用于TCP。 [ ]