模組的選擇:
使用 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):