小编心语:锵锵锵!各位看官注意了啊,走过路过表错过!上篇博文主要介绍了基于基于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. 跨语言和跨编译器的那些坑(CPython vs IronPython)

    代码是宝贵的,世界上最郁闷的事情,便是写好的代码,还要在另外的平台上重写一次,或是同时维护功能相同的两套代码.所以才需要跨平台. 不仅如此,比如有人会吐槽Python的原生解释器CPython跑得太慢 ...

  2. MySQL监控利器-Innotop

    Innotop是一款十分强大的MySQL监控工具,用perl所写,通过文本模式展示MysQL服务器和Innodb的运行状况. 安装innotop 下载地址:https://github.com/inn ...

  3. DDD 领域驱动设计-看我如何应对业务需求变化,领域模型调整?

    写在前面 上一篇:DDD 领域驱动设计-看我如何应对业务需求变化,愚蠢的应对? "愚蠢的应对",这个标题是我后来补充上的,博文中除了描述需求变化.愚蠢应对和一些思考,确实没有实质性 ...

  4. C# 将PDF文件转换为word格式

    Pdf(Portable Document Format)意为“便携式文档格式”,是现在最流行的文件格式之一,它有很多优点如:尺寸较小.阅读方便.操作系统平台通用等,非常适合在网络上传播和使用.如今在 ...

  5. 关于CLR、CIL、CTS、CLS、CLI、BCL和FCL 的区分与总结

    关于CLR.CIL.CTS.CLS.CLI.BCL和FCL 的区分与总结 如果要想深入学习.NET平台,那么标题中的这些关键字对你来说并不陌生,这些名词构成了.NET庞大的生态系统,为了宏观认识.NE ...

  6. objective-c 语法快速过(2)

    oc类的声明和定义的常见错误 1.只有类的声明,没有类的实现 2.漏了@end 3.@interface和@implementation嵌套,也就是@interface或者@implementatio ...

  7. struts2学习笔记--拦截器(Interceptor)和登录权限验证Demo

    理解 Interceptor拦截器类似于我们学过的过滤器,是可以在action执行前后执行的代码.是我们做web开发是经常使用的技术,比如权限控制,日志.我们也可以把多个interceptor连在一起 ...

  8. struts2学习笔记--总结获取servletAPI的几种方式

    struts2的Action放弃了request,response等ServletAPI,使得在业务层上更加独立,在有时候使用struts2进行Web开发的时候,不可避免的要在action中使用ser ...

  9. wsgiref 结构分析

    1.首先给出wsgiref的结构 上图是wsgiref的所有模块及模块间的调用关系,如下对wsgiref的模块进行简要的说明: ① simple_server 实现的是一个简单的 HTTP 服务器,并 ...

  10. gradle中使用嵌入式(embedded) tomcat, debug 启动

    在gradle项目中使用embedded tomcat. 最开始部署项目需要手动将web项目打成war包,然后手动上传到tomcat的webapp下,然后启动tomcat来部署项目.这种手动工作通常还 ...