1. 程式人生 > 其它 >44.socket程式設計(三)

44.socket程式設計(三)

4:執行緒級別的UDPServer

UDP伺服器端

    import threading
    import socket
    class Server:
        # 初始化:繫結埠
        def __init__(self, ip='127.0.0.1', port=9000):
            self.socket_instance = socket.socket(type=socket.SOCK_DGRAM)
            self.addr = (ip, port)
            self.event = threading.Event()
            self.clients 
= set() self.__run() def __run(self): # 繫結地址,監聽地址 self.socket_instance.bind(self.addr) # 將阻塞的__recvfrom()方法放到執行緒執行 threading.Thread(target=self.__recvfrom, name='recvfrom').start() def __recvfrom(self): while not self.event
.is_set(): try: # 這裡捕獲異常是由於一旦關閉socket,那麼data, addr不會獲取到值,那麼應該捕獲異常,給data, addr賦值 data, addr = self.socket_instance.recvfrom(1024) data = data.decode().strip() print(data) except Exception as e: data
= 'quit' addr = 'None' # 拿到心跳資訊 if data == '@#!%%^SDF@': self.clients.add(addr) continue # 拿到資料, 如果是quit if data == 'quit': if addr in self.clients: self.clients.remove(addr) # 這裡continue的原因是,udp不需要與客戶端建立連線,每次請求來了以後,可以直接拿到客戶端傳送的資料 # 不是break continue self.clients.add(addr) threading.Thread(target=self.__send,args=(data, ), name='send').start() def __send(self, data): for addr in self.clients: self.socket_instance.sendto('Robby收到{}\n'.format(data).encode(), addr) def stop(self): self.clients.clear() self.event.set() self.event.wait(3) self.socket_instance.close() print('UDP伺服器停止') raise SystemExit(0) if __name__ == '__main__': # 1: 啟動伺服器端 chatServer = Server() # 2:使用者可以輸入quit暫停伺服器 while True: cmd = input('請輸入命令: ').strip() chatServer.stop() if cmd == 'quit' else print('可以輸入quit暫停伺服器端')

UDP客戶端

    import threading
    import socket
    class Client:
        def __init__(self, ip='127.0.0.1', port=9000, interval=5):
            self.socket_instance = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
            self.addr = (ip, port)
            self.event = threading.Event()
            self.interval = interval
            self.__run()
        def __sendHeartBeat(self):
            while not self.event.wait(5):
                # 客戶端傳送的心跳包,伺服器端不會回包
                self.socket_instance.sendto('@#!%%^SDF@'.encode(), self.addr)
        def __recvfrom(self):
            while not self.event.is_set():
                try:
                    data, addr = self.socket_instance.recvfrom(1024)
                    print(addr)
                    print(data.decode())
                except Exception as e:
                    print(e)
        def __serverQuit(self):
            self.socket_instance.sendto('quit'.encode(), self.addr)
        def send(self, data:str):
            self.socket_instance.sendto(data.encode(), self.addr)
        def __run(self):
            # 啟動一個執行緒,傳送心跳包
            threading.Thread(target=self.__sendHeartBeat, daemon=True).start()
            # 啟動一個執行緒,接收資料
            threading.Thread(target=self.__recvfrom, ).start()
        def stop(self):
            self.__serverQuit()
            self.event.set()
            self.event.wait(3)
            self.socket_instance.close()
            print('UDP客戶端停止')
            raise SystemExit(0)
    if __name__ == '__main__':
        # 1:啟動客戶端
        client = Client()
        # 客戶端可以像伺服器端傳送資料
        while True:
            cmd = input('請輸入內容:').strip()
            client.stop() if cmd == 'quit' else client.send(cmd)

1.TCP 和UDP的區別有哪些

  • TCP傳輸資料使用位元組流的方式傳輸,而UDP是資料報傳輸;

  • TCP對網路條件要求高,而UDP更適合實時傳輸;

  • TCP程式設計可以保證傳輸的可靠性,UDP則不保證;

  • TCP會產生粘包現象,而UDP則容易丟包;

  • TCP使用listen方法和accpet方法,而UDP不需要;

  • TCP使用recv方法和send方法,而UDP使用recvfrom方法和sendto方法;

2.UDP 伺服器端的實現

  • 建立 socket 物件;

  • 向socket 物件繫結伺服器地址;

  • 進入與客戶端互動資料的迴圈階段;

  • 接收客戶端發來的資料(包括 bytes 物件 data,以及客戶端的 IP 地址和埠號 addr,其中 addr 為二元組 (host, port);

  • 列印接收資訊,表示從地址為 addr 的客戶端接收到資料);

  • 關閉;

3.UDP客戶端的實現

  • 建立 socket 物件;

  • 初始化 UDP 伺服器的地址;

  • 進入與伺服器互動資料的迴圈階段;

  • 等待使用者輸入資料;

  • 向伺服器端傳送接收資料;

  • 關閉套接字,不再向伺服器傳送資料;