小编心语:锵锵锵!各位看官注意了啊,走过路过表错过!上篇博文主要介绍了基于基于Server-Sent Event的简单在线聊天室,相信不管各位是大牛、小牛还是跟小编一样的小白,可能觉得看得不够过瘾,区区一个小小即时聊天又怎能满足大家的需求。于是小编我冥思苦想,辗转思服,白了三根头发,又去实验楼潜心钻研,埋头苦读,整理出了一篇新博文,Python聊天室,从服务器到客户端,步骤之详细令小编我的手指尖都在不停地发抖,望各路在奔跑在码农的康庄大道上,停下来,休息一会儿,喝杯茶,且看我与你细细说来Python聊天室。

嗯哼~上菜啦!!

1.简介

本次项目课是实现简单聊天室程序的服务器端和客户端。

2.知识点

服务器端涉及到asyncore、asynchat和socket这几个模块,客户端用到了telnetlib、wx、time和thread这几个模块。

二、项目实战(服务器端)

1.服务器类

首先需要一个聊天服务器,这里继承asyncore的dispatcher类来实现,代码如下

class ChatServer(dispatcher):
"""
聊天服务器
""" def __init__(self, port):
dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind(('', port))
self.listen(5)
self.users = {}
self.main_room = ChatRoom(self) def handle_accept(self):
conn, addr = self.accept()
ChatSession(self, conn)

2.会话类

有了服务器类还需要能维护每个用户的连接会话,这里继承asynchat的async_chat类来实现,代码如下:

class ChatSession(async_chat):
"""
负责和单用户通信
""" def __init__(self, server, sock):
async_chat.__init__(self, sock)
self.server = server
self.set_terminator('\n')
self.data = []
self.name = None
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) 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))

3.命令解释器

现在就需要一个命令解释器能够解释用户的命令,例如登录、查询在线用户和发消息等,代码如下:

class CommandHandler:
"""
命令处理类
""" def unknown(self, session, cmd):
'响应未知命令'
session.push('Unknown command: %s\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)

4.房间

接下来就需要实现聊天室的房间了,这里我们定义了三种房间,分别是用户刚登录时的房间、聊天的房间和退出登录的房间,这三种房间都有一个公共的父类,代码如下:

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):
'退出房间'
raise EndSession class LoginRoom(Room):
"""
刚登录的用户的房间
""" def add(self, session):
'用户连接成功的回应'
Room.add(self, session)
session.push('Connect Success') def do_login(self, session, line):
'登录命令处理'
name = line.strip()
if not name:
session.push('UserName Empty')
elif name in self.server.users:
session.push('UserName Exist')
else:
session.name = name
session.enter(self.server.main_room) class ChatRoom(Room):
"""
聊天用的房间
""" def add(self, session):
'广播新用户进入'
session.push('Login Success')
self.broadcast(session.name + ' has entered the room.\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.\n') def do_say(self, session, line):
'客户端发送消息'
self.broadcast(session.name + ': ' + line + '\n') def do_look(self, session, line):
'查看在线用户'
session.push('Online Users:\n')
for other in self.sessions:
session.push(other.name + '\n') class LogoutRoom(Room):
"""
用户退出时的房间
""" def add(self, session):
'从服务器中移除'
try:
del self.server.users[session.name]
except KeyError:
pass

5.服务器端完整代码

(略)

小编生平也最讨厌这个“略”字,不过既然各位看官已经辛苦看到了这里,小编不给点福利实在是于心不忍。

友情提示一:功力高深的各位好汉,集齐前四种代码,可自行召唤服务器端完整代码和小编香吻一枚。

友情提示二:功力不足的亲们,请登录实验楼官方网站:http://www.shiyanlou.com/courses/?course_type=project&tag=all 

,闭关练功。

(喂喂~不许扔鸡蛋!大家都是文明人!么么哒) 

乖~别闹~广告时间结束,请各位看官继续收看

三、项目实战(客户端)

完成了服务器端后,就需要实现客户端了,这里客户端连接服务器使用了telnetlib模块。

1.登录窗口

这里的图形界面包选择了wxPython,前面有安装说明,登录窗口通过继承wx.Frame类来实现,代码如下:

