一、为什么需要websocket?: 前端和后端的交互模式最常见的就是前端发数据请求,从后端拿到数据后展示到页面中。如果前端不做操作,后端不能主动向前端推送数据,这也是http协议的缺陷。
因此,一种新的通信协议应运而生—websocket,他最大的特点就是服务端可以主动向客户端推送消息,客户端也可以主动向服务端发送消息,实现了真正的平等。 websocket其他特点如下:
(1)建立在 TCP 协议之上,服务器端的实现比较容易。
(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
(3)数据格式比较轻量,性能开销小,通信高效。
(4)可以发送文本,也可以发送二进制数据。
(5)没有同源限制,客户端可以与任意服务器通信。
(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
二、vue部分(客户端): api.js:
export const getWebSocket = () => { let protocol = document.location.protocol === 'https:' ? 'wss://' : 'ws://' let url = protocol + document.location.host + '/nms/ws/' let ws = new WebSocket(url) ws.onopen = onOpen ws.onclose = onClose return ws }home.vue
... ... ... mounted: function() { this.initWebSocket() }, ... ... ... ```javascript methods: { // ws消息处理 initWebSocket: function () { this.ws = api.getWebSocket(); this.ws.onopen = onOpen this.ws.onmessage = this.onMessage; this.ws.onerror = this.websocketonerror; this.ws.onclose = this.websocketclose; }, websocketonerror(){//连接建立失败重连 this.initWebSocket(); }, onMessage(evt) { console.log(evt.data); let data = JSON.parse(evt.data); let status = ""; // 根据需求做相应的处理 try { switch (data.eventid) { case "aaa": // 抓包开始成功 ... ... ... break; case "bbb": // 抓包开始失败 ... ... ... break; ... ... ... default: break; } } catch (err) { console.warn(err); } }, websocketclose(e){ //关闭 console.log('断开连接',e); } },三、django部分(服务端): Django通过dwebsocket实现websocket通信: view.py
from django.shortcuts import render import logging import json import threading from dwebsocket.decorators import accept_websocket import os from nms_server.utils.myglobal import * from nms_server.dao.diagnosis import add_capture_file, updata_capture_file from nms_server.dao.redis.device import get_device_user_moid from nms_server.rmq import RMQConsumerThread from django.conf import settings logger = logging.getLogger('nms.'+__name__) # 存储连接websocket的用户 # 消费者创建标识 consumer_flag = True @accept_websocket def websocket_link(request): global consumer_flag # '连接websocket' # 获取连接 if request.is_websocket: lock = threading.RLock()#rlock线程锁 try: lock.acquire()#抢占资源 s = {} if consumer_flag: try: logger.info('init nms_webserver_consumer: %s' % os.getpid()) RMQConsumerThread( name='nms_webserver_consumer_{}'.format(os.getpid()), amqp_url=settings.AMQP_URL, exchange='nms.webserver.ex', queue='nms.webserver.q.{}'.format(os.getpid()), routing_key='nms.webserver.k', exchange_type='topic' ).start() consumer_flag = False except Exception as e: logger.error(e) user = getattr(request, 'sso_user', None) user_moid = '' if user is not None: user_moid = user['data']['moid'] else: logger.error('sso_user in None') raise ValueError clients = get_clients() # 因为同一个账号打开多个页面连接信息是不同的 if clients.get(user_moid) != None: # 连接信息 键 连接名 值:连接保存 s[str(request.websocket)] = request.websocket # 已存在的连接信息继续增加 clients[user_moid].update(s) else: # 连接信息 键 连接名 值:连接保存 s[str(request.websocket)] = request.websocket # 新增 用户 连接信息 clients[user_moid] = s set_clients(clients) logger.info('pid:%d ,client: %s',os.getpid(),clients) # 监听接收客户端发送的消息 或者 客户端断开连接 for message in request.websocket: if not message: break else: client_msg_handler(user_moid,message) finally: logger.info('close client connect,user_moid:%s,request.websocket:%s',user_moid,str(request.websocket)) # 通过用户名找到 连接信息 再通过 连接信息 k 找到 v (k就是连接信息) clients = get_clients() clients.get(user_moid).pop(str(request.websocket)) if not clients.get(user_moid): clients.pop(user_moid) set_clients(clients) logger.info('client: %s',clients) #释放锁 lock.release() # 客户端消息处理 def client_msg_handler(user_moid,msg): logger.info('[client_msg_handler] msg:%s' ,msg.decode('utf-8')) # test代码 # import time # data = { # 'event':'ack', # 'text':'hello world' # } # for i in range(5): # data['id'] = i # send_msg_to_client(user_moid,data) # time.sleep(1) pass # 发送消息 def websocketMsg(client, msg): # 因为一个账号会有多个页面打开 所以连接信息需要遍历 for cli in client: b1 = json.dumps(msg).encode('utf-8') client[cli].send(b1) ''' @description: 服务端发送消息 @user_moid {str} 用户moid @msg {json} 消息 @return: ''' def send_msg_to_client(data): clients = get_clients() logger.info('pid:%d, send_msg_to_client(data): %s',os.getpid(),data) logger.info('pid:%d ,client: %s',os.getpid(),clients) try: if data['user_moid'] == '': data['user_moid'] = get_device_user_moid(data['devid'],data['devtype']) user_moid = data['user_moid'] if clients[user_moid]: if data['eventid'] == 'EV_PACKETCAPTURE_STOP_ACK': # 保存抓包文件到数据库 file_name = data["url"].split("/")[-1] create_time = data['rpttime'].replace("/", "-").split(':',1) create_time = create_time[0] + " " + create_time[1] add_capture_file(data['user_moid'], data['devid'], file_name, data.get('size',0), create_time) if data['eventid'] == 'EV_PACKETCAPTURE_UPLOAD_PROGRESS_NTF': # 更新终端抓包文件信息 file_name = data["url"].split("/")[-1] if 'size' in data: updata_capture_file(data['user_moid'], data['devid'], file_name, data['size']) websocketMsg(clients[user_moid],data) except BaseException as e: logger.error(e)参考: python websocket Django 实时消息推送 封装websocket请求-----vue项目实战 Django通过dwebsocket实现websocket 官方链接
