Bootstrap

Django 基于dwebsocket实现简单的多人通讯

websocket是一个老生常谈的 TCP 连接全双工通讯的协议,本人闲着没事干想自己动手写一个聊天网页,看了很多的文章决定用channels来写,没想到越写坑越多,channels的依赖也很多,还要结合redis,并且部署也是坑的一批。所以就换方法了,俗话能走直路不走弯路,所以毅然的换成了dwebsocket,五个字来形容它:方便简单快!

在django项目中配置settings
INSTALLED_APPS = [
    'dwebsocket',
]

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'template')],
        'APP_DIRS': True,
    },
]

STATIC_URL = '/static/'
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)
在template里面创建luweichat.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">
<h2 style="text-align:center;">互联</h2>
<span id="userid" style="margin-left:95px;">欢迎进入互联:</span>
<table align="center">
    <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,myname;
    var userids = userids
    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;
                //判断是返回的是消息还是用户列表id1是消息,0是用户列表id
                msg = eval("(" + received_msg + ")")
                //用户列表和id
                if (msg.type == 0) {
                    //userid为空表示更新用户列表,不需要更新自己的id,否则为初次登录
                    if (msg.userid != null) {
                        myid = msg.userid
                        myname = msg.username
                        $("#userid").append(myname)
                    }

                    //当收到新的用户列表时,清空原来的用户列表,清空原来的用户选择框,添加群组发送选项
                    $("#userlist").empty()
                    $("#isgroup").empty()
                    $("#isgroup").append("<option value='1'>群发(或选择要私聊的用户)</option>")
                    for (var i = 0; i < msg.userlist.length; i++) {
                        //填充用户列表
                        $("#userlist").append(msg.userlist[i]["username"] + "<hr />")
                        //填充用户选择框
                        $("#isgroup").append("<option value='" + msg.userlist[i]["userid"] + "'>" + msg.userlist[i]["username"] + "</option>")
                    }
                }
                //用户发送的消息
                else {
                    var myDate = new Date();
                    nowtime = myDate.toLocaleString(); //获取当前的日期与时间
                    newmsg = ""
                    //判断是自己的消息,绿色显示
                    if (myid == msg.data.userid) {
                        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() //获取选择发送的人的id
        //判断是否是群发  1=, 否则是私聊
        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>
接下来就要在views里面写通讯逻辑了

模型

from django.db import models

# Create your models here.
class User_models(models.Model):
    user_id = models.CharField(max_length=255,verbose_name='userId')
    username = models.CharField(max_length=10,verbose_name='username')
    account = models.CharField(max_length=16,verbose_name='account')
    password = models.CharField(max_length=16,verbose_name='password')
    class Meta:
        db_table = 'user'
        verbose_name_plural = '用户表'

视图

from django.shortcuts import render,HttpResponse,redirect
from dwebsocket.decorators import accept_websocket
import uuid
import json
from Dweb import models

clients={} #线上用户客户端存储
clients_name={} #线上用户名存储

#登录
def Chat_login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    if request.method == 'POST':
        username = request.POST.get('userNameOrEmailAddress')
        password = request.POST.get('password')
        users = models.User_models.objects.filter(account=username).first()
        if users != None:
            if users.password == password:
                request.session['userid'] = users.user_id
                return redirect('news_chat/')
            else:
                return HttpResponse('密码错误')
        else:
            return HttpResponse('没有此用户名')

#注册
def Chat_register(request):
    if request.method == 'GET':
        return render(request, 'register.html')
    if request.method == 'POST':
        account = request.POST.get('account')
        password = request.POST.get('password')
        username = request.POST.get('username')
        userid = str(uuid.uuid1())[:10]
        user = models.User_models.objects.create(user_id=userid, account=account, 	
                                                 password=password,username=username)
        return redirect('/')



#chat
def News_chat(request):
    if request.method == 'GET':
        return render(request,'luweichat.html')

# 服务器方法,允许接受ws请求
@accept_websocket
def chat(request):
    # 判断是不是ws请求
    if request.is_websocket():
        # 保存客户端的ws对象,以便给客户端发送消息,每个客户端分配一个唯一标识
        userid = request.session.get('userid')
        clients[userid] = request.websocket
        users = models.User_models.objects.filter(user_id=userid).first()
        userdict = {"username": users.username, "userid": userid}
        clients_name[userid] = userdict
        # 判断是否有客户端发来消息,若有则进行处理,表示客户端与服务器建立链接成功
        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  0代表用户的ID
                	
                    request.websocket.send(json.dumps({"type":0,
                                                  "userlist":list(clients_name.values()),
                                             "userid":userid,
                                                  "username":users.username}).encode("'utf-8'"))
                    #将所有人的客户端更新userlist
                    print(clients)
                    for client in clients:
                        clients[client].send(json.dumps({"type":0,                                                                                "userlist":list(clients_name.values()),"user":None})
                                          .encode("'utf-8'"))
    #客户端关闭后从列表删除
    if userid in clients:
        del clients[userid]
        if userid in clients_name:
            del clients_name[userid]
        print(userid + "离线")
        # 更新所有人的userlist
        for client in clients:
            clients[client].send(
                json.dumps({"type": 0, "userlist": list(clients_name.values()),                                     "user":None}).encode("'utf-8'"))  


#消息发送方法
def msg_send(request):
    msg = request.POST.get("txt")
    useridto = request.POST.get("userto")
    useridfrom = request.POST.get("userfrom")
    print(useridfrom)
    type=request.POST.get("type")
    if type == "1":
        #群发
        for client in clients:
            clients[client].send(json.dumps({"type": 1, "data": {"msg": msg, "user": 	                       clients_name[useridfrom]["username"],"userid":useridfrom}}).encode('utf-8'))              
    else:
        # 私聊,对方显示
        clients[useridto].send(json.dumps({"type": 1, "data": {"msg": msg, "user":                 			clients_name[useridfrom]["username"],"userid":useridfrom}}).encode('utf-8'))              
        # 私聊,自己显示
        clients[useridfrom].send(json.dumps({"type": 1, "data": {"msg": msg, "user": 	 	 		 	clients_name[useridfrom]["username"],"userid":useridfrom}}).encode('utf-8'))       
    return HttpResponse(json.dumps({"msg":"success"}))

url

from django.urls import path
from Dweb import views as vs

urlpatterns = [
    path(r'news_chat/', vs.News_chat),
    path('chat/', vs.chat),
    path('msg_send/', vs.msg_send),
]

最后运行127.0.0.1:8000/news_chat/进入聊天页面,每当一个用户进入聊天室时直接就能看到,可以选择用户或者群发

注意的是此代码是我写的demo的核心功能,用户的登录与注册页面我没有往上放,可以自己写一下,流程上还是比较简单明了的,可以在此基础上进行更改!

;