• 11.3 创建UDP服务器
    • 问题
    • 解决方案
    • 讨论

    11.3 创建UDP服务器

    问题

    你想实现一个基于UDP协议的服务器来与客户端通信。

    解决方案

    跟TCP一样,UDP服务器也可以通过使用 socketserver 库很容易的被创建。例如,下面是一个简单的时间服务器:

    1. from socketserver import BaseRequestHandler, UDPServer
    2. import time
    3.  
    4. class TimeHandler(BaseRequestHandler):
    5. def handle(self):
    6. print('Got connection from', self.client_address)
    7. # Get message and client socket
    8. msg, sock = self.request
    9. resp = time.ctime()
    10. sock.sendto(resp.encode('ascii'), self.client_address)
    11.  
    12. if __name__ == '__main__':
    13. serv = UDPServer(('', 20000), TimeHandler)
    14. serv.serve_forever()

    跟之前一样,你先定义一个实现 handle() 特殊方法的类,为客户端连接服务。这个类的 request 属性是一个包含了数据报和底层socket对象的元组。client_address 包含了客户端地址。

    我们来测试下这个服务器,首先运行它,然后打开另外一个Python进程向服务器发送消息:

    1. >>> from socket import socket, AF_INET, SOCK_DGRAM
    2. >>> s = socket(AF_INET, SOCK_DGRAM)
    3. >>> s.sendto(b'', ('localhost', 20000))
    4. 0
    5. >>> s.recvfrom(8192)
    6. (b'Wed Aug 15 20:35:08 2012', ('127.0.0.1', 20000))
    7. >>>

    讨论

    一个典型的UDP服务器接收到达的数据报(消息)和客户端地址。如果服务器需要做应答,它要给客户端回发一个数据报。对于数据报的传送,你应该使用socket的 sendto()recvfrom() 方法。尽管传统的 send()recv() 也可以达到同样的效果,但是前面的两个方法对于UDP连接而言更普遍。

    由于没有底层的连接,UPD服务器相对于TCP服务器来讲实现起来更加简单。不过,UDP天生是不可靠的(因为通信没有建立连接,消息可能丢失)。因此需要由你自己来决定该怎样处理丢失消息的情况。这个已经不在本书讨论范围内了,不过通常来说,如果可靠性对于你程序很重要,你需要借助于序列号、重试、超时以及一些其他方法来保证。UDP通常被用在那些对于可靠传输要求不是很高的场合。例如,在实时应用如多媒体流以及游戏领域,无需返回恢复丢失的数据包(程序只需简单的忽略它并继续向前运行)。

    UDPServer 类是单线程的,也就是说一次只能为一个客户端连接服务。实际使用中,这个无论是对于UDP还是TCP都不是什么大问题。如果你想要并发操作,可以实例化一个 ForkingUDPServerThreadingUDPServer 对象:

    1. from socketserver import ThreadingUDPServer
    2.  
    3. if __name__ == '__main__':
    4. serv = ThreadingUDPServer(('',20000), TimeHandler)
    5. serv.serve_forever()

    直接使用 socket 来实现一个UDP服务器也不难,下面是一个例子:

    1. from socket import socket, AF_INET, SOCK_DGRAM
    2. import time
    3.  
    4. def time_server(address):
    5. sock = socket(AF_INET, SOCK_DGRAM)
    6. sock.bind(address)
    7. while True:
    8. msg, addr = sock.recvfrom(8192)
    9. print('Got message from', addr)
    10. resp = time.ctime()
    11. sock.sendto(resp.encode('ascii'), addr)
    12.  
    13. if __name__ == '__main__':
    14. time_server(('', 20000))

    原文:

    http://python3-cookbook.readthedocs.io/zh_CN/latest/c11/p03_creating_udp_server.html