简易UDP服务器和客户端通信代码

2019年7月30日 / 42次阅读 / Last Modified 2019年9月7日

UDP是无连接的socket通信,UDP通信不需要在交换数据之前,进行像TCP那样的三次握手。UDP通信很直接,socket创建了之后,直接就发数据,也不管对方能不能收到。看起来好像UDP通信一无是处,但是别忘了,DNS服务就是用UDP的53号端口。

这种简单的,无连接的,没有接收确认的UDP通信,也有它适合的应用场景。比如发送语音和视频这类重传无意义的流媒体数据。又比如调试单板,电脑直连一块板子,这个时候就没有必要使用TCP,TFTP就是属于这类。

本文介绍一组UDP服务器和客户端的代码,实际测试通过,并说明服务器的监听地址这个重要细节。

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成字符串后,打印出来,同时打印发送侧的地址。

UDP客户端代码

用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服务器的绑定地址

需要说明一下这个重要细节,以上代码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服务器和客户端通信代码》有2条留言

    电子邮件地址不会被公开。 必填项已用*标注

    • 麦新杰

      UDP bind后,即可以收,也可以发。 [回复]

    • 麦新杰

      UDP监听地址部分的细节,也适用于TCP。 [回复]


    前一篇:
    后一篇:

    More

    麦新杰的Python笔记

    Ctrl+D 收藏本页


    ©Copyright 麦新杰 Since 2019 Python笔记

    go to top