当前位置: 首页 > 新闻中心 > 行业新闻 > 佑凡科技:教你如何用Python开发你的第一款聊天软件

佑凡科技:教你如何用Python开发你的第一款聊天软件

发表日期:2020-07-09 浏览次数:5802

在本实验中,我们将实现一个简单的图形界面聊天系统。我们可以通过图形客户端登录聊天室,并与其他成员进行聊天。

本教程由实验楼120发布在实验楼,完整教程、代码及在线练习地址:Python 实现文字聊天室(更多项目请查看Python学习路径)

一、实验介绍

1、知识点

  • asyncore 、asynchat模块使用

  • wxPython 图形开发

2、实验环境

  • python3.5

二、原理解析

由于 Python 是一门带 GIL 的语言,所以在 Python 中使用多线程处理IO操作过多的任务并不是很好的选择。同时聊天服务器将同多个 socket 进行通信,所以我们可以基于 asyncore 模块实现聊天服务器。aysncore 模块是一个异步的 socket 处理器,通过使用该模块将大大简化异步编程的难度。asynchat 模块在 asyncore 模块的基础上做了进一步封装,简化了基于文本协议的通信任务的开发难度。

既然要开发聊天程序,那必然需要设计聊天时使用的协议。为了简单起见,我们将要开发的聊天服务器只支持文本协议,通过command message的方式调用相关的操作。比如如果客户端发送以下文本,将执行相应的操作

# 登录操作 login\n # 在聊天室中发表 hello 内容 say hello\n # 查看聊天室在线用户 look\n # 退出登录 logout\n

以上协议流中,login, say, look, logout就是相关协议代码。

然后使用下面的命令在/home/shiyanlou/Code目录下创建我们需要的server.py和client.py文件:

$ touch~/Code/server.py $ touch~/Code/client.py

三、服务器类

这里我们首先需要一个聊天服务器类,通过继承 asyncore 的 dispatcher 类来实现,我们编写 server.py 文件:

importasynchatimportasyncore# 定义端口PORT=6666# 定义结束异常类classEndSession(Exception):passclassChatServer(asyncore.dispatcher):""" 聊天服务器 """def__init__(self,port):asyncore.dispatcher.__init__(self)# 创建socketself.create_socket()# 设置 socket 为可重用self.set_reuse_addr()# 监听端口self.bind(('',port))self.listen(5)self.users={}self.main_room=ChatRoom(self)defhandle_accept(self):conn,addr=self.accept()ChatSession(self,conn)

这里需要补充说明的是,对于asyncore和asynchat模块来讲,在 python3.6 中,使用asyncio模块代替,但是实验环境中我们使用的是 python 3.5 ,也由于wxPython对于Linux 下CPython的支持,所以我们依然使用 python 3.5。

1、会话类

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

classChatSession(asynchat.async_chat):""" 负责和客户端通信"""def__init__(self,server,sock):asynchat.async_chat.__init__(self,sock)self.server=serverself.set_terminator(b'\n')self.data=[]self.name=Noneself.enter(LoginRoom(server))defenter(self,room):# 从当前房间移除自身,然后添加到指定房间try:cur=self.room exceptAttributeError:passelse:cur.remove(self)self.room=room room.add(self)defcollect_incoming_data(self,data):# 接收客户端的数据self.data.append(data.decode("utf-8"))deffound_terminator(self):# 当客户端的一条数据结束时的处理line=''.join(self.data)self.data=[]try:self.room.handle(self,line.encode("utf-8"))# 退出聊天室的处理exceptEndSession:self.handle_close()defhandle_close(self):# 当 session 关闭时,将进入 LogoutRoomasynchat.async_chat.handle_close(self)self.enter(LogoutRoom(self.server))

2、协议命令解释器
在之前的分析中,我们设计了聊天服务器的协议,我们需要实现协议命令的相应方法,具体来说就是处理用户登录,退出,发消息,查询在线用户的代码。在server.py文件中定义,

classCommandHandler:""" 命令处理类 """defunknown(self,session,cmd):# 响应未知命令# 通过 aynchat.async_chat.push 方法发送消息session.push(('Unknown command {} \n'.format(cmd)).encode("utf-8"))defhandle(self,session,line):line=line.decode()# 命令处理ifnotline.strip():returnparts=line.split(' ',1)cmd=parts[0]try:line=parts[1].strip()exceptIndexError:line=''# 通过协议代码执行相应的方法method=getattr(self,'do_'+cmd,None)try:method(session,line)exceptTypeError:self.unknown(session,cmd)

3、房间

接下来就需要实现聊天室的房间了,这里我们定义了三种房间,分别是用户刚登录时的房间、聊天的房间和退出登录的房间,这三种房间都继承自 CommandHandler,在server.py文件中定义,代码如下:

