Python简易聊天工具-基于异步Socket通信
继续学习Python中,最近看书《Python基础教程》中的虚拟茶话会项目,觉得很有意思,自己敲了一遍,受益匪浅,同时记录一下。
主要用到异步socket服务客户端和服务器模块asyncore以及异步socket命令和响应处理模块asynchat
其中asyncore模块中只有一个类dispatcher,我们继承该类去创建单会话的服务端,包括初始化socket的ip、port、ChatRoom(聊天房间)等,复写handle_accept方法调用命令和响应处理类ChatSession()。ChatSession()类继承了asynchat中的async_chat类,该类为虚拟类,继承asyncore.dispatcher,可以使用dispatcher的方法,但必须复写自己的collect_incoming_data() 和 found_terminator()方法,使其有意义。从而基于事件触发,异步处理socket通信中的响应和请求。运行时,ChatSession初始化函数中可用定义的enter方法进入LoginRoom类(用来进入房间,注册),所有的会话都开始于单独的LoginRoom中。
之后,定义了CommandHandler,Room,LoginRoom,LogoutRoom和ChatRoom类。继承关系:CommandHandler->Room->LoginRoom,LogoutRoom,ChatRoom
CommandHandler是负责具体处理相应的命令,在ChatSession.found_terminator方法中,在读取完整的命令后,调用当前room(Room,LoginRoom或ChatRoom)继承的CommandHandler中的handle方法,处理响应命令,此处用来调用Room.do_logout、LoginRoom.do_login、ChatRoom.do_say、ChatRoom.do_look、ChatRoom.do_who这些do_前缀的方法。
do_Logout方法生成EndSession异常,EndSession异常调用ChatSession.handle_close()方法,handle_close()方法使用enter方法进入LogoutRoom类,并调用LogoutRoom.add(self, session)解除users字典中的用户的绑定
代码:
#-*- coding: UTF-8 -*-
from asyncore import dispatcher
from asynchat import async_chat
import asyncore, socket
PORT = 5005
NAME = 'TestChat'
class EndSession(Exception):pass
class CommandHandler:
"""
类似于标准库中cmd.Cmd的简单命令处理程序
"""
def unknown(self, session, cmd):
"""响应未知命令"""
session.push('Unknown command: %s\r\n' % cmd)
def handle(self, session, line):
"""处理从给定的会话中接收到的行"""
if not line.strip(): return
#分离命令:
parts = line.split(' ', 1)
cmd = parts[0]
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)
def do_logout(self, session, line):
'响应logout命令'
raise EndSession
class LoginRoom(Room):
"""
为刚刚连接上的用户准备房间
"""
def add(self, session):
Room.add(self, session)
#当用户进入时, 问候
self.broadcast('Welcome to %s\r\n' % self.server.name)
def unknow(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('Pleas enter a name\r\n')
#确保用户名字没有被使用
elif name in self.server.users:
session.push('The name "%s" is taken.\r\n' % name)
session.push('Pleas try again.\r\n')
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
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')
def do_who(self, session, line):
#处理who命令,该命令用于查看谁登陆了
session.push('The following are logged in: \r\n')
for name in self.server.users:
session.push(name + '\r\n')
class LogoutRoom(Room):
"""
为单用户准备的简单房间。只用于将用户名从服务器移除
"""
def add(self, session):
#当会话(用户)进入要删除的LogoutRoom时
try: del self.server.users[session.name]
except KeyError: pass
class ChatSession(async_chat):
"""
单会话,负责和单用户通信
"""
def __init__(self, server, sock):
#标准设置任务
async_chat.__init__(self, sock)
self.server = server
self.set_terminator('\r\n')
self.data = []
self.name = None
#所有的会话都开始于单独的LoginRoom中:
self.enter(LoginRoom(server))
def enter(self, room):
#从当前房间中移除自身(self),并且将自身添加到下一个房间...
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)
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):
# Standard setup tasks
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()
#print 'Connection attempt from', addr[0]
#self.sessions.append(ChatSession(self, conn))
ChatSession(self, conn)
if __name__=="__main__":
s = ChatServer(PORT, NAME)
try: asyncore.loop()
except KeyboardInterrupt: print
测试效果:
服务启动在 telnet 119.29.207.141 5005,有兴趣可以一起玩耍交流:)
当前命令:login 名字;say 要说的话;look(查看房间有谁);who(查看谁在线,谁登陆了);loginout(注销登陆)
Python简易聊天工具-基于异步Socket通信的更多相关文章
- RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.8 版本━新增企业通(内部简易聊天工具)
RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.8 版本 新增企业通(内部简易聊天工具) RDIFramework.NET,基于.NET的快速信息化系统开发.整合框架,给用 ...
- c#之异步Socket通信
0.基于上一篇的c#之Socket(同步)通信,在几个大神评论之后,发现是有挺多地方不足的,所以写了一个改进版本的基于c#的异步Socket通信.再加深一下对Socket的使用和理解.其中客户端和服务 ...
- JAVA基础知识之网络编程——-基于AIO的异步Socket通信
异步IO 下面摘子李刚的<疯狂JAVA讲义> 按照POSIX标准来划分IO,分为同步IO和异步IO.对于IO操作分为两步,1)程序发出IO请求. 2)完成实际的IO操作. 阻塞IO和非阻塞 ...
- java+socket 简易聊天工具
1.服务器端程序 package com.test3; import java.io.*; import java.net.*; import java.awt.*; import java.awt. ...
- Python 简易聊天机器人
聊天机器人 | |-----MySql | |---module--"逻辑运算层" | | | |---ciku--"与词库交互" | | | |---dict ...
- 使用 boost.asio 简单实现 异步Socket 通信
客户端: class IPCClient { public: IPCClient(); ~IPCClient(); bool run(); private: bool connect(); bool ...
- c# TcpClient简易聊天工具
说明: TcpClient 链接是一个比较安全稳定的链接,作为聊天或者是数据稳定传输,是比较合适的,下面的代码测试过,如果你吧他放在公网服务器上,也是可以用的 using System; using ...
- 05-python 学习第五天,简易聊天工具(shelve模块练习)
需求:1.有两个用户,一个是mychat.py,另一个是youchat.py2.通过执自己的文件,可以看到对方输入的内容,实现连个命令行窗口之间聊天的功能.3.通过shelve 持久化写入和和读取功能 ...
- 使用PHP+Swoole实现的网页即时聊天工具:PHPWebIM
使用PHP+Swoole实现的网页即时聊天工具 全异步非阻塞Server,可以同时支持数百万TCP连接在线 同时支持websocket+comet2种兼容协议,可用于所有种类的浏览器包括IE 拥有完整 ...
随机推荐
- MongoDB副本集学习(三):性能和优化相关
Read Preferences/读写分离 有时候为了考虑应用程序的性能或响应性,为了提高读取操作的吞吐率,一个常见的措施就是进行读写分离,MongoDB副本集对读写分离的支持是通过Read Pref ...
- 北京程序员 VS 硅谷程序员(转)
夫妻双码农,北京 or 硅谷,值得吗? http://mp.weixin.qq.com/s?__biz=MzA5MzE4MjgyMw==&mid=401228574&idx=1& ...
- C语言中可变参数函数实现原理
C函数调用的栈结构 可变参数函数的实现与函数调用的栈结构密切相关,正常情况下C的函数参数入栈规则为__stdcall, 它是从右到左的,即函数中的最右边的参数最先入栈.例如,对于函数: void fu ...
- webpack 教程 那些事儿05-多页应用
本篇主要关于如何用webpack构建多页应用 为什么要构建多页应用呢?因为我的项目本来就是多页应用啊至于为什么要用webpack?因为我要用vue啊,嫌gulp 每次打包慢 啊 文章目录 1. 利用v ...
- 实现Android的不同精度的定位(基于网络和GPS)
解决方案: 实现Android的不同精度的定位(基于网络和GPS) Android中的定位服务的相关类基本上都在android.location包中,其中位置服务管理器(LocationManager ...
- JVM内存模型和关键参数设置
一. JVM内存模型: Jvm内存模型是学好Java很重要的一部分,该部分学习能让我们在系统运维的时候,或者优化服务器的时候能够有方法,懂原理. 二. Jvm关键参数: 1. 堆大小设置参数: -Xm ...
- Effective Java 读书笔记之八 异常
一.只针对异常的情况才使用异常 1.类具有状态相关的方法时,可采用状态测试方法和可识别的返回值两个策略. 二.对可恢复的情况使用受检异常,对编程错误使用运行时异常 1.期望调用者能够适当恢复的情况,应 ...
- 学号160809224姓名黄家帅c语言程序设计实验2 选择结构程序设计
实验2-1 输入3个数,并按由大到小的顺序输出. 实验要求: 编写一个C程序,输入3个数,并按由大到小的顺序输出. 源码: #include <stdio.h>void main(){ i ...
- 顺序栈的c++实现及利用其实现括号的匹配
#include<iostream>#include<cassert>#include<cstring>#include<string>using na ...
- 百度地图api 常用demo
功能一:获取map地图窗口的可视区域: var map = new BMap.Map("allmap"); // 创建Map实例 map.centerAndZ ...