45網(wǎng)絡(luò)編程_UDP

?

成都創(chuàng)新互聯(lián)專注于企業(yè)成都營(yíng)銷網(wǎng)站建設(shè)、網(wǎng)站重做改版、宕昌網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5高端網(wǎng)站建設(shè)成都商城網(wǎng)站開發(fā)、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為宕昌等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。

?

?

UDP編程:

?

>netstat -anp tcp | find "9998"?? #win下

#echo "123abc" | nc -u 127.0.0.1 9998

?

應(yīng)用:

無(wú)連接協(xié)議,基于如下假設(shè):網(wǎng)絡(luò)足夠好、消息不會(huì)丟包、包不會(huì)亂序;

1、音頻、視頻傳輸,一般丟些包,問(wèn)題不大,最多丟些圖像、聽不清說(shuō)話,再次說(shuō)話即可;

2、海量采集數(shù)據(jù),如傳感器發(fā)來(lái)的數(shù)據(jù),丟幾十、幾百條數(shù)據(jù)也沒關(guān)系;

3、DNS協(xié)議,數(shù)據(jù)內(nèi)容小,一個(gè)包就能查詢到結(jié)果,不存在亂序、丟包,重新請(qǐng)求解析;

?

注:

即使在LAN,也不能保證不丟包,且包的到達(dá)不一定有序;

一般來(lái)說(shuō),UDP性能優(yōu)于TCP,但可靠性要求高的場(chǎng)合還是選擇用TCP;

QUIC,quick udp internet connection,google,是谷歌制定的一種基于UDP的低時(shí)延的互聯(lián)網(wǎng)傳輸層協(xié)議。在2016年11月國(guó)際互聯(lián)網(wǎng)工程任務(wù)組(IETF)召開了第一次QUIC工作組會(huì)議,受到了業(yè)界的廣泛關(guān)注。這也意味著QUIC開始了它的標(biāo)準(zhǔn)化過(guò)程,成為新一代傳輸層協(xié)議;

?

?

UDP服務(wù)端編程步驟:

創(chuàng)建socket對(duì)象,sock=socket.socket(type=socket.SOCK_DGRAM);

綁定ip和port,bind()方法;

傳輸數(shù)據(jù):

???????? recvfrom(bufsize[,flags]),接收數(shù)據(jù),獲取一個(gè)二元組(string,address);

???????? sendto(string,address),發(fā)送數(shù)據(jù),發(fā)送某信息給某地址;

釋放資源;

?

例:

import socket

?

sock = socket.socket(type=socket.SOCK_DGRAM)

?

addr = ('127.0.0.1', 9998)

sock.bind(addr)

?

data, clientaddr = sock.recvfrom(1024)

print(clientaddr)

?

msg = 'ack: {}'.format(data.decode())

sock.sendto(msg.encode(), clientaddr)

輸出:

('127.0.0.1', 9999)

45網(wǎng)絡(luò)編程_UDP

45網(wǎng)絡(luò)編程_UDP

?

例,ChatServerUdp:

ver1:

class ChatServerUdp:

??? def __init__(self, ip='127.0.0.1', port=9998):

??????? self.sock = socket.socket(type=socket.SOCK_DGRAM)

??????? self.addr = (ip, port)

??????? self.event = threading.Event()

??????? self.clients = set()?? #集合,去重,client主動(dòng)退出后要清此數(shù)據(jù)結(jié)構(gòu)

?

??? def start(self):

??????? self.sock.bind(self.addr)

??????? threading.Thread(target=self._recv, name='recv').start()

?

??? def stop(self):

??????? for c in self.clients:?? #業(yè)務(wù)中udp的server關(guān)閉時(shí)不會(huì)通知client

??????????? self.sock.sendto(b'end', c)

??????? self.sock.close()?? #udp的socket關(guān)閉很快,不會(huì)有很多垃圾

??????? self.event.set()

?

