您的位置:首页 > 编程语言 > Python开发

python3基础教程 项目5:虚拟茶话会 详解

2018-08-12 17:44 513 查看

一、模块介绍

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()

 

 

 

 

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: