小编心语:锵锵锵!各位看官注意了啊,走过路过表错过!上篇博文主要介绍了基于基于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. 【实时】DevExpress内存监视

    前言 在做项目的时候,我们有时候需要检测项目的内存占用情况,有时候是检测内存泄露~,有时候是查看某段代码执行前后的内存对比,以方便找出问题并以解决. 内存泄漏也称作“存储渗漏”,用动态存储分配函数动态 ...

  2. spring笔记--使用springAPI以及自定义类 实现AOP的一个例子

    Spring的另一个重要思想是AOP,面向切面的编程,它提供了一种机制,可以在执行业务前后执行另外的代码,Servlet中的Filter就是一种AOP思想的体现,下面通过一个例子来感受一下. 假设我们 ...

  3. 使用OAuth打造webapi认证服务供自己的客户端使用

    一.什么是OAuth OAuth是一个关于授权(Authorization)的开放网络标准,目前的版本是2.0版.注意是Authorization(授权),而不是Authentication(认证). ...

  4. 自动实现接口的实例,mybatis就是这样实现的

    思考 很多人用过MyBatis,或者使用过SpringData,那么和传统的jdbc相比,差别在哪里呢?传统的dao层,接口需要有实现,需要连接发出sql,需要接收返回值,但是mybatis只需要实现 ...

  5. SpringMVC 实现POI读取Excle文件中数据导入数据库(上传)、导出数据库中数据到Excle文件中(下载)

    读取Excale表返回一个集合: package com.shiliu.game.utils; import java.io.File; import java.io.FileInputStream; ...

  6. javascript设计模式实践之模板方法--具有百叶窗切换图片效果的JQuery插件(二)

    在上一篇<javascript设计模式实践之迭代器--具有百叶窗切换图片效果的JQuery插件(一)>里,通过采用迭代器模式完成了各初始化函数的定义和调用. 接下来就要完成各个切换效果的编 ...

  7. ActiveX(一)第一个简单的Demo

    说道ActiveX,我的第一直觉就是Flash,利用ActiveX.我们可以创建丰富的可交互式应用程序.同时.利用ActiveX特性.我们可以实现Js 与 ActiveX 的无缝连接(包括数据共享.和 ...

  8. .NET编码解码(HtmlEncode与HtmlEncode)

    编码代码: System.Web.HttpUtility.HtmlEncode("<a href=\"http://hovertree.com/\">何问起& ...

  9. Genesis2000使用c#开发脚本

    这是我自学程序以来在博客园的第一篇博客,如有不好的地方请大家指正,谢谢! 这边文章的目的是给予那些在PCB使用Genesis2000程序脚本开发的人员提供.net平台下的开发方法. 目前genesis ...

  10. 【处理手记】VS2010SP1安装不上Visual Studio 2010 SP1 SDK的解决办法

    想写个VS插件,需要安装VS的SDK,VS2010SP1对应的SDK自然是Visual Studio 2010 SP1 SDK,下载页面: https://www.microsoft.com/en-u ...