python3實現聊天室

一直沒有寫過python異步方面的代碼,最近看到ascyncore這個庫,就參照python基礎教程寫了一個相似qq的聊天室。記錄下一些坑。 


 廢話很少說,先看一個最簡單的聊天室的架構圖。python



1. socket相關的api 參數在python3.2以上都改成了 byte類型api

2. super().method() === Fu.method(self)服務器


from asyncore import dispatcher
from asynchat import async_chat
import socket, asyncore 

class EndSession(Exception): pass


class ChatServer(dispatcher):
    def __init__(self, port, name):
        super().__init__()
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind(('', port))
        self.listen(10)
        self.name = name 
        self.users = {}     # 管理用戶信息
        self.main_room = ChatRoom(self)

    def handle_accepted(self, conn, addr):
        ChatSession(self, conn) 


class ChatSession(async_chat):
    """維持每個客戶端對接"""
    def __init__(self, server, sock):
        super().__init__(sock)
        self.server = server 
        self.set_terminator(b"\r\n")        # 注意這個地方要是byte(debug了很久,,不看文檔的結果。。)
        self.data = []
        self.name = None
        self.enter(LoginRoom(server))       # 全部的客戶端 一鏈接 就進入LoginRoom (能夠當成一個單人的聊天室)
    
    def enter(self, room):
        try:
            cur = self.room
            cur.remove(self)
        except AttributeError: pass
        self.room = room
        room.add(self)         # 向聊天室

    def collect_incoming_data(self, data): self.data.append(data.decode("utf-8"))

    def found_terminator(self):
        line = ''.join(self.data)
        self.data = []
        try:
            self.room.handle(self, line)
        except EndSession:      # 自定義的一個 exception 
            self.handle_close() 

    def handle_close(self):
        async_chat.handle_close(self) # TODO:等同於super().handle_close()
        self.enter(LogoutRoom(self.server))

    def push(self, message):
        """一個小坑,push中的message必須encode一下成byte,str類型不能直接傳給push"""
        super().push(message.encode("utf-8"))



class Room:
    """聊天室"""
    def __init__(self, server):
        self.server = server 
        self.sessions = []

    def unknown(self, session, cmd):
        """處理沒有規定的 命令"""
        session.push("Unknown command: {}s\r\n".format(cmd))        # push用來作服務器的迴應

    def handle(self, session, cmd):
        if not cmd.strip(): return 
        parts = cmd.split(" ",1)
        cmd = parts[0]
        try: 
            line = parts[1].strip()
        except IndexError:
            line = ""
        try:
            meth = getattr(self, "do_" + cmd, None)
            meth(session, line)
        except TypeError:
            self.unknown(session, cmd)

    def add(self, session):
        self.sessions.append(session)
    
    def remove(self, session):
        self.sessions.remove(session)
    
    def broadcast(self, message):
        for session in self.sessions:
            session.push(message)


class LoginRoom(Room):
    def add(self, session):
        super().add(session)
        self.broadcast("Welcome to {}\r\n".format(self.server.name))        #.encode("utf-8"))

    def do_login(self, session, line):
        name = line.strip()
        if not name:
            session.push("Please enter a name\r\n")
        elif name in self.server.users:
            session.push("The name is exist!\r\n")
            session.push("please try again\r\n")
        else:
            session.name = name
            session.enter(self.server.main_room)


class ChatRoom(Room):
    def add(self, session):
        self.sessions.append(session)
        self.server.users[session.name] = session
        self.broadcast(session.name + " has entered the room.\r\n")

    def remove(self, session):
        self.broadcast(session.name + " has leave the room.\r\n")
        super().remove(session)

    def do_say(self, session, message):
        self.broadcast(session.name + ": {}\r\n".format(message))

    def do_look(self, session, message):
        session.push(str([session.name for session in self.sessions])+ "\r\n")

    def do_who(self, session, message):
        session.push("The following are logged in :\r\n")
        for name in self.server.users:
            session.push(name+"\r\n")


class LogoutRoom(Room):
    def add(self, session):
        try:
            del self.server.users[session.name]
        except KeyError: 
            pass

if __name__ == "__main__":
    s = ChatServer(5050,"test")
    try: 
        asyncore.loop()
    except KeyboardInterrupt:
        print()複製代碼
相關文章
相關標籤/搜索