??? def _recv(self):?? #_recv中使用多線程場(chǎng)景,在一對(duì)多情況下,server發(fā)送消息和接收消息出現(xiàn)不匹配時(shí),用另一線程單獨(dú)處理發(fā)送數(shù)據(jù),否則接收和發(fā)送是同步,只有等發(fā)送完才能繼續(xù)再次接收

??????? while not self.event.is_set():

??????????? data, client = self.sock.recvfrom(1024)

??????????? data = data.strip().decode()

?

??????????? if data == 'quit':

??????????????? self.clients.remove(client)

??????????????? continue?? #關(guān)鍵,接收下個(gè)client的消息

??????????? self.clients.add(client)

??????????? print(self.clients)

?

??????????? msg = 'ack: {}'.format(data)

??????????? for c in self.clients:

??????????????? self.sock.sendto(msg.encode(), c)

?

if __name__ == '__main__':

??? cs = ChatServerUdp()

??? cs.start()

??? myutils.show_threads()

?

例:

ver2:

增加ack和heartbeat機(jī)制;

心跳即一端定時(shí)發(fā)往另一端信息,一般每次發(fā)的數(shù)據(jù)越少越好,心跳時(shí)間間隔約定好就行,ack響應(yīng),一端收到另一端的消息后返回的信息;

心跳包設(shè)計(jì):

c主動(dòng),一般由client發(fā)hb-->server,server并不需要發(fā)ack-->client,只需要記錄client還活著就行;

s主動(dòng),server發(fā)hb掃一遍client,一般需要client發(fā)ack響應(yīng)來(lái)表示活著,server沒收到ack就斷開與client連接,server移除其信息,這種實(shí)現(xiàn)較為復(fù)雜,用的少;

c-s雙向,用的更少;

class ChatServerUdp:

??? def __init__(self, ip='127.0.0.1', port=9998, interval=10):

??????? self.sock = socket.socket(type=socket.SOCK_DGRAM)

??????? self.addr = (ip, port)

??????? self.event = threading.Event()

??????? self.clients = {}

??????? self.interval = interval

?

??? def start(self):

??????? self.sock.bind(self.addr)

??????? threading.Thread(target=self._recv, name='recv').start()

?

??? def stop(self):

??????? for c in self.clients:

??????????? self.sock.sendto(b'end', c)

??????? self.sock.close()

??????? self.event.set()

?

??? def _recv(self):

??????? while not self.event.is_set():

??????????? lostset = set()

??????????? data, client = self.sock.recvfrom(1024)

??????????? data = data.strip().decode()

?

??????????? current = datetime.datetime.now().timestamp()

??????????? if data == '^hb^' or data == 'reg':

??????????????? print('hb')

??????????????? self.clients[client] = current

??????????????? continue

??????????? elif data == 'quit':

??????????????? self.clients.pop(client, None)

??????????????? logging.info('{} leaving'.format(client))

?????? ?????????continue

?

??????????? self.clients[client] = current

??????????? print(self.clients)

?

??????????? msg = 'ack: {} {}\n{}\n'.format(*client, data)

??????????? logging.info(msg)

??????????? for c, stamp in self.clients.items():

??????????????? if current - stamp > self.interval:

??????????????????? lostset.add(c)

??????????????? else:

??????????????????? self.sock.sendto(msg.encode(), c)

?

??????????? for c in lostset:

??????????????? self.clients.pop(c)

?

if __name__ == '__main__':

??? cs = ChatServerUdp()

??? cs.start()

??? myutils.show_threads()

?

UDP客戶端編程步驟:

創(chuàng)建socket對(duì)象,socket.socket(type=socket.SOCK_DGRAM);

發(fā)送數(shù)據(jù):sendto(string,address),發(fā)送某信息給某地址;

釋放資源;

?

udp客戶端編程中,只能在sendto()后,才能recvfrom(),否則OSError;

?

例:

sock = socket.socket(type=socket.SOCK_DGRAM)

?

addr = ('127.0.0.1', 9998)

data = 'test_data'.encode()

?

