Python基础教程之虚拟茶话会程序分析

来源:互联网 发布:淘宝小智 编辑:程序博客网 时间:2024/05/21 05:17

本程序需要windows 的telnet客户端支持,在windows7下telnet客户端是默认关闭的。如何打开它请参考http://jingyan.baidu.com/article/2fb0ba4055e55800f3ec5f7a.html

具体程序如下:

from asyncore import dispatcherfrom asynchat import async_chatimport socket, asyncorePORT = 5005NAME = 'TestChat'class EndSession(Exception):    passclass CommandHandler(object):    def unknown(self, session, cmd):        session.push('Unknown command: %s\r\n' % cmd)    def handle(self, session, line):        if not line.strip():            return        parts = line.split(' ', 1)        cmd = parts[0]        try:            line = parts[1].strip()        except IndexError:            line = ''        meth = getattr(self, 'do_' + cmd, None)        try:            meth(session, line)        except TypeError:            self.unknown(session, cmd)class Room(CommandHandler):    def __init__(self, server):        self.server = server        self.sessions = []    def add(self, session):        self.sessions.append(session)    def remove(self, session):        self.sessions.remove(session)    def broadcast(self, line):        for session in self.sessions:            session.push(line)    def do_logout(self, session, line):        raise EndSessionclass LoginRoom(Room):    def add(self, session):        Room.add(self, session)        self.broadcast('Welcome to %s\r\n' % self.server.name)    def unknow(self, session, cmd):        session.push('Plz log in \nUse "login <nick>"\r\n')    def do_login(self, session, line):        name = line.strip()        if not name:            session.push('Plz enter a name\r\n')        elif name in self.server.users:            session.push('The name "%s" is taken.\r\n' % name)            session.push('Plz try again.\r\n')        else:            session.name = name            session.enter(self.server.main_room)class ChatRoom(Room):    def add(self, session):        self.broadcast(session.name + ' has entered the room.\r\n')        self.server.users[session.name] = session        Room.add(self, session)    def remove(self, session):        Room.remove(self, session)        self.broadcast(session.name + ' has left the room.\r\n')    def do_say(self, session, line):        self.broadcast(session.name + ': ' + line + '\r\n')    def do_look(self, session, line):        session.push('The following are in this room\r\n')        for other in self.sessions:            session.push(other.name + '\r\n')    def do_who(self, session, line):        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:            passclass ChatSession(async_chat):    def __init__(self, server, sock):        async_chat.__init__(self, sock)        self.server = server        self.set_terminator('\r\n')        self.data = []        self.name = None        self.enter(LoginRoom(server))    def enter(self, room):        try:            cur = self.room        except AttributeError:            pass        else:            cur.remove(self)        self.room = room        room.add(self)    def collect_incoming_data(self, data):        self.data.append(data)    def found_terminator(self):        line = ''.join(self.data)        self.data = []        try:            self.room.handle(self, line)        except EndSession:            self.handle_close()    def handle_close(self):        async_chat.handle_close(self)        self.enter(LogoutRoom(self.server))class ChatServer(dispatcher):    def __init__(self, port, name):        dispatcher.__init__(self)        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)        self.set_reuse_addr()        self.bind(('', port))        self.listen(5)        self.name = name        self.users = {}        self.main_room = ChatRoom(self)    def handle_accept(self):        conn, addr = self.accept()        ChatSession(self, conn)if __name__ == '__main__':    s = ChatServer(PORT, NAME)    try:        asyncore.loop()    except KeyboardInterrupt:        print



对于charserver.py程序其构成思路是
ChatServer类(服务器类):负责侦听和连接响应
ChatRoom类(聊天房间类):负责用户管理
ChatSession类(聊天接口类):负责接收客户端发来的程序。

当有连接进入时,chatserver类将建立对应的ChatSession实例,该实例初始化过程包含:
1.初始化一些配置,如聊天语句终止符设立(必须)
2.给某些变变量赋值:包括该会话属于哪个服务器,会话的接收数据缓冲,会话的名字、
然后建立一个LoginRoom的实例,并将本聊天加入到LoginRoom中去。

LoginRoom类继承自Room超类,所以其中包含了一个ChatSession的列表,用于管理包含在本room中的会话。每次有新用户进入,则将其放入sessions列表中;当有用户退出时。则将该ChatSession类移出列表。

当用户login时,程序将调用do_login方法。然后将向ChatSession类传入使用中的ChatRoom实例(该实例在ChatServer类初始化时实例化),ChatSession调用自身的enter方法,将自身的room变量从LoginRoom更新为ChatRoom,同时调用LoginRoom的remove方法,将自己从LoginRoom的用户列表中移出。再调用ChatRoom的add方法,将自身添加到ChatRoom的用户列表中。


当用户logout时,与之类似,程序将调用do_logout方法,然后向ChatSession中传入LogoutRoom实例(调用的同时实例化),ChatSession用自身的enter方法,将其从ChatRoom中退出,然后加入到LogoutRoom中去,然后调用默认的中断连接方法,终止连接。

注意,当用户接入时,不管进入那个room,命令调用过程都是一样的:
在collect_incoming_data方法中尝试调用超类CommandHandler的handle方法,handle方法将命令自动拼接,然后调用对应room中的do_xx方法。如果调用handle时返回的是EndSession异常,那么调用handle_close()方法,终止连接。

此外,调试中发现一个意外:
当LogoutRoom类中的add方法下self.server.users不幸写错为self.server.use时,asyncore会在某不知名时刻报错。似乎在asyncore某些位置会调用这个方法。然后报错的出错点在ChatSession类下的cur.remove()处,说list中无该实例。原因不明。

思考:
当用户从LoginRoom进入ChatRoom中时,原先的LoginRoom实例是否会被回收?
用户logout之后,对应的ChatSession实例是否会被回收?感觉这个是asyncore保证的,但是在pycharm中是否可以进行观察跟踪?




0 0