1. 程式人生 > >Python下UDP的Socket程式設計:伺服器端因客戶端的非正常退出而報錯?

Python下UDP的Socket程式設計:伺服器端因客戶端的非正常退出而報錯?

伺服器端程式碼


import socket
import threading
import logging
import datetime

logging.basicConfig(format='%(message)s', level=logging.INFO)


class ChatUdpServer:
    def __init__(self, ip='127.0.0.1', port=9999, interval=10,):
        self.sock = socket.socket(type=socket.SOCK_DGRAM)
        self.addr = ip, port
        self.event = threading.Event()
        self.client_set = {}
        self.interval = interval

    def start(self):
        self.sock.bind(self.addr)
        threading.Thread(target=self.recv,).start()

    def recv(self):
        while not self.event.is_set():
            try:
                data, raddr_info = self.sock.recvfrom(1024)
                print(self.sock, raddr_info)
            except ConnectionResetError:
                print('{} client is stop'.format(raddr_info))
                self.client_set.pop(raddr_info)
                continue
            # temp_key = []
            # data, raddr_info = self.sock.recvfrom(1024)
            # if data.strip() == b'QAQ-TAT':
            #     self.client_set[raddr_info] = datetime.datetime.now().timestamp()
            #     continue

            if data.strip() == b'exit':
                self.client_set.pop(raddr_info)
                continue

            current_time = datetime.datetime.now().timestamp()
            self.client_set[raddr_info] = current_time

            logging.info((data, raddr_info))
            msg = '{} send {}'.format(raddr_info, data.decode()).encode()

            for addr, ts in self.client_set.items():
                # if current_time - ts > self.interval:
                #     temp_key.append(addr)
                #     continue
                self.sock.sendto(msg, addr)

            # for i in temp_key:
            #     self.client_set.pop(i)

    def stop(self):
        self.sock.close()


def main():
    cs = ChatUdpServer()
    cs.start()

    while True:
        cmd = input('>>>')
        if cmd.strip() == 'exit':
            cs.stop()
            break
        print(cs.client_set)


if __name__ == '__main__':
    main()

客戶端程式碼

import socket
import threading
import logging
import random

logging.basicConfig(format='%(message)s', level=logging.INFO)


class ChatUdpClient:
    def __init__(self, ip='127.0.0.1', port=9999):
        self.sock = socket.socket(type=socket.SOCK_DGRAM)
        self.raddr = ip, port
        self.event = threading.Event()
        self.randomIP = ip, random.randint(10000, 65535)

    def start(self):
        self.sock.bind(self.randomIP)  # 嘗試不連線,結果與connect()下一樣。
        # self.sock.connect(self.raddr)
        threading.Thread(target=self.recv,).start()

    def recv(self):
        while not self.event.is_set():
            try:
                data, raddr = self.sock.recvfrom(1024)
            except OSError:
                logging.info('client is quit')
                break
            logging.info(data.decode())

    def send(self, msg: str):
        msg = '{}'.format(msg).encode()
        self.sock.sendto(msg, self.raddr)

    def stop(self):
        self.sock.close()


if __name__ == '__main__':
    cc = ChatUdpClient()
    cc.start()

    while 1:
        cmd = input('{} >>>'.format(cc.randomIP))
        if cmd.strip() == 'exit':
            cc.send(cmd)
            cc.stop()
            break
        cc.send(cmd)


print()語句,用來除錯。  伺服器端註釋程式碼是為了加心跳包。

環境:windows7 下 Pycharm本地環境測試

1、伺服器端開啟

2、客戶端開啟兩次(如果隨機的埠被佔用,重啟)

3、client1傳送資料,client2傳送資料

4、非正常退出client1:點Pycharm終止鍵

5、client2傳送資料多次。此時圖示結果如下:

伺服器視窗:

ConnectionResetError 異常丟擲。 且多次被丟擲。原因未明。作為UDP連線協議,兩端理論上是無聯絡的。伺服器端因客戶端的非正常退出而報錯???

客戶端1視窗:

客戶端2視窗:

解決方法:增加心跳機制,由心跳機制去控制刪除異常掉線的客戶端。