python3基础教程 项目5:虚拟茶话会 详解
一、模块介绍
1、socket:套接字,python网络编程必备。可理解为一个通讯通道,连接几个主机或一台计算机上的多个进程,实现信息的相互传输。
2、asyncore:异步套接字处理程序。通过该模块可以实现处理多个同时连接的用户。
3、asynchat:在此主要提供两个方法——collect_incoming_data和found_terminator。前者在读取一些文本后调用,后者在遇到结束符时调用。
二、过程
1、首先,创建一个ChatServer()对象(服务器类对象)。然后在ChatServer()类中进行一些必要的服务器初始化,包括对dispatcher的初始化、创建套接字、bind指定特定的地址(主机名和端口)、实现地址重用、指定服务器名、指定最大监听数等。最后在服务器接受客户端连接时进入ChatSession()类。
2、ChatSession类接收客户端发来的各种信息(data),指定结束符(\r\n),并将用户送入注册房间(LoginRoom)。等待用户输入,当输入结束时,通过执行Room的handle方法实现相应的(do_XX)操作。
3、由于此时用户在LoginRoom中所以此时只有login命令有效,login完成后,用户被送入ChatRoom,可以进行say、look等操作。
4、当用户输入logout时,首先引发EndSession异常,此时调用ChatSession()中的handle_close()方法,将用户断开连接后,进入LogoutRoom。用户聊天结束。
三、如何运行
1、首先配置一下telnet,不太会的话可以百度下。(书上还说了终端模拟器什么的,这里我没有用,单纯要实现该项目用telnet就可以了。)
2、然后在你的IDE中(我用的是PyCharm)运行你的程序。
3、打开cmd,输入telnet localhost 5005,回车...
四、代码及注释
这里我还要啰嗦一句,书上的代码我敲下来后运行不出来,后来看了一下网上的代码,好像和编码有关系,于是在所有format的地方加了encode(),在data后加了decode()。需要书上源码的可去这里:https://github.com/Apress/beginning-python-3ed/blob/master/Chapter24/listing24-6.py
[code]from asyncore import dispatcher from asynchat import async_chat import socket, asyncore PORT = 5005 NAME = 'My Chat Room' class EndSession(Exception): pass class CommandHandler: #类似于标准库中cmd,Cmd的简单命令处理程序 def unknown(self, session, cmd): #响应未知命令 session.push(('Unknown command: {}\r\n'.format(cmd)).encode()) def handle(self, session, line): #处理从指定会话收到的行 if not line.strip(): return #提取命令: parts = line.split(' ', 1) cmd = parts[0] #将指令执行内容赋给line: 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.encode()) def do_logout(self, session, line): '响应命令logout' raise EndSession class LoginRoom(Room): """ 为刚连接的用户准备的聊天室 """ def add(self, session): Room.add(self, session) #用户进入时,向他/她发出问候: self.broadcast('Welcome to {}\r\n '.format(self.server.name)) def unknown(self, session, cmd): #除login和logout外的所有命令都会 #导致系统显示如下提示信息: session.push(('Please log in\nUse "login <nick>"\r\n').encode()) def do_login(self, session, line): name = line.strip() # 确保用户输入了用户名: if not name: session.push(('please enter a name:\r\n').encode()) # 确保用户名未被占用: elif name in self.server.users: session.push(('The name "{}" is taken.\r\n'.format(name)).encode()) session.push(('Please try again.\r\n').encode()) 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.\r\n') self.server.users[session.name] = session super().add(session) def remove(self, session): Room.remove(self, session) #告诉所有人有用户离开 self.broadcast(session.name + ' has left the room.\r\n') def do_say(self, session, line): self.broadcast(session.name + ': ' + line + '\r\n') def do_look(self, session, line): session.push(('The following are in this room:\r\n').encode()) for other in self.sessions: session.push((other.name + '\r\n').encode()) def do_who(self, session, line): session.push(('The following are logged in:\r\n').encode()) for name in self.server.users: session.push(((name + '\r\n').encode()).encode()) class LogoutRoom(Room): def add(self, session): try: del self.server.users[session.name] except KeyError: pass class ChatSession(async_chat): def __init__(self, server, sock): #此处的server为ChatSever对象 super().__init__(sock) self.server = server self.set_terminator(("\r\n").encode()) self.data = [] self.name = None #所有会话最初都位于LoginRoom中 self.enter(LoginRoom(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.decode()) 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()
阅读更多
- python基础教程项目五之虚拟茶话会
- Python基础教程项目(5)虚拟茶话会
- [Python-*-读书]Python基础教程--虚拟茶话会
- python基础教程总结15——5 虚拟茶话会
- python 基础教程 24章 虚拟茶话会 async2
- python 基础教程 24章 虚拟茶话会 async4
- Python基础教程之tcp socket编程详解及简单实例
- python基础教程项目四之新闻聚合
- PYTHON基础教程中的十个项目(1)
- python项目练习五:虚拟茶话会
- python基础教程项目三之万能的XML
- Python 基础教程之str和repr的详解
- Python项目五: 虚拟茶话会
- python基础教程之数字处理(math)模块详解
- python基础教程项目2:画幅好画
- lynda.com教程之Learing Python and Django零基础搭建Django项目
- python基础教程之元组操作使用详解
- Python学习入门基础教程(learning Python)--2.3.2Python函数实参详解
- python 教程_【python 基础教程详解】
- python基础教程项目二之画幅好画