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(注销登陆)

  • 相关阅读:
    angularjs 默认选中ng-repeat的一个
    AngularJs中directive的延迟加载
    AngularJS的angucomplete-alt
    Bootstrap Multiselect中文
    input[file]标签的accept=”image/*”属性响应很慢的解决办法
    全局安装cnpm
    实现鼠标悬浮内容自动撑开的过渡动画
    table表格 css样式
    IntelliJ idea 的破解
    浏览器使用谷歌搜索
  • 原文地址:https://www.cnblogs.com/yuanzhaoyi/p/5956885.html
Copyright © 2011-2022 走看看