废话不多说,先看一个最简单的聊天室的架构图。
1. socket相关的api 参数在python3.2以上都改为了 byte类型
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()复制代码