class LoginFrame(wx.Frame):
"""
登录窗口
""" def __init__(self, parent, id, title, size):
'初始化,添加控件并绑定事件'
wx.Frame.__init__(self, parent, id, title)
self.SetSize(size)
self.Center()
self.serverAddressLabel = wx.StaticText(self, label = "Server Address", pos = (10, 50), size = (120, 25))
self.userNameLabel = wx.StaticText(self, label = "UserName", pos = (40, 100), size = (120, 25))
self.serverAddress = wx.TextCtrl(self, pos = (120, 47), size = (150, 25))
self.userName = wx.TextCtrl(self, pos = (120, 97), size = (150, 25))
self.loginButton = wx.Button(self, label = 'Login', pos = (80, 145), size = (130, 30))
self.loginButton.Bind(wx.EVT_BUTTON, self.login)
self.Show() def login(self, event):
'登录处理'
try:
serverAddress = self.serverAddress.GetLineText(0).split(':')
con.open(serverAddress[0], port = int(serverAddress[1]), timeout = 10)
response = con.read_some()
if response != 'Connect Success':
self.showDialog('Error', 'Connect Fail!', (95, 20))
return
con.write('login ' + str(self.userName.GetLineText(0)) + '\n')
response = con.read_some()
if response == 'UserName Empty':
self.showDialog('Error', 'UserName Empty!', (135, 20))
elif response == 'UserName Exist':
self.showDialog('Error', 'UserName Exist!', (135, 20))
else:
self.Close()
ChatFrame(None, -2, title = 'ShiYanLou Chat Client', size = (500, 350))
except Exception:
self.showDialog('Error', 'Connect Fail!', (95, 20)) def showDialog(self, title, content, size):
'显示错误信息对话框'
dialog = wx.Dialog(self, title = title, size = size)
dialog.Center()
wx.StaticText(dialog, label = content)
dialog.ShowModal()

2.聊天窗口

聊天窗口中最主要的就是向服务器发消息并接受服务器的消息,这里通过子线程来接受,代码如下:

class ChatFrame(wx.Frame):
"""
聊天窗口
""" def __init__(self, parent, id, title, size):
'初始化,添加控件并绑定事件'
wx.Frame.__init__(self, parent, id, title)
self.SetSize(size)
self.Center()
self.chatFrame = wx.TextCtrl(self, pos = (5, 5), size = (490, 310), style = wx.TE_MULTILINE | wx.TE_READONLY)
self.message = wx.TextCtrl(self, pos = (5, 320), size = (300, 25))
self.sendButton = wx.Button(self, label = "Send", pos = (310, 320), size = (58, 25))
self.usersButton = wx.Button(self, label = "Users", pos = (373, 320), size = (58, 25))
self.closeButton = wx.Button(self, label = "Close", pos = (436, 320), size = (58, 25))
self.sendButton.Bind(wx.EVT_BUTTON, self.send)
self.usersButton.Bind(wx.EVT_BUTTON, self.lookUsers)
self.closeButton.Bind(wx.EVT_BUTTON, self.close)
thread.start_new_thread(self.receive, ())
self.Show() def send(self, event):
'发送消息'
message = str(self.message.GetLineText(0)).strip()
if message != '':
con.write('say ' + message + '\n')
self.message.Clear() def lookUsers(self, event):
'查看当前在线用户'
con.write('look\n') def close(self, event):
'关闭窗口'
con.write('logout\n')
con.close()
self.Close() def receive(self):
'接受服务器的消息'
while True:
sleep(0.6)
result = con.read_very_eager()
if result != '':
self.chatFrame.AppendText(result)

3.客户端完整代码

咳咳咳~小编要告诉你们一个不幸的消息,(众:下去下去!)好吧,我想你们也该知道了,客户端完整代码=1(登录窗口)+2(聊天窗口)+自我调整,若有不明白的看客,请登录实验楼官方网站:http://www.shiyanlou.com/courses/?course_type=project&tag=all

(小编我是穿着钢铁盔甲上来滴,亲们,温柔点~)

四、小结

最后就可以运行程序进行聊天了,注意需要先启动服务器再启动客户端。这个项目中使用了asyncore的dispatcher来实现服务 器,asynchat的asyn_chat来维护用户的连接会话,用wxPython来实现图形界面,用telnetlib来连接服务器,在子线程中接受 服务器发来的消息,由此一个简单的聊天室程序就完成了。

这里的图形界面使用的是wxPython,试着换一个图形界面包来实现客户端。这个程序非常简单,你也可以自己扩展想要的功能。

项目效果截图

登录窗口

聊天窗口

小编谢谢各位看官的打赏,各位看官辛苦了~么么哒