sock.sendto(data, addr)?? #方式1,使用sendto()和recvfrom(),建議用此種方式

data, saddr = sock.recvfrom(1024)?? #也可用recv(),只不過(guò)不知道誰(shuí)發(fā)的消息了

print(data, saddr)

sock.close()

?

# sock.connect(addr)?? #方式2,用connect()連接后才能用send()或sendto(),沒有connect()連接只能用sendto();此方式可能會(huì)有問(wèn)題,client-->server正常,server-->client,server上連client的端口不對(duì)了

# sock.send(data)

# data, saddr = sock.recvfrom(1024)

# print(data, saddr)

# sock.close()

?

例:

addr = ('127.0.0.1', 9998)

event = threading.Event()

?

def recv1(sock:socket.socket, event:threading.Event):

??? while not event.is_set():

??????? data, saddr = sock.recvfrom(1024)

??????? logging.info('recv: {} ; from: {}'.format(data, saddr))

?

sock1 = socket.socket(type=socket.SOCK_DGRAM)

sock1.sendto('udp client1 send'.encode(), addr)

threading.Thread(target=recv1, args=(sock1, event)).start()?? #recvfrom()必須在sendto()或connect()之后,否則OSError,即recvfrom()操作之前應(yīng)該先sendto()或connect();如果用connect(),則遠(yuǎn)端必須有服務(wù)

?

def recv2(sock:socket.socket, event:threading.Event):

??? while not event.is_set():

??????? data, saddr2 = sock.recvfrom(1024)

??????? logging.info('recv: {} ; from: {}'.format(data, saddr2))

?

sock2 = socket.socket(type=socket.SOCK_DGRAM)

sock2.connect(addr)

threading.Thread(target=recv2, args=(sock2, event)).start()

threading.Event().wait(5)

sock2.sendto('udp client2 send'.encode(), addr)

event.wait(2)

sock2.send('udp client2.1 send'.encode())

?

while True:

??? if input('>>> ').strip() == 'quit':

??????? sock1.close()

??????? sock2.close()

??????? event.wait(3)

??????? break

logging.info('end')

?

例,ChatClientUdp:

注:此代碼有問(wèn)題,在發(fā)送hb后一直阻塞

class ChatClientUdp:

??? def __init__(self, ip='127.0.0.1', port=9998, interval=5):

??????? self.sock = socket.socket(type=socket.SOCK_DGRAM)

??????? self.addr = (ip, port)

??????? self.event = threading.Event()

??????? self.interval = interval

??????? self.sock.connect(self.addr)

??????? self._sendhb()

?

??? def start(self):

??????? # self.sock.send(b'reg')

??????? threading.Thread(target=self._sendhb, name='hb', daemon=True).start()

??????? # threading.Thread(target=self._recv, name='recv').start()

??????? # self._recv()

?

??? def stop(self):

??????? self.send()

??????? self.sock.close()

??????? self.event.wait(2)

??????? self.event.set()

?

??? def _sendhb(self):

??????? while not self.event.wait(5):

??????????? self.sock.sendto(b'^hb^', self.addr)

?

??? def send(self, msg:str='quit'):

??????? self.sock.sendto(msg.encode(), self.addr)

?

??? def _recv(self):

??????? while not self.event.is_set():

??????????? data, addr = self.sock.recvfrom(1024)

??????????? logging.info('recv {} from {}'.format(data, addr))

?

cc = ChatClientUdp()

cc.start()

while True:

??? data = input('plz input string>>> ')

??? if data == 'quit':

??????? cc.stop()

??????? break

??? else:

??????? cc.send(data)

logging.info('end')

?

網(wǎng)站名稱:45網(wǎng)絡(luò)編程_UDP
轉(zhuǎn)載源于:http://muchs.cn/article12/jojdgc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供App設(shè)計(jì)、靜態(tài)網(wǎng)站網(wǎng)站收錄、服務(wù)器托管網(wǎng)站營(yíng)銷網(wǎng)站制作

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)公司