11.3 创建UDP�务器¶

问题¶

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

解决方案¶

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

from socketserver import BaseRequestHandler, UDPServer
import time

class TimeHandler(BaseRequestHandler):
    def handle(self):
        print('Got connection from', self.client_address)
        # Get message and client socket
        msg, sock = self.request
        resp = time.ctime()
        sock.sendto(resp.encode('ascii'), self.client_address)

if __name__ == '__main__':
    serv = UDPServer(('', 20000), TimeHandler)
    serv.serve_forever()

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

我们�测试下这个�务器,首先�行它,然�打开�外一个Python进程��务器��消�:

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

讨论¶

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

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

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

from socketserver import ThreadingUDPServer

   if __name__ == '__main__':
    serv = ThreadingUDPServer(('',20000), TimeHandler)
    serv.serve_forever()

直接使用 socket �实现一个UDP�务器也�难,下�是一个例�:

from socket import socket, AF_INET, SOCK_DGRAM
import time

def time_server(address):
    sock = socket(AF_INET, SOCK_DGRAM)
    sock.bind(address)
    while True:
        msg, addr = sock.recvfrom(8192)
        print('Got message from', addr)
        resp = time.ctime()
        sock.sendto(resp.encode('ascii'), addr)

if __name__ == '__main__':
    time_server(('', 20000))