Python 編寫的 線上多人多聊天室伺服器

發表於2016-01-19

模組的選擇:

使用 asyncore 和 asynchat 模組,實現 多客戶端的接入 和 伺服器、客戶端之間訊息的傳遞。

幾個類及各自的功能:

EndSession:異常類,用於產生異常退出

CommandHandler:訊息分類處理,區分 命令訊息 和 聊天訊息

Room:進行使用者的會話管理,比如保持房間內所有使用者的會話、使用者進入房間、退出房間、退出伺服器、向其他使用者傳送訊息

Hall:大廳。輸入暱稱登陸、退出伺服器、選擇聊天室

ChatRoom:聊天室。進入聊天室、檢視聊天室線上使用者、廣播訊息、返回大廳

ChatSession:為每一個使用者會話建立例項,保持使用者暱稱,處理伺服器與客戶端的訊息傳遞

Server:伺服器。啟動的同時,建立 大廳、三個聊天室,偵探、接受使用者的接入請求,字典形式儲存所有登陸伺服器的使用者的 會話:暱稱。

伺服器執行:

建立一個 Server類 作為伺服器,繼承 asyncore.dispatcher ,程式執行時,建立 例項物件 ,初始化伺服器、偵聽使用者的服務請求,同時例項化 大廳、三個聊天室。

host = ‘localhost’

port = 5000

if __name__ == ‘__main__’:

s = Server(port, host)

asyncore.loop()

class Server(asyncore.dispatcher):

def __init__(self, port, host):

asyncore.dispatcher.__init__(self)

self.create_socket(socket.AF_INET, socket.SOCK_STREAM)

self.set_reuse_addr()

self.bind((host, port))

self.listen(5)

self.users = {}

self.hall = Hall(self, ‘Hall’)

self.python = ChatRoom(self, ‘python’)

self.write = ChatRoom(self, ‘write’)

self.pm = ChatRoom(self, ‘pm’)

接受使用者接入請求:

Server類 的 handle_accept方法,接收使用者接入請求,為每個使用者建立一個 socket物件,表示使用者。併為每一個 socket物件呼叫 ChatSession類 建立例項。

class Server(asyncore.dispatcher):

def handle_accept(self):

conn,addr = self.accept()

ChatSession(self, conn)

class ChatSession(asynchat.async_chat):

def __init__(self, server, sock):

asynchat.async_chat.__init__(self, sock)

self.set_terminator(‘\r\n’)

self.data = []

self.client_name = ”

使用者登入、退出:

使用者在大廳、三個聊天室直接的切換,通過 ChatSession類 的 enter方法 完成。

class ChatSession(asynchat.async_chat):

def enter(self, room):

”’  更改使用者所在的房間,並將 使用者的會話 新增到 房間的session列表  ”’

self.room = room room.add(self) # 將新 session 新增到 hall 的 sessions 列表

對訊息的處理:

ChatSession類 的 collectincomingdata方法,快取從使用者接收到的訊息,found_terminator方法 將訊息傳送給 使用者所在房間的 room.handle方法進行處理,也就是繼承自 CommandHandler 的 handle方法。

handle方法 約定,以\開頭的是命令,其他是普通聊天訊息,命令對應的類方法以do_開頭。

如果訊息是以\開頭,通過 getattr 方法檢查所在房間是否有對應的do_方法,如果有,執行,否則,提示命令未知。

而對於普通的聊天訊息,預設通過do_broadcast方法廣播給聊天室其他使用者。

class ChatSession(asynchat.async_chat):

def collect_incoming_data(self, data):

”’

快取從使用者收到的資料

”’

self.data.append(data)

def found_terminator(self):

”’

將 從使用者接收到的訊息 發給 房間的訊息處理函式 進行處理

”’

line = ”.join(self.data) # 將所有發來的訊息放入 line 中

self.data = []

try:

self.room.handle(self, line)  #對當前所在房間的方法進行查詢,判斷是否是命令

except EndSession:

self.handle_close()  #如果不是,呼叫退出房間的方法

class Room(CommandHandler):

class CommandHandler:

def handle(self, session, line):

if not line: return

parts = line.split(‘ ‘, 1)

if parts[0][0] == ‘/’: # 檢查是否命令,命令格式:/cmd

cmd = parts[0][1:]

try:

line = parts[1].strip()

except IndexError:

line = None

meth = getattr(self, ‘do_’+cmd, None)

try:

meth(session, line)

except TypeError:

self.unknow(session, cmd)

else:  # 對於普通的聊天資訊,預設直接呼叫 ‘do_broadcast’ 方法廣播給房間內其他人

meth = getattr(self, ‘do_broadcast’, line)

try:

meth(session, line)

except TypeError:

self.unknow(session, line)

def unknow(self, session, cmd):

”’

當找不到相應的命令時,進行提示

”’

session.send(‘Unknow command: %s\r\n’ % cmd)

class Hall(Room):

def do_login(self, session, line):

”’

對使用者輸入的暱稱處理。

給出幫助資訊。

”’

def do_logout(self, session, line):

”’

使用者退出連線

”’

def do_python(self, session, line):

”’

進入聊天室 python

”’

def do_write(self, session, line):

session.enter(self.server.write)

def do_pm(self):

session.enter(self.server.pm)

def do_help(self, session, line):

”’

傳送幫助資訊

”’

class ChatRoom(Room):

def do_online(self, session, line):

”’

檢視房間內有哪些其他使用者

”’

def do_back(self, session, line):

”’

退回到大廳

”’

def do_broadcast(self, session, line):

”’

廣播訊息給房間內其他所有人

”’

def do_help(self, session, line):

完整程式碼倉庫

專案wiki

相關文章