45網(wǎng)絡(luò)編程_UDP-創(chuàng)新互聯(lián)

創(chuàng)新互聯(lián)公司專(zhuān)注為客戶(hù)提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于成都做網(wǎng)站、網(wǎng)站制作、成都外貿(mào)網(wǎng)站建設(shè)、浮山網(wǎng)絡(luò)推廣、小程序開(kāi)發(fā)、浮山網(wǎng)絡(luò)營(yíng)銷(xiāo)、浮山企業(yè)策劃、浮山品牌公關(guān)、搜索引擎seo、人物專(zhuān)訪(fǎng)、企業(yè)宣傳片、企業(yè)代運(yùn)營(yíng)等,從售前售中售后,我們都將竭誠(chéng)為您服務(wù),您的肯定,是我們大的嘉獎(jiǎng);創(chuàng)新互聯(lián)公司為所有大學(xué)生創(chuàng)業(yè)者提供浮山建站搭建服務(wù),24小時(shí)服務(wù)熱線(xiàn):18982081108,官方網(wǎng)址:muchs.cn

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)題不大,最多丟些圖像、聽(tīng)不清說(shuō)話(huà),再次說(shuō)話(huà)即可;

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

3、DNS協(xié)議,數(shù)據(jù)內(nèi)容小,一個(gè)包就能查詢(xún)到結(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)召開(kāi)了第一次QUIC工作組會(huì)議,受到了業(yè)界的廣泛關(guān)注。這也意味著QUIC開(kāi)始了它的標(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中使用多線(xiàn)程場(chǎng)景,在一對(duì)多情況下,server發(fā)送消息和接收消息出現(xiàn)不匹配時(shí),用另一線(xiàn)程單獨(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沒(méi)收到ack就斷開(kāi)與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客戶(hù)端編程步驟:

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

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

釋放資源;

udp客戶(hù)端編程中,只能在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(),沒(méi)有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ù)器可以了解下創(chuàng)新互聯(lián)cdcxhl.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線(xiàn),公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性?xún)r(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專(zhuān)為企業(yè)上云打造定制,能夠滿(mǎn)足用戶(hù)豐富、多元化的應(yīng)用場(chǎng)景需求。

分享文章:45網(wǎng)絡(luò)編程_UDP-創(chuàng)新互聯(lián)
轉(zhuǎn)載注明:http://muchs.cn/article36/dejspg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供云服務(wù)器、網(wǎng)站排名、做網(wǎng)站、網(wǎng)站制作營(yíng)銷(xiāo)型網(wǎng)站建設(shè)、外貿(mào)建站

廣告

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

綿陽(yáng)服務(wù)器托管