1. 程式人生 > >Python-UDP編程

Python-UDP編程

opened timestamp 圖片 NPU __init__ true 使用 ems cti

1、UDP編程:

  測試命令:

    windows:

       netstat -anp udp | findstr 9999
    Linux: 發給服務器數據

      echo ‘233‘ | nc -u 127.0.0.1 9999

2、UDP服務器端編程:

  UDP服務器端編程流程:(從圖中可以看到,服務器端只需要一個socket)

    技術分享圖片

      • 創建socket 對象,socket.SOCK_DGRAM
      • 綁定IP 和 Port, bind() 方法
      • 傳輸數據:
        • 接受數據:socket.recvfrom(bufsize [, flags] ) ,獲取一個二元組()
        • 發送數據:socket.sendto(string, address) 發給某地址某信息
      • 釋放資源     
 1 import socket
 2 
 3 socket = socket.socket(type=socket.SOCK_DGRAM)
 4 
 5 ip = 127.0.0.1
 6 port = 9999
 7 
 8 laddr = ip, port
 9 
10 socket.bind(laddr) #  服務器端正式啟動
11 
12 data, raddr = socket.recvfrom(1024) #
沒有接收到,會處於阻塞狀態 13 14 socket.sendto(data, raddr) 15 16 socket.close()

  UDP客戶端編程流程

    註意:UDP是無連接協議,多以可以只有任何一端,例如客戶端數據發往服務端,服務器端存在與否無所謂。

  UDP編程中bind、connect、send、sendto、recv、recfrom方法使用

  UDP的socket對象創建後,是麽有占用本地地址額端口的

  技術分享圖片

    註意:

      1、UDP 創建socket後,不能直接recv,recvfrom,,只有知道了本地地址和端口,服務器端才能知道數據應該發給你

      2、send 和 connect 搭配使用。

  心跳機制:

    加一個ack機制 和 心跳 hearbeat

    心跳,就是一端定時的發往另一端的信息,一般每次數據越少越好,心跳時間間隔約定好就行。

    ack 即響應,一端收到另一端的消息後返回的確認消息

    心跳機制:

    1. 一般來說是客戶端定時發往服務器端的,服務器端並不需要ACK 回復客戶端,只需要記錄該客戶端還活著就行(比如群聊,如果客戶端不活著,就沒必要回復)
    2. 如果是服務器端定時發往客戶端的,一般需要 客戶端ack 響應,表示活著,,如果沒有收到ack的客戶端,服務器端移除其信息,這種實現比較復雜,較少使用。
    3. 也就是雙向都發送心跳,很少使用

UDP實現群聊:

  server端:

技術分享圖片
 1 import socket
 2 import threading
 3 import logging
 4 import datetime
 5 
 6 
 7 FORMAT = %(asctime)s %(thread)s %(threadName)s %(message)s
 8 logging.basicConfig(format=FORMAT, level=logging.INFO)
 9 
10 class ChatServer:
11     def __init__(self, ip=127.0.0.1, port=9999, interval=10):
12         self.laddr = ip, port
13         self.event = threading.Event()
14         self.sock = socket.socket(type=socket.SOCK_DGRAM)
15         self.clients = {}
16         self.interval = interval
17 
18     def start(self):
19         self.sock.bind(self.laddr)
20 
21         threading.Thread(target=self.recv, name=recvive).start()
22 
23     def recv(self):
24         while not self.event.is_set():
25             data, raddr = self.sock.recvfrom(1024)
26             localkeys = set()
27             logging.info(data)
28 
29             # 心跳信息
30             if data.strip() == b^hb^:
31                 self.clients[raddr] = datetime.datetime.now().timestamp()
32                 continue
33 
34             if data.strip() == bquit:
35                 # 若一個客戶端剛進來,還沒有加到字典中,直接pop,會報KerryError
36                 # if raddr in self.clients.keys():
37                 self.clients.pop(raddr, None)
38                 continue
39             self.clients[raddr] = datetime.datetime.now().timestamp()
40 
41             # z再次發送數據的時間,如果之後沒有再次進入,這次的時間就作為下次比較時間,所以這次的時間可以認為是最新的心跳信息
42             current = datetime.datetime.now().timestamp()
43             msg = {}--{}.format(data.decode(), ******).encode()
44 
45             # 過期的就不在發數據給客戶端了,並且剔除掉
46             #z 字典不能再遍歷的時候,刪除內容
47             for r, t in self.clients.items():
48                 if current - t > self.interval:
49                     localkeys.add(r)
50                 self.sock.sendto(msg, r)
51             for r in localkeys:
52                 self.clients.pop(r)
53 
54     def stop(self):
55         self.sock.close()
56         self.event.set()
57 
58 
59 def main():
60     cs = ChatServer()
61     cs.start()
62 
63     while True:
64         cmd = input(">>>")
65         if cmd == quit:
66             cs.stop()
67             break
68         logging.info(threading.enumerate())
69 
70 
71 if __name__ == "__main__":
72     main()
群聊server端

  client端:

技術分享圖片
 1 import socket
 2 import threading
 3 import logging
 4 import datetime
 5 
 6 
 7 FORMAT = %(asctime)s %(thread)s %(threadName)s %(message)s
 8 logging.basicConfig(format=FORMAT, level=logging.INFO)
 9 
10 class ChatClient:
11     def __init__(self, ip=127.0.0.1, port=9999, interval=3):
12         self.raddr = ip, port
13         self.event = threading.Event()
14         self.sock = socket.socket(type=socket.SOCK_DGRAM)
15         self.interval = interval
16 
17     def start(self):
18         self.sock.connect(self.raddr)
19 
20         threading.Thread(target=self.recv, name=c-recv).start()
21 
22         #  每隔interval 就發送一次心跳信息
23         threading.Thread(target=self.hearbeat, name=ht,daemon=True).start()
24 
25     def recv(self):
26         while not self.event.is_set():
27             data, raddr = self.sock.recvfrom(1024)
28             print(data)
29             print(raddr)
30     def hearbeat(self):
31         while not self.event.wait(self.interval):
32             self.send(^hb^)
33 
34 
35 
36     def send(self, msg):
37         self.sock.send(msg.encode())
38 
39     def stop(self):
40         self.sock.close()
41         self.event.set()
42 
43 
44 def main():
45     cc = ChatClient()
46     cc.start()
47 
48     while True:
49         cmd = input(>>)
50         if cmd == quit:
51             cc.stop()
52             break
53         cc.send(cmd)
54         logging.info(threading.enumerate())
55 
56 
57 
58 if __name__ == "__main__":
59     main()
群聊client端

    技術分享圖片

    註意:

      如果是如上圖所示,是一個解釋器進程,建立兩個對象,但是這兩個對象分別在不同的線程中跑。

      如果直接運行兩次 client,就是兩個客戶端進程

UDP 協議的應用:

  UDP 是無連接的,它是基於以下假設:

    網絡足夠好
    消息不會丟包

    包不會亂序

  但是,即使在局域網,也不能保證不丟包,而且包到達的不一定有序。

  應用場景:

    視頻,音頻傳輸,一般來說丟一些包。

  技術分享圖片

Python-UDP編程