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

2018-02-24 15:27 更新

問題

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

解決方案

跟TCP一樣,UDP服務(wù)器也可以通過使用?<span class="pre" style="box-sizing: border-box;">socketserver</span>?庫很容易的被創(chuàng)建。 例如,下面是一個簡單的時間服務(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()

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

我們來測試下這個服務(wù)器,首先運(yùn)行它,然后打開另外一個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))
>>>

討論

一個典型的UPD服務(wù)器接收到達(dá)的數(shù)據(jù)報(消息)和客戶端地址。如果服務(wù)器需要做應(yīng)答, 它要給客戶端回發(fā)一個數(shù)據(jù)報。對于數(shù)據(jù)報的傳送, 你應(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á)到同樣的效果, 但是前面的兩個方法對于UDP連接而言更普遍。

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

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

from socketserver import ThreadingUDPServer

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

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

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)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號