zoukankan      html  css  js  c++  java
  • python练习四—简单的聊天软件

    python最强大的是什么?库支持!!有了强大的库支持,一个简单的聊天软件实现就更简单了,本项目思路如下

    # 项目思路
    1. 服务器的工作
    * 初始化服务器
    * 新建一个聊天房间
    * 维护一个已链接用户的会话列表
    * 维护一个已登录用户的字典,用户名和会话
    * 监听端口,接受会话,并启动一个ChatSession处理
    
    2. 会话线程
    * 初始化一个接收数据缓冲区
    * 处理用户输入的命令,并提醒用户先登录(也就是说目前指处理登陆命令)
    * 将用户输入的数据加入缓冲区
    * 用户一次输入结束后,将缓冲区数据发送,并清空缓冲区
    
    3. 命令分发
    * 对用的输入进行分割,并将对应的命令分发到相应的处理函数,如果没有则采用默认处理函数
    
    4. 房间,继承自命令分发类,即:具有处理命令能力的房间
    
    5. LoginRoom,继承自Room,处理登陆相关的命令
    * 加入房间,有用户进入房间,欢迎该用户
    * 登录命令,登陆成功之后进入房间,将该session加入队列
    
    6. ChatRoom,继承自Room,处理发言、查看所有用户等命令
    
    7. LogoutRoom,继承自Room,处理用户登出,相当于用户输入登出命令之后进入一个房间,
    然后抛出异常通知ChatSession关闭线程

    代码如下:

    #! /usr/bin/env python
    # -*- coding=utf-8 -*-
    
    from asyncore import dispatcher
    import socket
    import asyncore
    from asynchat import async_chat
    
    # 服务器监听端口号
    PORT = 5005
    # 服务器名称
    NAME = 'QQ'
    
    class EndException(Exception):
        pass
    
    class CommandHandler:
        '''
        用来处理用户输入简单的命令
        '''
    
        def unknown(self, session, cmd):
            '''处理未知命令'''
            session.push('Unknown command %s
    ' %cmd)
    
        def handler(self, session, line):
            '''
            处理命令(命令路由),根据用户输入分析储命令,调用对应的处理程序
            '''
            if not line.strip():
                # 如果输入为空,返回,不做处理
                return
            # 切分命令
            print line + '----------'
            parts = line.split(' ', 1)
            cmd = parts[0]
            try:
                line = parts[1]
            except IndexError:
                line = ''
            method = getattr(self, 'do_' + cmd, None)
            try:
                method(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, sessioni, line):
            '用户退出房间'
            raise EndException
    
    class LoginRoom(Room):
        '''
        用户登录房间,处理用户登录房间的一些操作
        '''
    
        def add(self, session):
            '扩展父类方法,在加入房间之后,给用户发送欢迎语'
            Room.add(self, session)
            session.push('welcome to %s 
    ' % self.server.name)
    
        def unknown(self, session, cmd):
            '处理未知命令,提示用户输入登录命令'
            session.push('please log in 
     "Use login <nick>" 
    ')
    
        def do_login(self, session, line):
            '处理用户登录命令'
            name = line.strip()
            if not name:
                # 如果没有输入名称,提示用户输入
                session.push('Please input a name')
            elif name in self.server.users:
                # 如果名称已被占用
                session.push('the % is taken 
    ' %name)
                session.push('Please try again')
            else:
                # 如果正常则让用户进入房间
                session.name = name
                session.enter(self.server.main_room)
    
    
    class ChatRoom(Room):
        '''
        聊天房间,处理用户在房间中聊天的命令
        '''
    
        def add(self, session):
            '用户进入聊天房间的时候,通知其他用户'
            Room.add(self, session)
            self.broadcast('%s has enter in the room 
    ' %session.name)
            self.server.users[session.name] = session
    
        def remove(self, session):
            '提醒其他用户,xx离开房间'
            Room.remove(self, session)
            self.broadcast('%s has left the room 
    ' %session.name)
    
        def do_say(self, session, line):
            '用户发言'
            self.broadcast(session.name + ':' + line + '
    ')
    
        def do_look(self, session, line):
            '查看房间中所有用户'
            self.broadcast('the following are in the room: 
    ')
            for user in self.sessions:
                self.broadcast(user.name + '
    ')
    
    class LogoutRoom(Room):
        '''
        用户退出房间,所需要的命令
        '''
    
        def do_logout(self, session, line):
            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
    
            # 如果有新用户连接,则先要求登陆,登陆成功以后进入房间
            self.enter(LoginRoom(self.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.handler(self, line)
            except EndException:
                self.handle_close()
    
        def handle_close(self):
            async_chat.handle_close(self)
            self.enter(LogoutRoom(self))
    
    class ChatServer(dispatcher):
        '''
        服务器,负责接受客户端链接并分配给具体的程序(线程处理)
        '''
    
        def __init__(self, port, name):
            '初始化服务器,初始会话列表,初始化用户字典(用户名对应用户session),初始化服务器主房间'
            dispatcher.__init__(self)
            self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
            self.set_reuse_addr()
            self.bind(('', port))
            self.listen(5)
            self.sessions = []
            self.users = {}
            self.name = name
            self.main_room = ChatRoom(self)
    
        def handle_accept(self):
            conn, addr = self.accept()
            print 'connection attemp from ', addr[0]
            ChatSession(self, conn)
    
    s = ChatServer(PORT, NAME)
    try:
        asyncore.loop()
    except KeyboardInterrupt:
        pass

    其实从第一个练习开始我就开始意识到,书中的引导是逐步的——使用的是迭代式的开发,一开始知识一个简单的例子视线了基本的功能,然后基于简单的列子将代码模块化,逐渐加功能,我也是试着这样做的,但是鉴于篇幅,其他代码就没有贴上来,在完整的代码可以下载

    再进行本练习中,有一下基础知识得到巩固:

    # 判断正在运行的模块是直接运行还是被import执行的,即:
    # 直接运行的话__name='__main__'
    # 如果是import的话就不是__main__
    if __name__ == '__main__':
    
    # 变量作用域
    data = 123
    * 在函数内部申明,作用域就是函数内部,
    * 在类内部申明就是类变量,访问的时候直接data
    * 在类内部的函数内部申明如:self.data=124,调用的时候只能是self.data,如果使用data,默认查找的是全局变量
    * 如果不适用self指明,python默认调用的全局变量
    * python变量可以重名,在同一作用于内如果已经定义过,只是进赋值
    
    # 在子类方法内部调用父类方法(如:Room父类,ChatRoom子类)
    Room.__init__()

    完整代码

    http://pan.baidu.com/s/1nu5KE4T

  • 相关阅读:
    获取字符串最长不重复子串
    斐波那契数列&&上台阶
    mysql 索引
    Python unittest框架实现appium登录
    解决RedisDesktopManager连接不上redis问题
    Nginx配置---启用gzip压缩
    Nginx配置---同一端口下部署不同项目
    Nginx配置---解决History路由报错问题
    个人网站搭建基本流程
    Create-React-App项目中CSS Modules的使用
  • 原文地址:https://www.cnblogs.com/sunshine-2015/p/5468451.html
Copyright © 2011-2022 走看看