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

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

允许用户同时操作

能够解释命令,例如,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实现虚拟茶话会的更多相关文章

  1. python基础教程项目五之虚拟茶话会

    python基础教程项目五之虚拟茶话会 几乎在学习.使用任何一种编程语言的时候,关于socket的练习从来都不会少,尤其是会写一些局域网的通信的东西.所以书上的这个项目刚好可以练习一下socket编程 ...

  2. 创建 Python Virtualenv 虚拟隔离环境

    video:创建 Python Virtualenv 虚拟隔离环境 python 虚拟环境 venv 简单用法 - littlemore - 博客园 创建 Python Virtualenv 虚拟隔离 ...

  3. python基础教程总结15——5 虚拟茶话会

    聊天服务器: 服务器能接受来自不同用户的多个连接: 允许用户同时(并行)操作: 能解释命令,例如,say或者logout: 容易拓展 套接字和端口: 套接字是一种使用标准UNIX文件描述符(file ...

  4. [译]Python编写虚拟解释器

    使用Python编写虚拟机解释器 一.实验说明 1. 环境登录 无需密码自动登录,系统用户名shiyanlou,密码shiyanlou 2. 环境介绍 本实验环境采用带桌面的Ubuntu Linux环 ...

  5. Python之虚拟环境管理

    Python本身有很多个版本,第三方的Python包又有很多可用的版本,所以经常会遇到下面的问题: 运行不同的Python程序,需要使用不同版本的Python(2.x或3.x). 在同一中Python ...

  6. faker之python构造虚拟数据

    python中可以使用faker来制造一些虚拟数据 首选安装faker pip install Faker 老版的叫法是faker-factory,但是已不适用 使用faker.Factory.cre ...

  7. 【python】虚拟环境管理之 virtualenv 、pipenv

    虚拟环境介绍 应用场景 python在安装第三方包时,会被pip安装到/site-package下,如果我们需要同时维护多个python项目,那这些项目都会共用一个python,而真实需求是多个项目之 ...

  8. python的虚拟运行环境

    Python 虚拟环境:Virtualenv 博客分类: Python python 在进行python开发的时候避免不同版本python或python不同版本组件之间的冲突, 有必要配置python ...

  9. python的虚拟环境管理工具venv使用方法介绍及与nodejs的包管理方式对比

    一.nodejs 包管理方式 我们知道, nodejs的包管理工具npm可以安装项目所需要的包,安装方法及区别如下: npm i module_name -g 全局安装 npm i module_na ...

随机推荐

  1. LGLSearchBar

    平时我们都是用UITextFeild 来写搜索框, 最近有时间就自己重新封装了UISearchBar, 他可以自行修改里面的属性来达到我们使用的要求. 源代码下载地址:https://github.c ...

  2. 基于java的socket编程

    #开头的废话#学习java已经半个月了,原本在抠教材里面的字眼时,觉得教材好厚,要看完不知道要到猴年马月去了.突然在网上看到一个教程,里面老师说学编程语言书不用太细看,看个大概,知道里面讲些什么就好, ...

  3. Vue表单

    gitHub地址: https://github.com/lily1010/vue_learn/tree/master/lesson11 一 vue表单 实在是太简单了,直接来个例子 <!DOC ...

  4. atitit.短信 验证码  破解  v3 p34  识别 绕过 系统方案规划----业务相关方案 手机验证码  .doc

    atitit.短信 验证码  破解  v3 p34  识别 绕过 系统方案规划----业务相关方案 手机验证码  .doc 1. 手机短信验证码 vs 图片验证码 安全性(破解成本)确实要高一些1 1 ...

  5. [ html canvas getImageData Object.data.length ] canvas绘图属性 getImageData Object.data.length 属性讲解

    <!DOCTYPE html> <html lang='zh-cn'> <head> <title>Insert you title</title ...

  6. NSOperation创建队列

    #pragma mark - NSOperation //NSOperation不能直接进行多线程的创建,需要帮助:NSOperationQueue // 使用NSOperation的第一个子类去创建 ...

  7. ARC-数据类型需要释放的情况

    // Foundation :  OC// Core Foundation : C语言// Foundation和Core Foundation框架的数据类型可以互相转换的 //NSString *s ...

  8. Mysql锁初步

    存储引擎 要了解mysql的锁,就要先从存储引擎说起. 常用存储引擎列表如下图所示: 最常使用的两种存储引擎: Myisam是Mysql的默认存储引擎.当create创建新表时,未指定新表的存储引擎时 ...

  9. Windows7下Blend for Visual Studio 2012使用问题

    目前开发的系统里很多控件样式和动画比较复杂,应该是之前同事用Blend做的,这种神器不用太浪费了,自己也准备试试. 系统环境Windows7+Visual Studio 2012 1.Windows7 ...

  10. Mac上的软件的一些对开发者有用的使用技巧(持续更新)

    内容大纲: Google浏览器设置开发者模式 正文: 1.Google浏览器设置开发者模式