python实现虚拟茶话会

这个项目目的是编写一个聊天服务器,该聊天服务器的功能有:

服务器能同时接收来自不同用户的连接

允许用户同时操作

能够解释命令,例如,say或者logout命令

服务器容易扩展

这个项目里面我们会使用到的模块式asyncore,使用asyncore框架,程序可以处理同时连接多个用户。asyncore框架基于一些底层的机制,这些机制允许服务器逐个的对于连接上的用户进行服务。在处理下一个连接前,它并不读取当前用户的所有可用数据,而只读取一部分。除此之外,服务器只从那些需要读取数据的套接字中读取。程序就这样一遍遍的循环。写入操作同理。只使用socket和select模块就可以实现,但是asyncore和asynchat提供了一个可以处理所有细节的有用框架。

首先我们需要一个客户端来测试服务器,在windows下我们可以用telnet服务端,在unix内也可以用telnet。

初次实现:

首先我们需要创建两个类:一个作为聊天服务器,一个用于表示每个聊天会话(以连接用户)

要生成基本的ChatServer类,需要继承asyncore模块中的dispatcher类,dispatcher基本上就是一个套接字对象,但是可以利用它额外的事件处理特性。

下面是可以接受连接的服务器代码

 from asyncore import dispatcher
import socket, asyncore class ChatServer(dispatcher):
def handle_accept(self):
conn, addr = self.accept()
print 'Connection attempt from ',addr[0] s = ChatServer()
s.create_socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((''.5005))
s.listen(5)
asyncore.loop()

handle_accpet方法会调用允许客户端连接的self.accept函数。她会返回一个连接和一个地址。handle_accept方法知识打印有关连接尝试的信息。

当我们运行这个服务器并且用客户端连接他的时候,我们可以再服务器的控制台上看到返回的连接信息。

ChatSession类的主要任务是为每一个连接创建一个对象,该对象负责收集来自客户端的数据并且进行响应。我们可以自己继承dispatcher并且重写一些方法来实现这个功能,但是幸运的是已经有现成的模块能够完成绝大多数的工作,asynchat

下面便是一个简单的聊天服务器

 # -*- coding:utf-8 -*-
'''
xianghang
2015.4.9
虚拟聊天室
''' from asyncore import dispatcher
from asynchat import async_chat
import socket, asyncore PORT = 5005
NAME = 'TestChat'
class ChatSessioin(async_chat):
'''
处理服务器和一个用户之间连接的类
'''
def __init__(self, server, sock):
#标准设置任务
async_chat.__init__(self, sock)
self.server = server
self.set_terminator('\r\n')
self.data = []
#问候用户
self.push('Welcome to %s\r\n' % self.server.name) def collect_incoming_data(self, data):
self.data.append(data) def found_terminator(self):
'''
如果发现了一个终止对象,也就意味着读入了一个完整的行,将广播给所有人
'''
line = ''.join(self.data)
self.data = []
self.server.broadcast(line) def handle_close(self):
async_chat.handle_close(self)
self.server.disconnect(self) class ChatServer(dispatcher):
'''
接收连接并且产生单个会话的类。他还会处理到其他会话的广播
'''
def __init__(self, port, name):
#标准设置任务
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.sessions = []
def disconnect(self, session):
self.sessions.remove(session) def broadcast(self, line):
for session in self.sessions:
session.push(line+'\r\n') def handle_accept(self):
conn, addr = self.accept()
self.sessions.append(ChatSessioin(self, conn)) if __name__ == '__main__':
s = ChatServer(PORT, NAME)
try: asyncore.loop()
except KeyboardInterrupt: print

运行这个程序后会发现,客户端虽然能够显示其它用户发送的信息,但不能识别是哪一个用户,而且整个程序没有登录登出等功能,下面我们就来扩展这些功能。

再次实现:

首先我们把服务器分为三个房间,登录房间,聊天室,登出房间,三个房间分别为三个类,这些类的父类拥有添加和删除会话 的功能。代码如下

 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

登录房间继承room父类

 class LoginRoom(Room):
'''
为刚刚连接上的用户准备的房间
'''
def add(self, session):
Room.add(self, session)
#当用户进入时,问候他
self.broadcast('Welcome to %s \r\n' % self.server.name) def unknown(self, session, cmd):
#所有未知命令(除了login或者logout除外)
#会导致一个警告
session.push('Please log in \nUse "login <nick>"\r\n') def do_login(self, session, line):
name = line.strip()
#确保用户输入了名字
if not name:
session.push('Please enter a name\r\n')
#确保用户名没有被使用
elif name in self.server.users:
session.push('The name "%s" is taken.\r\n' % name)
session.push('Please try again.\r\n')
else:
#名字没问题,所以存储在会话中
#将用户移动到主聊天室
session.name = name
session.enter(self.server.main_room)

  登录房间是让用户登录时所在的房间,在该房间内接收用户的登录命令,登陆成功后进入聊天房间。同样的,也有登出房间登出房间同理。

  聊天室房间负责处理say,look命令,say命令可以让用户在该房间内发言,并显示在其他的客户端上;look命令可以让用户查看当前聊天室所存在的用户。

 class ChatRoom(Room):
'''
为多用户相互聊天准备房间
'''
def add(self, session):
#告诉所有人有新用户进入:
self.broadcast(session.name + ' has entered the room\r\n')
self.server.users[session.name] = session
Room.add(self, 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):
'处理look命令,该命令用于查看谁在房间'
session.push('The following are in this room:\r\n')
for other in self.sessions:
session.push(other.name + '\r\n')

整个服务器程序的结构如下图:

python实现虚拟茶话会

上一篇:Mac下Intellij IDea发布JavaWeb项目 详解一 (1、新建JavaEE Project并进行相应设置 2、配置tomcat)


下一篇:JavaWeb Session详解