zoukankan      html  css  js  c++  java
  • Python简易聊天工具-基于异步Socket通信

    继续学习Python中,最近看书《Python基础教程》中的虚拟茶话会项目,觉得很有意思,自己敲了一遍,受益匪浅,同时记录一下。

    主要用到异步socket服务客户端和服务器模块asyncore以及异步socket命令和响应处理模块asynchat

    其中asyncore模块中只有一个类dispatcher,我们继承该类去创建单会话的服务端,包括初始化socket的ip、port、ChatRoom(聊天房间)等,复写handle_accept方法调用命令和响应处理类ChatSession()。ChatSession()类继承了asynchat中的async_chat类,该类为虚拟类,继承asyncore.dispatcher,可以使用dispatcher的方法,但必须复写自己的collect_incoming_data()found_terminator()方法,使其有意义。从而基于事件触发,异步处理socket通信中的响应和请求。运行时,ChatSession初始化函数中可用定义的enter方法进入LoginRoom类(用来进入房间,注册),所有的会话都开始于单独的LoginRoom中。

    之后,定义了CommandHandler,Room,LoginRoom,LogoutRoom和ChatRoom类。继承关系:CommandHandler->Room->LoginRoom,LogoutRoom,ChatRoom

    CommandHandler是负责具体处理相应的命令,在ChatSession.found_terminator方法中,在读取完整的命令后,调用当前room(Room,LoginRoom或ChatRoom)继承的CommandHandler中的handle方法,处理响应命令,此处用来调用Room.do_logout、LoginRoom.do_login、ChatRoom.do_say、ChatRoom.do_look、ChatRoom.do_who这些do_前缀的方法。

    do_Logout方法生成EndSession异常,EndSession异常调用ChatSession.handle_close()方法,handle_close()方法使用enter方法进入LogoutRoom类,并调用LogoutRoom.add(self, session)解除users字典中的用户的绑定

    代码:

    #-*- coding: UTF-8 -*-
    from asyncore import dispatcher
    from asynchat import async_chat
    import asyncore, socket

    PORT = 5005
    NAME = 'TestChat'

    class EndSession(Exception):pass

    class CommandHandler:
        """
        类似于标准库中cmd.Cmd的简单命令处理程序
        """
        def unknown(self, session, cmd):
            """响应未知命令"""
            session.push('Unknown command: %s ' % 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):
            '响应logout命令'
            raise EndSession
        
    class LoginRoom(Room):
        """
        为刚刚连接上的用户准备房间
        """
        
        def add(self, session):
            Room.add(self, session)
            #当用户进入时, 问候
            self.broadcast('Welcome to %s ' % self.server.name)
        
        def unknow(self, session, cmd):
            #所有未知命令(除了login或者logout外的一切)
            #会导致一个告警
            session.push('Please log in Use "login<nick>" ')
            
        def do_login(self, session, line):
            name = line.strip()
            #确保用户输入了名字
            if not name:
                session.push('Pleas enter a name ')
            #确保用户名字没有被使用
            elif name in self.server.users:
                session.push('The name "%s" is taken. ' % name)
                session.push('Pleas try again. ')
            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 ')
            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 ')
        
        def do_say(self, session, line):
            self.broadcast(session.name + ':' + line + ' ')
            
        def do_look(self, session, line):
            #处理look命令,该命令用于查看谁在房间内
            session.push('The following are in this room: ')
            for other in self.sessions:
                session.push(other.name + ' ')
        
        def do_who(self, session, line):
            #处理who命令,该命令用于查看谁登陆了
            session.push('The following are logged in: ')
            for name in self.server.users:
                session.push(name + ' ')
                
    class LogoutRoom(Room):
        """
        为单用户准备的简单房间。只用于将用户名从服务器移除
        """
        
        def add(self, session):
            #当会话(用户)进入要删除的LogoutRoom时
            try: del self.server.users[session.name]
            except KeyError: pass
                
    class ChatSession(async_chat):
        """
        单会话,负责和单用户通信
        """
        def __init__(self, server, sock):
            #标准设置任务
            async_chat.__init__(self, sock)
            self.server = server
            self.set_terminator(' ')
            self.data = []
            self.name = None
            
            #所有的会话都开始于单独的LoginRoom中:
            self.enter(LoginRoom(server))
            
        def enter(self, room):
            #从当前房间中移除自身(self),并且将自身添加到下一个房间...
            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):
            # Standard setup tasks
            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()
            #print 'Connection attempt from', addr[0]
            #self.sessions.append(ChatSession(self, conn))
            ChatSession(self, conn)

    if __name__=="__main__":
        s = ChatServer(PORT, NAME)
        try: asyncore.loop()
        except KeyboardInterrupt: print

    测试效果:

    服务启动在 telnet 119.29.207.141 5005,有兴趣可以一起玩耍交流:)

    当前命令:login 名字;say 要说的话;look(查看房间有谁);who(查看谁在线,谁登陆了);loginout(注销登陆)

  • 相关阅读:
    【C++】资源管理
    【Shell脚本】逐行处理文本文件
    【算法题】rand5()产生rand7()
    【Shell脚本】字符串处理
    Apple iOS产品硬件参数. 不及格的程序员
    与iPhone的差距! 不及格的程序员
    iPhone游戏 Mr.Karoshi"过劳死"通关. 不及格的程序员
    XCode V4 发布了, 苹果的却是个变态. 不及格的程序员
    何时readonly 字段不是 readonly 的?结果出呼你想象!!! 不及格的程序员
    object file format unrecognized, invalid, or unsuitable Command 不及格的程序员
  • 原文地址:https://www.cnblogs.com/yuanzhaoyi/p/5956885.html
Copyright © 2011-2022 走看看