目錄
基於websocket的網頁實時消息推送與在線聊天(上篇)
“使用dwebsocket在django中實現websocket”
websocket原理圖
django框架並沒有自帶websocket通信,所以一般由channels或dwebsocket來實現
一.使用dwebsocket來完成實時消息推送
pip install dwebsocket
服務端
客戶端
代碼如下
views.py文件中
from django.shortcuts import render,HttpResponse #引入HttpResponse
from dwebsocket.decorators import accept_websocket #引入dwbsocket的accept_websocket裝飾器
def to_sendmsg(request):
return render(request,'sendmsg.html')
def to_recmsg(request):
return render(request,'recmsg.html')
clients={} #創建客戶端列表,存儲所有在線客戶端
# 允許接受ws請求
@accept_websocket
def link(request):
# 判斷是不是ws請求
if request.is_websocket():
userid=str(uuid.uuid1())
# 判斷是否有客戶端發來消息,若有則進行處理,若發來“test”表示客戶端與服務器建立鏈接成功
while True:
message=request.websocket.wait()
if not message:
break
else:
print("客戶端鏈接成功:"+str(message, encoding = "utf-8"))
#保存客戶端的ws對象,以便給客戶端發送消息,每個客戶端分配一個唯一標識
clients[userid]=request.websocket
def send(request):
# 獲取消息
msg=request.POST.get("msg")
# 獲取到當前所有在線客戶端,即clients
# 遍歷給所有客戶端推送消息
for client in clients:
clients[client].send(msg.encode('utf-8'))
return HttpResponse({"msg":"success"})
urls.py
from django.contrib import admin
from django.urls import path
import app.views as views
urlpatterns = [
path('admin/', admin.site.urls),
path('link/', views.link),
path('send/', views.send),
path('to_sendmsg/', views.to_recmsg),
path('to_recmsg/', views.to_sendmsg),
]
recmsg.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>實時消息接收</title>
</head>
<body>
<script>
window.onload = function () {
//先判斷瀏覽器是否支持websocket
if ("WebSocket" in window) {
alert("您的瀏覽器支持 WebSocket!");
// 打開一個 web socket
ws = new WebSocket("ws://127.0.0.1:8000/link/");
ws.onopen = function () {
// Web Socket 已連接上,使用 send() 方法嘗試發送數據
ws.send("test");
};
//監聽服務端是否有消息發送過來,當有消息時執行方法
ws.onmessage = function (evt) {
//獲取服務器發來的消息
var received_msg = evt.data;
//顯示消息
alert("收到消息:"+received_msg)
};
//關閉頁面或其他行為導致與服務器斷開鏈接是執行
ws.onclose = function () {
// 關閉 websocket
alert("連接已關閉...");
};
} else {
// 瀏覽器不支持 WebSocket
alert("您的瀏覽器不支持 WebSocket!");
}
}
</script>
</body>
</html>
sendmsg.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
</head>
<body>
<input id="msg" placeholder="輸入要推送的消息">
<button onclick="javascript:sendmsg()">推送</button>
<script>
function sendmsg() {
$.post("/send/",{msg:$("#msg").val()},function (result) {
if(result.msg=="success"){
alert("消息推送成功")
}
})
}
</script>
</body>
</html>
二.使用dwebsocket來完成聊天室
界面設計
實現的邏輯順序和思路:
1.加載頁面的同時連接服務器
2.服務器監聽客戶端鏈接,當有客戶端鏈接時,將其添加到在線列表,將在線列表和為其分配的客戶端隨機id發送給當前鏈接的客戶端,並將列表發送給所有在線客戶端
3.獲取服務端返回的隨機客戶端id和在線用戶列表
4.加載用戶列表和本人id
5.填寫消息,選擇群組或者指定用戶,發送到后台
6.后台服務器判斷是群組消息還是私聊消息,根據不同的情況發送給指定客戶端
7.指定客戶端接收到消息,顯示在消息列表中
8.離線時,服務器刪除指定用戶,更新在線列表,並將列表發送給所有在線客戶端
代碼實現
views.py
#聊天界面
def to_chat(request):
return render(request,'chat.html')
# 服務器方法,允許接受ws請求
@accept_websocket
def chat(request):
# 判斷是不是ws請求
if request.is_websocket():
# 保存客戶端的ws對象,以便給客戶端發送消息,每個客戶端分配一個唯一標識
userid=str(uuid.uuid1())[:8]
clients[userid] = request.websocket
# 判斷是否有客戶端發來消息,若有則進行處理,表示客戶端與服務器建立鏈接成功
while True:
'''獲取消息,線程會阻塞,
他會等待客戶端發來下一條消息,直到關閉后才會返回,當關閉時返回None'''
message=request.websocket.wait()
if not message:
break
else:
msg=str(message, encoding = "utf-8")
print(msg)
#1、發來test表示鏈接成功
if msg == "test":
print("客戶端鏈接成功:"+userid)
#第一次進入,返回在線列表和他的id
request.websocket.send(json.dumps({"type":0,"userlist":list(clients.keys()),"userid":userid}).encode("'utf-8'"))
#更新所有人的userlist
for client in clients:
clients[client].send(json.dumps({"type":0,"userlist":list(clients.keys()),"user":None}).encode("'utf-8'"))
#客戶端關閉后從列表刪除
if userid in clients:
del clients[userid]
print(userid + "離線")
# 更新所有人的userlist
for client in clients:
clients[client].send(
json.dumps({"type": 0, "userlist": list(clients.keys()), "user": None}).encode("'utf-8'"))
#消息發送方法
def msg_send(request):
msg = request.POST.get("txt")
useridto = request.POST.get("userto")
useridfrom = request.POST.get("userfrom")
type=request.POST.get("type")
#發來{type:"2",msg:data,user:user},表示發送聊天信息,user為空表示群組消息,不為空表示要發送至的用戶
if type == "1":
#群發
for client in clients:
clients[client].send(json.dumps({"type": 1, "data": {"msg": msg, "user": useridfrom}}).encode('utf-8'))
else:
# 私聊,對方顯示
clients[useridto].send(json.dumps({"type": 1, "data": {"msg": msg, "user": useridfrom}}).encode('utf-8'))
# 私聊,自己顯示
clients[useridfrom].send(json.dumps({"type": 1, "data": {"msg": msg, "user": useridfrom}}).encode('utf-8'))
return HttpResponse(json.dumps({"msg":"success"}))
urls.py
# 聊天
path('to_chat/', views.to_chat),
path('chat/', views.chat),
path('msg_send/', views.msg_send),
chat.html
<!DOCTYPE html>
<html lang="zh">
<head>
<link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.css" rel="stylesheet">
<link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<meta charset="UTF-8">
<title>Title</title>
<style>
td {
border: 1px #000 solid;
margin: 0;
}
textarea {
width: 100%;
height: 100%;
}
</style>
</head>
<body style="padding: 30px">
<span id="userid">我的隨機賬號:</span>
<table>
<tr>
<td style="width: 500px;">
<div id="historymsg" style="height: 400px;overflow: auto"></div>
</td>
<td style="width: 400px">
<div id="userlist" style="height: 400px;overflow: auto"></div>
</td>
</tr>
<tr>
<td colspan="2">
<textarea id="msg"></textarea>
</td>
</tr>
<tr>
<td colspan="2">
<select class="form-control" id="isgroup">
</select>
<input class="btn btn-info btn-block" type="button" onclick="send()" value="發送">
</td>
</tr>
</table>
<!--腳本開始-->
<script>
var ws,myid;
window.onload = function () {
//先判斷瀏覽器是否支持websocket
if ("WebSocket" in window) {
// 打開一個 web socket,鏈接服務器
ws = new WebSocket("ws://127.0.0.1:8000/chat/");
ws.onopen = function () {
// Web Socket 已連接上,使用 send() 方法嘗試發送數據
ws.send("test");
};
//監聽服務端是否有消息發送過來,當有消息時執行方法
ws.onmessage = function (evt) {
//獲取服務器發來的消息
var received_msg = evt.data;
//判斷是返回的是消息還是用戶列表和id,1是消息,0是用戶列表和id
msg = eval("(" + received_msg + ")")
//用戶列表和id
if (msg.type == 0) {
//userid為空表示更新用戶列表,不需要更新自己的id,否則為初次登錄
if (msg.userid != null) {
myid = msg.userid
$("#userid").append(myid)
}
//當收到新的用戶列表時,清空原來的用戶列表,清空原來的用戶選擇框,添加群組發送選項
$("#userlist").empty()
$("#isgroup").empty()
$("#isgroup").append("<option value='1'>群發(或選擇要私聊的用戶)</option>")
for (var i = 0; i < msg.userlist.length; i++) {
//填充用戶列表
$("#userlist").append(msg.userlist[i] + "<hr />")
//填充用戶選擇框
$("#isgroup").append("<option value='" + msg.userlist[i] + "'>" + msg.userlist[i] + "</option>")
}
}
//用戶發送的消息
else {
var myDate = new Date();
nowtime = myDate.toLocaleString(); //獲取日期與時間
newmsg = ""
//判斷是自己的消息,綠色顯示
if (myid == msg.data.user) {
newmsg = "<span style='color:blue'>" + msg.data.user + ":" + nowtime + "<br />" + msg.data.msg + "</span>" + "<br />"
} else {
newmsg = "<span >" + msg.data.user + ":" + nowtime + "<br />" + msg.data.msg + "</span>" + "<br />"
}
$("#historymsg").append(
newmsg
)
}
};
//關閉頁面或其他行為導致與服務器斷開鏈接是執行
ws.onclose = function () {
// 關閉 websocket
alert("連接已關閉...");
};
} else {
// 瀏覽器不支持 WebSocket
alert("您的瀏覽器不支持 WebSocket!");
}
}
//消息發送
function send() {
msgtxt = $("#msg").val()
msguser = $("#isgroup").val()
//判斷是否是群發0是,不是0 則是私聊
if ($("#isgroup").val() == "1") {
msg = {
type: "1",
txt: msgtxt,
userfrom: myid
}
} else {
msg = {
type: "0",
txt: msgtxt,
userto: msguser,
userfrom: myid
}
}
//發送消息后清空消息框,並定位到消息框內
$.post("/msg_send/", msg, function () {
$("#msg").val("")
$("#msg").focus()
})
}
</script>
<!--腳本結束-->
</body>
</html>
群發效果
私聊效果