Python聊天室的更多相关文章

  1. 基于select的python聊天室程序

    python网络编程具体参考<python select网络编程详细介绍>. 在python中,select函数是一个对底层操作系统的直接访问的接口.它用来监控sockets.files和 ...

  2. python 聊天室

    server端程序 # -*- coding: utf-8 -*- #!/usr/bin/python """ """ import soc ...

  3. tornado websocket聊天室

    1.app.py #!/usr/bin/env python # -*- coding:utf-8 -*- import uuid import json import tornado.ioloop ...

  4. WebSocket请求过程分析及实现Web聊天室

    WebSocket协议是基于TCP的一种新的协议.WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符.它实现了浏览器与服务器全双工(full-duplex ...

  5. 基于tornado实现的web聊天室

    目录结构: # -*- coding:utf-8 -*- import uuid import json import tornado.ioloop import tornado.web import ...

  6. Python Socket 编程——聊天室示例程序

    上一篇 我们学习了简单的 Python TCP Socket 编程,通过分别写服务端和客户端的代码了解基本的 Python Socket 编程模型.本文再通过一个例子来加强一下对 Socket 编程的 ...

  7. python tornado websocket 多聊天室(返回消息给部分连接者)

    python tornado 构建多个聊天室, 多个聊天室之间相互独立, 实现服务器端将消息返回给相应的部分客户端! chatHome.py // 服务器端, 渲染主页 --> 聊天室建立web ...

  8. 小小聊天室 Python实现

    相对于Java方式的聊天室,Python同样可以做得到.而且可以做的更加的优雅.想必少了那么多的各种流的Python Socket,你一定会喜欢的. 至于知识点相关的内容,这里就不多说了. UDP方式 ...

  9. python 实现聊天室

    所用模块 asyncore 官方介绍, 源码 英文捉鸡点 这里  源码中可以看到其实本质上就对 select 以及 socket 的进一步封装 简单说明 Python的asyncore模块提供了以异步 ...

随机推荐

  1. Util应用程序框架公共操作类(五):异常公共操作类

    任何系统都需要处理错误,本文介绍的异常公共操作类,用于对业务上的错误进行简单支持. 对于刚刚接触.Net的新手,碰到错误的时候,一般喜欢通过返回bool值的方式指示是否执行成功. public boo ...

  2. 字符串模式匹配之KMP算法图解与 next 数组原理和实现方案

    之前说到,朴素的匹配,每趟比较,都要回溯主串的指针,费事.则 KMP 就是对朴素匹配的一种改进.正好复习一下. KMP 算法其改进思想在于: 每当一趟匹配过程中出现字符比较不相等时,不需要回溯主串的 ...

  3. 【url重写】

    一.原理void Application_BeginRequest(object sender, EventArgs e)    {  //url重写        HttpApplication a ...

  4. Mac OS X平台下QuickLook开发教程

    一.引言 Quick Look技术是Apple在Mac OS X 10.5中引入的一种用于快速查看文件内容的技术.用户只需要选中文件单击空格键即可快速查看文件内容,可以在不打开文件的情况下快速浏览内容 ...

  5. C++如何调用C#开发的dll

    序言 本文介绍一个C++如何调用C#开发的dll实例. 前言 C++编写的程序为非托管代码,C#编写的程序为托管代码.托管代码虽然提供了其他开发平台没有的许多优势,但由于前期系统及历史版本很多使用的是 ...

  6. C# ~ 从 委托事件 到 观察者模式 - Observer

    委托和事件的部分基础知识可参见 C#/.NET 基础学习 之 [委托-事件] 部分: 参考 [1]. 初识事件 到 自定义事件: [2]. 从类型不安全的委托 到 类型安全的事件: [3]. 函数指针 ...

  7. 自定义MVC框架

    我们在学习自定义MVC框架的时候常常会听到Model1 ,Model2和MVC.那么什么是Model1 什么是Model2什么又是MVC呢? 什么是Model1? Model1就是一种纯jsp开发技术 ...

  8. PetaPoco4.0的事务为什么不会回滚

    using (var srop=DbHelper.CurrentDb.GetTransaction()) { ID = bp.AddModel(model).ToStr(); #region 参与楼盘 ...

  9. jenkins中使用tfs插件做增量的版本发布部署

    一 配置介绍 使用jenkins的tfs插件进行,源码的下载,编译,打包的操作,然后使用windows的批处理命令,在局域网内(或者本机)把打包的release包,删除掉web.config,然后靠配 ...

  10. 【C#进阶系列】30 学习总结

    前面学起来还是很顺的,毕竟很多都接触过. 后面学起来只能用“磨”来形容,以至于八章用了2个月.(当然也有相当一些原因是这两个月中发生了一些个人生活上的问题) 总的来说收获超大,这种感觉就像大一的时候学 ...