Python练习2-基本聊天程序-虚拟茶会话

这篇博客介绍了如何使用Python编写一个基本的聊天程序,包括登录、退出、发言、查看房间人员等功能。通过CommandHandler类处理命令,Room类管理房间和用户,以及ChatSession和ChatServer类构建服务器。代码适用于Python 3.6.0。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基本聊天程序

先来个基本的测试例子:

Main.py

from asyncore import dispatcher
import socket,asyncore
PORT = 11223
class ChatServer(dispatcher):
    def __init__(self, port):
        dispatcher.__init__(self)
        self.create_socket(socket.AF_INET ,socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind(('',port))
        self.listen(5)
    def handle_accept(self):
        conn,addr = self.accept()
        print ('Connection attempt from',addr[0])
 
if __name__ == '__main__':
    s = ChatServer(PORT)
    try:asyncore.loop()
except KeyboardInterrupt:pass


    上面是服务器段程序,接受到连接之后马上在服务器上显示对方IP,但是不保持连接。客户端测试的话很简单,可以自己写一个socket的连接,也可以为了省事直接本地开启telnet就行了,开启telnet的方法是,在开始菜单里搜索:



然后确定,之后cmd里直接telnet就行了。



运行上面的main.py,然后客户端链接服务端:

Cmd->telnet    : open 127.0.0.1 11223


    上面就是一个最基本的Python服务器脚本流程。接下来实现一个基本的聊天程序用来练手。

     实现的基本功能(虚拟茶会话) 测试环境python 3.6.0[python基础教程上的代码不能直接在3.6.0版本上跑,以下是改过的虚拟茶会话]

功能:

login name 登录房间

logout 退出房间

say   XXXX 发言

look 查看同一个房间内的人

who 查看谁登陆了当前服务器

测试截图


 

简单说下设计结构:

CommandHandler : 处理命令的,函数存在则执行,不存在则直接走unknown函数。结合着try和getattr函数python可以直接尝试去执行一个不确定存不存在的函数。[我一直在想,别的语言要怎么实现这个东西,是创建虚拟工厂?对了想起来了,干脆就创建一些函数,然后把函数名字格式化封装在一个void指针容器里的了。函数格式化要统一]


Room类:表示一个房间,里面有一些对房间人[链接]数据结构的增加删除操作,同时还有相关广播函数,用于把消息发给所有人。


LoginRoom类,里面有成功登陆房间,登陆房间失败以及登陆操作,比如问候登陆者,同时通知别人有人登陆等细节。


LogoutRoom类,登出房间类,用户登出房间时候进行的一些数据结构处理。


ChatRoom类,聊天室类,主要就是封装了一些功能函数。比如say look who等等。


ChatSessionChatServer 类基本的服务器程序需要的,分别继承async_chatdispatcher,处理一些服务器参数,以及重载设置一些处理函数等。

 

详细代码如下[注意本代码测试于python3.6.0]

#!/usr/bin/env python3
__author__ = 'tcstory'
from asyncore import dispatcher
from asynchat import async_chat
import socket, asyncore
 
PORT=5005
NAME='TestChat'
 
class EndSession(Exception): pass
 
class CommandHandler:
    '''
    Simple command handler similar to cmd.Cmd from the standard library
    '''
 
    def unknown(self, session, cmd):
        'Respond to an unknown command'
        session.push('Unkonw command: {0}\r\n'.format(cmd).encode())
 
    def handle(self, session, line):
        'Handle a received line from a given session'
        if not line.strip():
            return
        #Split off the command
        parts = line.split(' ', 1)
        cmd = parts[0]
        try:
            line=parts[1].strip()
        except IndexError:
            line=''
        #Try to find a handler
        meth=getattr(self,'do_'+cmd,None)
        try:
            #Assume it's callable
            meth(session,line)
        except TypeError:
            #If it isn't ,respond to the unknown command
            self.unknown(session,cmd)
 
class Room(CommandHandler):
    '''
    A generic environment that may contain one or more users(sessions).it takes care of basic command handling and broadcasting.
    '''
    def __init__(self,server):
        self.server=server
        self.sessions=[]
 
    def add(self,session):
        'A session(user) has entered the room'
        self.sessions.append(session)
 
    def remove(self,session):
        'A session (user) has left the room'
        self.sessions.remove(session)
 
    def broadcast(self,line):
        'Send a line to all sessions in the room'
        for session in self.sessions:
            session.push(line.encode())
 
    def do_logout(self,session,line):
        'Respond to the logout command'
        raise EndSession
 
class LoginRoom(Room):
    '''
    A room meant for a single person who has just connected
    '''
 
    def add(self,session):
        Room.add(self,session)
        #When a user enters,greet him/her
        self.broadcast('Welcome to {0}\r\n'.format(self.server.name))
 
    def unknown(self, session, cmd):
        #All unknown commands (anything except login or logout)
        #results in a prodding
        session.push('Please log in\nUse "login <nick>"\r\n'.encode())
 
    def do_login(self,session,line):
        name=line.strip()
        #Make sure the user has entered a name
        if not name:
            session.push('Please enter a name\r\n'.encode())
        #Make sure that the name isn't in use
        elif name in self.server.users:
            session.push('The name {0} is taken.\r\n'.format(name).encode())
            session.push('Please try again.\r\n'.encode())
        else:
            #The name is OK,os it is stored in the session.and
            #the user is moved into the main room
            session.name=name
            session.enter(self.server.main_room)
 
class ChatRoom(Room):
    '''
    A room meant for multiple users who can chat with the others in the room
    '''
 
    def add(self,session):
        #Notify everyone that a new user has entered
        self.broadcast('{0} has entered the room.\r\n'.format(session.name))
        self.server.users[session.name]=session
        Room.add(self,session)
 
    def remove(self,session):
        Room.remove(self,session)
        #Notify everyone that a user has left
        self.broadcast('{0} has left the room.\r\n'.format(session.name))
 
    def do_say(self,session,line):
        self.broadcast(('{0}: '+line+'\r\n').format(session.name))
 
    def do_look(self,session,line):
        'Handles the look command,used to see who is in a room'
        session.push('The following are in this room:\r\n'.encode())
        for other in self.sessions:
            session.push('{0}\r\n'.format(other.name).encode())
 
    def do_who(self,session,line):
        'Handles the who command ,used to see who is logged in'
        session.push('The following are logged in:\r\n'.encode())
        for name in self.server.users:
            session.push('{0}\r\n'.format(name).encode())
 
class LogoutRoom(Room):
    '''
    A simple room for a single user.Its sole purpose is to remove the user's name from the server
    '''
 
    def add(self,session):
        #When a session (user) enters the LogoutRoom it is deleted
 
        try:
            del self.server.users[session.name]
        except KeyError:
            pass
 
class ChatSession(async_chat):
    '''
    A single session,which takes care of the communication with a single user
    '''
 
    def __init__(self,server,sock):
        # async_chat.__init__(self,sock)
        super().__init__(sock)
        self.server=server
        self.set_terminator(b'\r\n')
        self.data=[]
        self.name=None
        #All sessions begin in a separate LoginRoom
        self.enter(LoginRoom(server))
 
    def enter(self,room):
        # Remove self from current room and add self to next 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.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):
    '''
    A chat server with a single room
    '''
 
    def __init__(self,port,name):
        super().__init__()
        # 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()
        ChatSession(self,conn)
 
if __name__=='__main__':
    s=ChatServer(PORT,NAME)
    try:
        asyncore.loop()
    except KeyboardInterrupt:
        print()


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值