程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

基於python實現的聊天室(服務端)

編輯:Python

前言

就是自己想用python做一個聊天室,然後看看socket庫,websocket庫,有點底層,然後也會用到協程的東西,不是很明白,一時間也不知道怎麼寫,然後就用了封裝好的python-socketio來實現,做好了以後,跟同學用,被吐槽說滾輪不會自動下滑到底部,難用,於是我找天找地,在網上啥也沒找到,經過自己一個多小時的摸索,終於找到了解決方法
想看看聊天室代碼的話,可以到https://github.com/cgynb/a-flask-project/tree/guiChatroom看看。或者直接訪問這個地址

http://81.70.180.118:12347/聊天呀.exe

是打包好的程序,服務端有在服務器上跑著了,可以直接使用的,但是低版本的windows運行可能會有點問題

需求

需要用python-socketio實現雙向通信
由於對協程不是很熟悉,不大會使用,所以我就先放棄使用asyncio,具體使用官方文檔寫的還是相當詳細的,我這裡就只說說這個聊天室通信的實現吧

具體內容

導入模塊

socketio就是通信使用的模塊了;
eventlet.wsgi是一個網絡庫,可以看看這篇文章,這裡就不贅述了,因為我目前也沒有能力搞得很明白;
random是一個隨機數模塊,因為是匿名聊天室,所以使用random產生隨機整數從昵稱文件裡選取一個;
socket模塊在這裡是用來產生主機ip的,因為在運行的時候eventlet.wsgi.server(eventlet.listen((‘0.0.0.0’, 5000)), app),我是沒想到,我這樣寫,它真就。。。(20380) wsgi starting up on http://0.0.0.0:5000,這樣的,至於為什麼把ip寫成’0.0.0.0’,可以看看這篇文章;
logging是處理日志的模塊,我這裡並沒有什麼高級應用,只是為了打印一下上面用socket模塊給到的主機ip罷了。。。

import socketio
import eventlet.wsgi
import random
import socket
import logging

打印主機ip

hostname = socket.gethostname()
ip = socket.gethostbyname(hostname)
logging.basicConfig()
logging.warning(' 服務器ip: ' + ip)

這樣他就會打印出主機ip了,socket和logging庫的使用就到此為止了

得到隨機昵稱

通過這裡可以看到name_list是儲存當前房間裡的人的昵稱的,可以看到name_list中元素是以(name, sid)的形式存儲起來的,sid是客戶端連接服務端的時候服務端分配給的唯一id,那麼這裡為什麼要把sid和name綁定在一起呢,在後面會有講到,這裡是為了統計房間在線用戶。random庫的使用也到此為止了

name_list = []
def create_username(sid):
with open('name.txt', 'r', encoding='utf-8') as f:
names = f.readlines()
names = list(map(lambda x: x.strip(), names))
name = names[random.randint(0, len(names)-1)]
while name in name_list:
name = names[random.randint(0, len(names) - 1)]
name_list.append((name, sid))
return name

服務器跑起來

可以看到,我甚至連官方文檔的注釋都沒有刪,因為真的是復制過來就能用。。。那翻譯一下第一行代碼就是創建一個socket.io服務器,第二行是把他包裝秤WSGI應用,那麼WSGI又是什麼呢,可以看看這篇文章
socket.io是一個事件驅動的庫,可以看到最底下有個一裝飾器@sio.on(‘msg’),如果有一點js基礎的話可以知道這是一個事件監聽函數,就是觸發了這個事件,就會觸發執行函數,具體來看,就是觸發了msg事件,函數內部的sio.emit()函數就會攜帶data向連接服務端的用戶(沒有使用房間,否則可以發送到指定房間)發送’msg’事件。
那麼上面的兩個connect還有disconnect函數呢,為什麼沒有用@sio.on(‘connect’)和@sio.on(‘disconnect’)呢?诶,這裡就要看看官方文檔了

The connect, connect_error and disconnect events have to be defined explicitly and are not invoked on a catch-all event handler.

chrome自帶的翻譯一下

和事件必須明確定義connect,connect_error並且disconnect不能在包羅萬象的事件處理程序上調用。

所以,只能使用@sio.event這個裝飾器,當發生了connect/disconnect事件時,會執行connect和disconnect函數,那我們具體看看這兩個函數
connect函數中,我們可以看到data這個字典中的username是使用上面講的create_username生成的,action表示這個客戶端操作,是登陸,而name_list在客戶端那裡就可以改變用戶列表,增加新用戶。發送data
disconnect函數中,得到斷開用戶的sid,刪除name_list中該用戶信息,然後發送data到客戶端,即可改變用戶列表,刪除退出用戶

# create a Socket.IO server
sio = socketio.Server()
# wrap with a WSGI application
app = socketio.WSGIApp(sio)
@sio.event
def connect(sid, environ, auth):
data = {
'username': create_username(sid), 'action': 'login', 'name_list': name_list}
sio.emit('msg', data)
print('connect ', sid)
@sio.event
def disconnect(sid):
for i, j in name_list:
if j == sid:
name_list.remove((i, j))
sio.emit('msg', {
'action': 'logout', 'name_list': name_list})
print(name_list)
print('disconnect ', sid)
@sio.on('msg')
def msg(sid, data):
print(data)
sio.emit('msg', data)
if __name__ == '__main__':
eventlet.wsgi.server(eventlet.listen(('', 5000)), app)

不知道各位有沒有發現,登錄,退出,發送消息,用的都是’msg’事件,這樣不會亂掉嗎,其實是不會滴,因為可以看到,不同動作,我設置的action不同,這樣客戶端就可以辨識應該做什麼(做幾次判斷action即可)。

總結

通信的內容就這麼多了,內容不多,如有問題,請多多指教


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved