虚拟茶话会

虚拟茶话会

在这个项目中,将编写一个聊天服务器,让人们能够通过网络实时地聊天

准备工作

首先,必须由一台连接到网络(如互联网)的计算机,否则别人无法连接到聊天服务器。(可在自己计算机上连接到聊天服务器,但这样做没多大意思。)要连接到聊天服务器,用户必须知道计算机地址(可以是机器名,如foo.bar.baz.com,也可以是IP地址)。另外,用户必须知道聊天服务器使用的端口号。这种端口号可在程序中设置;在代码中,使用的端口号为5005(这里是随便选择的)。
注意: 有些端口号受到限制,必须有管理员权限才能使用。一般而言,使用大于1023的端口号就不会有什么问题。

设计

代码

# -*- coding: utf-8 -*-

from asyncore import dispatcher
from asynchat import async_chat
import socket, asyncore


PORT = 5005
NAME = 'TestChat'


#Exception所有异常的基类
class EndSession(Exception): pass


class CommandHandler():
    """
    类似于标准库中的cmd.Cmd简单命令处理程序
    """

    def unknown(self, session, cmd):
        '响应未知命令'
        session.push(b'Unknown command: %s\r\n'%cmd.encode('utf-8'))

    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(b'Welcome to %s\r\n'%self.server.name.encode('utf-8'))

    def unknown(self, session, cmd):
        #所有未知命令(除了login或者logout外的一切)会导致一个警告
        session.push(b'Please log in \n Use "login <nick>"\r\n')

    def do_login(self, session, line):
        name = line.strip().encode('utf-8')
        #确保用户输入了名字
        if not name:
            session.push(b'Please enter a name\r\n')
        #确保用户名没有被使用
        elif name in self.server.users:
            session.push(b'The name "{}" is taken.\r\n'.format(name))
            session.push(b'Please 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 + b' has entered the room.\r\n')
        self.server.users[session.name] = session
        super().add(session)

    def remove(self, session):
        Room.remove(self, session)
        #告诉所有人有用户离开
        self.broadcast(session.name + b' has left the room.\r\n')

    def do_say(self, session, line):
        '处理say命令,向每个会话发送发言人名称和发言内容'
        self.broadcast(session.name+":".encode('utf-8')+line.encode('utf-8') +b"\r\n")

    def do_look(self, session, line):
        '处理look命令,该命令用于查看谁在房间内'
        session.push(b'The follwing are in this room: \r\n')
        for other in self.sessions:
            session.push(other.name + b'\r\n')

    def do_who(self, session, line):
        '处理who命令, 该命令用于查看谁登录了'
        session.push(b'The following are logged in : \r\n')
        for name in self.server.users:
            session.push(name + b'\r\n')


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):
        super().__init__(sock)
        self.server = server
        self.set_terminator(b"\r\n")
        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.decode('utf-8'))

    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):
        super().__init__()
        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(' End run')

运行

打开三个cmd,在电脑属性下找到计算机名称复制下来,在命令行中输入: telnet 机器名 端口号(5005)
三个用户就可以进行通话了


本文借鉴了《python基础教程(第3版)》,想了解更详细的内容可以自行搜索查阅

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值