11.3 創(chuàng)建UDP服務(wù)器

2018-02-24 15:27 更新

問(wèn)題

你想實(shí)現(xiàn)一個(gè)基于UDP協(xié)議的服務(wù)器來(lái)與客戶端通信。

解決方案

跟TCP一樣,UDP服務(wù)器也可以通過(guò)使用?<span class="pre" style="box-sizing: border-box;">socketserver</span>?庫(kù)很容易的被創(chuàng)建。 例如,下面是一個(gè)簡(jiǎn)單的時(shí)間服務(wù)器:

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

跟之前一樣,你先定義一個(gè)實(shí)現(xiàn)?<span class="pre" style="box-sizing: border-box;">handle()</span>?特殊方法的類,為客戶端連接服務(wù)。 這個(gè)類的?<span class="pre" style="box-sizing: border-box;">request</span>屬性是一個(gè)包含了數(shù)據(jù)報(bào)和底層socket對(duì)象的元組。<span class="pre" style="box-sizing: border-box;">client_address</span>?包含了客戶端地址。

我們來(lái)測(cè)試下這個(gè)服務(wù)器,首先運(yùn)行它,然后打開(kāi)另外一個(gè)Python進(jìn)程向服務(wù)器發(fā)送消息:

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

討論

一個(gè)典型的UPD服務(wù)器接收到達(dá)的數(shù)據(jù)報(bào)(消息)和客戶端地址。如果服務(wù)器需要做應(yīng)答, 它要給客戶端回發(fā)一個(gè)數(shù)據(jù)報(bào)。對(duì)于數(shù)據(jù)報(bào)的傳送, 你應(yīng)該使用socket的?<span class="pre" style="box-sizing: border-box;">sendto()</span>?和?<span class="pre" style="box-sizing: border-box;">recvfrom()</span>?方法。 盡管傳統(tǒng)的?<span class="pre" style="box-sizing: border-box;">send()</span>?和?<span class="pre" style="box-sizing: border-box;">recv()</span>?也可以達(dá)到同樣的效果, 但是前面的兩個(gè)方法對(duì)于UDP連接而言更普遍。

由于沒(méi)有底層的連接,UPD服務(wù)器相對(duì)于TCP服務(wù)器來(lái)講實(shí)現(xiàn)起來(lái)更加簡(jiǎn)單。 不過(guò),UDP天生是不可靠的(因?yàn)橥ㄐ艣](méi)有建立連接,消息可能丟失)。 因此需要由你自己來(lái)決定該怎樣處理丟失消息的情況。這個(gè)已經(jīng)不在本書討論范圍內(nèi)了, 不過(guò)通常來(lái)說(shuō),如果可靠性對(duì)于你程序很重要,你需要借助于序列號(hào)、重試、超時(shí)以及一些其他方法來(lái)保證。 UDP通常被用在那些對(duì)于可靠傳輸要求不是很高的場(chǎng)合。例如,在實(shí)時(shí)應(yīng)用如多媒體流以及游戲領(lǐng)域, 無(wú)需返回恢復(fù)丟失的數(shù)據(jù)包(程序只需簡(jiǎn)單的忽略它并繼續(xù)向前運(yùn)行)。

<span class="pre" style="box-sizing: border-box;">UDPServer</span>?類是單線程的,也就是說(shuō)一次只能為一個(gè)客戶端連接服務(wù)。 實(shí)際使用中,這個(gè)無(wú)論是對(duì)于UDP還是TCP都不是什么大問(wèn)題。 如果你想要并發(fā)操作,可以實(shí)例化一個(gè)?<span class="pre" style="box-sizing: border-box;">ForkingUDPServer</span>?或<span class="pre" style="box-sizing: border-box;">ThreadingUDPServer</span>?對(duì)象:

from socketserver import ThreadingUDPServer

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

直接使用?<span class="pre" style="box-sizing: border-box;">socket</span>?來(lái)是想一個(gè)UDP服務(wù)器也不難,下面是一個(gè)例子:

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))
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)