#!/usr/bin/env python # -*- coding:utf-8 -*- from channels.generic.websocket import WebsocketConsumer from channels.generic.websocket import AsyncWebsocketConsumer from channels.exceptions import StopConsumer from asgiref.sync import async_to_sync # 导入redis配置 import redis from api.redis_cli import Pool # 用户列表 USERLIST = {} class ChatConsumer(WebsocketConsumer): # 服务端接收连接,像客户端发送一个加密字符串 def websocket_connect(self, message): # 固定获取参数格式 self.scope 所有前端来的请求参数 username = self.scope[‘url_route‘][‘kwargs‘].get("username") room_id = self.scope[‘url_route‘][‘kwargs‘].get("room_id") # self.channel_name 生成随机字符串ID 代表的是连接的自己 async_to_sync(self.channel_layer.group_add)(room_id, self.channel_name) # 连接 self.accept() sr = redis.Redis(connection_pool=Pool) res = sr.hget(room_id,"room") # print("res",res) if res == None: # 如果没有有用户在这个房间 sr.hset(room_id,"room",username) user_list = username else: if username in res.split("-"): user_list = res else: # 如果房间已经存在用户 那么就增加用户组 user_list = res + "-" + username # print("增加之后的",user_list) sr.hset(room_id,"room",user_list) # 发消息 这里面是可以定义自己发送的内容 async_to_sync(self.channel_layer.group_send)(room_id, { ‘type‘: ‘make.first‘, ‘message‘: user_list, }) # 客户端向服务端发送消息,此方法被触发 def websocket_receive(self, text_data=None, bytes_data=None,): """ :param text_data[‘text‘]: 接收到的消息 """ username = self.scope[‘url_route‘][‘kwargs‘].get("username") room_id = self.scope[‘url_route‘][‘kwargs‘].get("room_id") # 针对群体发消息 async_to_sync(self.channel_layer.group_send)(room_id, { ‘type‘: ‘make.send‘, ‘message‘: "2#" + username + "#" + text_data[‘text‘] }) # 断开连接 def websocket_disconnect(self, message): print(‘客户端断开连接了‘) username = self.scope[‘url_route‘][‘kwargs‘].get("username") room_id = self.scope[‘url_route‘][‘kwargs‘].get("room_id") # 踢出群 async_to_sync(self.channel_layer.group_discard)(room_id, self.channel_name) # 先取出redis里面的这个房间的用户 sr = redis.Redis(connection_pool=Pool) res = sr.hget(room_id,"room") res_list = res.split("-") # 获取改房间用户组,移除退出的用户 try: res_list.remove(username) except: pass # 整理剩下的用户 填入redis if len(res_list) == 0: sr.expire(room_id, 1) user_list = "" if len(res_list) == 1: user_list = res_list[0] else: for user in res_list: user_list = user + "-" + user_list user_list = user_list[0:-1] # 传入redis sr = redis.Redis(connection_pool=Pool) sr.hset(room_id,"room", user_list) # 发消息 这里面是可以定义自己发送的内容 async_to_sync(self.channel_layer.group_send)(room_id, { ‘type‘: ‘make.first‘, ‘message‘: user_list, }) raise StopConsumer() # 发消息内容 def make_first(self, event): # event储存着上面定义的内容 message = "1#" + event[‘message‘] self.send(message) # 回复群里的消息 event相当于是text_data def make_send(self, event): message = event[‘message‘] self.send(message)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Talking</title> <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.3.1/css/bootstrap.min.css"> <script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script> <script src="https://cdn.staticfile.org/popper.js/1.15.0/umd/popper.min.js"></script> <script src="https://cdn.staticfile.org/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script> <style> #taking { width: 100%; height: 700px; background: #42fbff; } img{ max-height: 200px; max-width: 200px; } </style> </head> <body> <div id="app" class="container"> <div class="card-header">在线聊天室</div> <span style="color: red;font-size: 18px">当前在线用户: {%verbatim %} {{ userList }} {% endverbatim %}</span> <div id="taking"> <ul> <li v-for="item in chat">{%verbatim %} {{ item }} {% endverbatim %}</li> {# <li v-for="item in chat"><img src=""> </li>#} </ul> </div> <div style="width: 100%;height: 80px"> <textarea type="text" v-model="txt" placeholder="请输入内容" style="width: 100%;height: 100%"></textarea> </div> <button class="btn btn-primary" style="width: 100px" @click="send">发送</button> <button class="btn btn-primary" style="width: 100px" @click="sendimages">发送图片</button> <input type="file" name="file" id="file"/> <button class="btn btn-danger" style="width: 100px" @click="close">断开连接</button> </div> </body> </html> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> var vmm = new Vue({ el: ‘#app‘, data: { txt: "", path: "ws://127.0.0.1:8001/chat/", socket: "", base:"", chat: [], userList: "" }, mounted() { // 初始化 this.init() }, methods: { close() { this.chat.push("socket已断开") this.socket.close() }, destroyed() { // 销毁监听 this.socket.onclose = this.close }, // 初始化 init: function () { function random(min, max) { return Math.floor(Math.random() * (max - min)) + min; } if (typeof (WebSocket) === "undefined") { alert("您的浏览器不支持socket") } else { // 实例化socket this.socket = new WebSocket(this.path + sessionStorage.getItem(‘token‘) + "/") // 监听socket连接 this.socket.onopen = this.open // 监听socket错误信息 this.socket.onerror = this.error // 监听socket消息 this.socket.onmessage = this.getMessage } }, open() { this.chat.push("socket连接成功") }, error() { alert("连接错误") }, // 渲染消息 getMessage(msg) { // 在线人数 if (msg.data.split("#")[0] === "1") { this.userList = msg.data.split("#")[1] } else { // 在线内容 if (msg.data.split("#")[2].length > 500){ var user = msg.data.split("#")[1] + ":" var src = msg.data.split("#")[2] var $ele = ("<li>" + user + " <img src=" + src +"></li>") $("ul").append($ele) }else { this.chat.push(msg.data.split("#")[1] + ":" + msg.data.split("#")[2]) } } }, // 发送消息 send() { this.socket.send(this.txt) }, //传入图片路径,返回base64 getBase64(img) { function getBase64Image(img, width, height) {//width、height调用时传入具体像素值,控制大小 ,不传则默认图像大小 var canvas = document.createElement("canvas"); canvas.width = width ? width : img.width; canvas.height = height ? height : img.height; var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0, canvas.width, canvas.height); var dataURL = canvas.toDataURL(); return dataURL; } var image = new Image(); image.crossOrigin = ‘‘; image.src = img; var deferred = $.Deferred(); if (img) { image.onload = function () { deferred.resolve(getBase64Image(image));//将base64传给done上传处理 } return deferred.promise(); } }, // 获取文件对象 转换成它的url暂缓地址 getObjectURL(file) { var url = null; if (window.createObjcectURL != undefined) { url = window.createOjcectURL(file); } else if (window.URL != undefined) { url = window.URL.createObjectURL(file); } else if (window.webkitURL != undefined) { url = window.webkitURL.createObjectURL(file); } return url; }, // 转换编码 发送图片 sendimages() { that = this var inputElement = document.getElementById("file") var file = inputElement.files var objURL = this.getObjectURL(file[0]); // 获取64 this.getBase64(objURL) .then(function (base64) { {#console.log(typeof(base64));//处理成功打印在控制台#} that.base = base64 that.socket.send(that.base) }, function (err) { console.log(err);//打印异常信息 }); } }, }) </script>