classRoom(CommandHandler):""" 包含多个用户的环境,负责基本的命令处理和广播 """def__init__(self,server):self.server=server self.sessions=[]defadd(self,session):# 一个用户进入房间self.sessions.append(session)defremove(self,session):# 一个用户离开房间self.sessions.remove(session)defbroadcast(self,line):# 向所有的用户发送指定消息# 使用 asynchat.asyn_chat.push 方法发送数据forsessioninself.sessions:session.push(line)defdo_logout(self,session,line):# 退出房间raiseEndSessionclassLoginRoom(Room):""" 处理登录用户 """defadd(self,session):# 用户连接成功的回应Room.add(self,session)# 使用 asynchat.asyn_chat.push 方法发送数据session.push(b'Connect Success')defdo_login(self,session,line):# 用户登录逻辑name=line.strip()# 获取用户名称ifnotname:session.push(b'UserName Empty')# 检查是否有同名用户elifnameinself.server.users:session.push(b'UserName Exist')# 用户名检查成功后,进入主聊天室else:session.name=name session.enter(self.server.main_room)classLogoutRoom(Room):""" 处理退出用户 """defadd(self,session):# 从服务器中移除try:delself.server.users[session.name]exceptKeyError:passclassChatRoom(Room):""" 聊天用的房间 """defadd(self,session):# 广播新用户进入session.push(b'Login Success')self.broadcast((session.name+' has entered the room.\n').encode("utf-8"))self.server.users[session.name]=session Room.add(self,session)defremove(self,session):# 广播用户离开Room.remove(self,session)self.broadcast((session.name+' has left the room.\n').encode("utf-8"))defdo_say(self,session,line):# 客户端发送消息self.broadcast((session.name+': '+line+'\n').encode("utf-8"))defdo_look(self,session,line):# 查看在线用户session.push(b'Online Users:\n')forotherinself.sessions:session.push((other.name+'\n').encode("utf-8"))if__name__=='__main__':s=ChatServer(PORT)try:print("chat serve run at '0.0.0.0:{0}'".format(PORT))asyncore.loop()exceptKeyboardInterrupt:print("chat server exit")

四、登陆窗口

完成了服务器端后,就需要实现客户端了。客户端将基于 wxPython 模块实现。 wxPython 模块是 wxWidgets GUI 工具的 Python 绑定。所以通过 wxPython 模块我们就可以实现 GUI 编程了。同时我们的聊天协议基于文本,所以我们和服务器之间的通信将基于 telnetlib 模块实现。

登录窗口通过继承 wx.Frame 类来实现,编写client.py文件,代码如下:

import wx import telnetlib from time import sleep import _thread as threadclassLoginFrame(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()deflogin(self,event):# 登录处理try:serverAddress=self.serverAddress.GetLineText(0).split(':')con.open(serverAddress[0],port=int(serverAddress[1]),timeout=10)response=con.read_some()ifresponse!=b'Connect Success':self.showDialog('Error','Connect Fail!',(200,100))returncon.write(('login '+str(self.userName.GetLineText(0))+'\n').encode("utf-8"))response=con.read_some()ifresponse==b'UserName Empty':self.showDialog('Error','UserName Empty!',(200,100))elif response==b'UserName Exist':self.showDialog('Error','UserName Exist!',(200,100))else:self.Close()ChatFrame(None,2,title='ShiYanLou Chat Client',size=(500,400))exceptException:self.showDialog('Error','Connect Fail!',(95,20))defshowDialog(self,title,content,size):# 显示错误信息对话框dialog=wx.Dialog(self,title=title,size=size)dialog.Center()wx.StaticText(dialog,label=content)dialog.ShowModal()

1、聊天窗口
聊天窗口中最主要的就是向服务器发消息并接受服务器的消息,这里通过子线程来接收消息,在client.py文件中定义,代码如下:

classChatFrame(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)# Users按钮绑定获取在线用户数量方法self.usersButton.Bind(wx.EVT_BUTTON,self.lookUsers)# 关闭按钮绑定关闭方法self.closeButton.Bind(wx.EVT_BUTTON,self.close)thread.start_new_thread(self.receive,())self.Show()defsend(self,event):# 发送消息message=str(self.message.GetLineText(0)).strip()ifmessage!='':con.write(('say '+message+'\n').encode("utf-8"))self.message.Clear()deflookUsers(self,event):# 查看当前在线用户con.write(b'look\n')defclose(self,event):# 关闭窗口con.write(b'logout\n')con.close()self.Close()defreceive(self):# 接受服务器的消息whileTrue:sleep(0.6)result=con.read_very_eager()ifresult!='':self.chatFrame.AppendText(result)if__name__=='__main__':app=ICANN Verification Required()con=telnetlib.Telnet()LoginFrame(None,-1,title="Login",size=(320,250))app.MainLoop()

五、执行

  • 首先,我们执行server.py,如下图所示:
  • 这时,我们再打开一个终端,执行client.py文件,如下图:
  • 输入对应的信息之后,点击Login,再次重复上一步骤,使用另一用户名shiyanlou002登陆,如下图:
  • 在最终的示例中,我们可以分别通过shiyanlou001和shiyanlou002的客户端发送消息,此时,所有的在线用户都可以收到对应的消息。

六、小结

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


作者:实验楼
链接:https://www.jianshu.com/p/ad5231ef8ba8
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
如没特殊注明,文章均为佑凡科技原创,转载请注明来自http://www.u-van.cn/news/530.html
相关新闻

佑凡科技:教你如何用P…

在本实验中,我们将实现一个简单的图形界面聊天系统。我们可以通过…

日期:2020-07-09 浏览次数:5802

佑凡科技 小程序在朋友…

小程序的开局似乎并不太顺。这可是微信乃至腾讯的战略级产品,承载…

日期:2017-05-27 浏览次数:7167

佑凡科技:全球制造业争…

随着特朗普减税大招引领的新一轮减税潮的可能到来,全球争夺实体企…

日期:2016-12-21 浏览次数:4909

佑凡科技:沈强:人工智…

编者按:本文来自微信公众号“将门创业”(ID:thejiangmen),36氪…

日期:2016-12-21 浏览次数:4785

佑凡科技:华为、百度、…

提到牛逼程序员,你是想到比尔盖茨,还是扎克伯格,还是某某语言之…

日期:2016-12-21 浏览次数:5200

佑凡科技:为了撑起189…

本文头图来自视觉中国,未经授权请勿使用新政以来,外间对滴滴的浮…

日期:2016-12-21 浏览次数:4866