當前位置: 首頁 > 新聞中心 > 行業新聞 > 佑凡科技:教你如何用Python開發你的第一款聊天軟件

佑凡科技:教你如何用Python開發你的第一款聊天軟件

發表日期:2020-07-09 瀏覽次數:7068

在本實驗中,我們將實現一個簡單的圖形界面聊天系統。我們可以通過圖形客戶端登錄聊天室,并與其他成員進行聊天。

本教程由實驗樓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 瀏覽次數:7068

佑凡科技 小程序在朋友…

小程序的開局似乎并不太順。這可是微信乃至騰訊的戰略級產品,承載…

日期:2017-05-27 瀏覽次數:8458

佑凡科技:全球制造業爭…

隨著特朗普減稅大招引領的新一輪減稅潮的可能到來,全球爭奪實體企…

日期:2016-12-21 瀏覽次數:5367

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

編者按:本文來自微信公眾號“將門創業”(ID:thejiangmen),36氪…

日期:2016-12-21 瀏覽次數:5298

佑凡科技:華為、百度、…

提到牛逼程序員,你是想到比爾蓋茨,還是扎克伯格,還是某某語言之…

日期:2016-12-21 瀏覽次數:5636

佑凡科技:為了撐起189…

本文頭圖來自視覺中國,未經授權請勿使用新政以來,外間對滴滴的浮…

日期:2016-12-21 瀏覽次數:5359

日本道二区高清视频_2012中文字幕一页_国产欧美国日产在线播放