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))