python练习四—简单的聊天软件

本文介绍了一个基于Python的简单聊天室程序的设计与实现过程。通过利用asyncore和asynchat库,实现了服务器端与客户端的交互,包括用户登录、聊天、查看在线用户等功能。文章详细解释了各组件的作用及如何通过命令分发来处理不同的用户指令。
摘要由CSDN通过智能技术生成

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\r\n' %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 \r\n' % self.server.name)

    def unknown(self, session, cmd):
        '处理未知命令,提示用户输入登录命令'
        session.push('please log in \r "Use login <nick>" \r\n')

    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 \r\n' %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 \r\n' %session.name)
        self.server.users[session.name] = session

    def remove(self, session):
        '提醒其他用户,xx离开房间'
        Room.remove(self, session)
        self.broadcast('%s has left the room \r\n' %session.name)

    def do_say(self, session, line):
        '用户发言'
        self.broadcast(session.name + ':' + line + '\r\n')

    def do_look(self, session, line):
        '查看房间中所有用户'
        self.broadcast('the following are in the room: \r\n')
        for user in self.sessions:
            self.broadcast(user.name + '\r\n')

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('\r\n')
        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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值