IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> WebSocket介绍、例子、开发遇到的问题记录 -> 正文阅读

[网络协议]WebSocket介绍、例子、开发遇到的问题记录

WebSocket知识分享

前提简介

什么是WebSocket

webSocket是一种网络通讯协议,与Htpp协议的区别是,Htpp通信只能由客户端发起,服务端响应的这种单向通信;而webSocket最大的特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,实现客户端与服务端的双向通讯。

WebSocket API

  • WebSocket.onopen 连接成功的回调
  • WebSocket.onclose 连接关闭后的回调
  • WebSocket.onerror 连接失败后的回调
  • WebSocket.onmessage 客户端接收服务端数据的回调
  • WebSocket.readyState 当前连接状态
  • WebSocket.bufferedAmount 未发送至服务器的二进制字节数
  • WebSocket.binaryType 使用二进制的数据类型连接
  • WebSocket.protocol 服务器选择的下属协议
  • WebSocket.url WebSocket的绝对路径

方法

  • WebSocket.close() 关闭当前连接
  • WebSocket.send(data) 向服务器发送数据

其余特点

  • 握手阶段采用HTTP协议
  • 数据格式轻量,性能开销小
  • 可以发送文本,和二进制数据
  • 没有同源策略的限制
  • webSocket协议的标识符是ws,加密的就是wss
    http协议的标识符是http,加密的就是https

几种与服务端实时通信的方法

除了WebSocket外,还有其它与服务端建立实时通信的方式

  • AJAX轮询
  • Long Polling长轮询

前端代码部分(根据需求没有做太复杂的ws,使用的是原生的ws连接)

// 初始化ws,与服务器建立连接
var url = 'ws://localhost:8080/rest/openingController/websocket'
var websocket = null
//判断浏览器是否支持WebSocket
if ('WebSocket' in window) {
    websocket = new WebSocket(url);
} else if ('MozWebSocket' in window) {
    websocket = new MozWebSocket(url);
} else {
    websocket = new SockJS(url); // SockJS第三方库
}

// 初始化ws方法
websocket.onopen = onOpen;
websocket.onmessage = onMessage;
websocket.onerror = onError;
websocket.onclose = onClose;
function onOpen(result) {
    console.log('连接建立时触发')
	heartBeatCheck.start(); // 建立连接成功后,启动心跳检测
}
function onMessage(result) {
    console.log('客户端接收服务端数据时触发')
}
function onError(result) {
    console.log('通信发生错误时触发');
}
function onClose(result) {
    console.log('连接关闭时触发');
}
function doSend(params) {
    websocket.send(params);
	console.log('客户端向服务端发送数据')
}

//处理websocket业务
var ifCheckHeart = true; //是否开启心跳检测

//websocket 心跳检测
var heartBeatCheck = {
	timeout: 5000,
	timeoutObj: null,
	serverTimeoutObj: null,
	start: function start() {
		if (!ifCheckHeart) {
			return false;
		}

		clearTimeout(heartBeatCheck.timeoutObj);
		clearTimeout(heartBeatCheck.serverTimeoutObj);

		heartBeatCheck.timeoutObj = setTimeout(function () {
			websocket.send("client heart beat check");//这里发送一个心跳,后端收到后,返回一个心跳消息,
			heartBeatCheck.serverTimeoutObj = setTimeout(function () {
			   websocket.onClose();
			}, heartBeatCheck.timeout);
		}, heartBeatCheck.timeout);
	}
}

// 关闭网页时断开连接,其实再离开网页时,因为不再能检测到,会自动断开
window.onbeforeunload = function () {
     websocket.onClose();
};

开发中遇到的疑难问题

这里记录开发中遇到的2个问题,大家后面遇到类似的问题,可以参考下,因为我觉得,这种问题算的上是一种共性问题

问题1

问题现象:

进入页面几秒后弹出连接断开

问题原因:

再谷歌、ie、360安全浏览器中建立ws均正常,但再13、14版本的苹果浏览器中,ws已经建立成功,但是因为苹果浏览器不支持ws原有的协议,所以再建立连接成功后又给断开了连接,错误码为1006
(注:此问题不光是苹果浏览器会出现,再一些低版本的浏览器中也会出现)

解决办法:

修改后端代码,代码/注释如下

/** 在拦截器内强行修改websocket协议,将部分浏览器不支持的 x-webkit-deflate-frame 扩展修改成 permessage-deflate */
if(request.getHeaders().containsKey("Sec-WebSocket-Extensions")){
    request.getHeaders().set("Sec-WebSocket-Extensions", "permessage-deflate");
}

问题2

问题现象:

进入页面几秒后弹出连接断开,ws可以正常建立连接,但客户端和服务器端再发送或推送消息都失败

问题原因:

客户端发送ws连接请求后,服务器与客户端握手成功(建立成功),握手成功后服务端会有一个拦截器,服务端拦截器的作用是给当前请求过来的客户端打一个标识*(标识可以用token、sessionId或后端生成一个随机数等),以此来识别该用户;
因为每个浏览器的协议不同,所以再世界之窗浏览器上,服务端的拦截器获取请求头中的sessionId为空
(后端方法:request.getHeaders() 获取到的 sessionId为空)* ,此时服务端识别不了是哪个用户,所以推送消息失败

解决办法:

即使后端通过request.getHeaders()获取 sessionId为空,但却可以从请求头的cookie中获取到token*(后端方法:request.getHeaders().get(“cookie”))*来标识发请求来的用户是谁,以此来解决该问题

public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
                                   Map<String, Object> attributes) throws Exception {
        System.out.println("Before Handshake");
        logger.debug("Before Handshake");

        /** 在拦截器内强行修改websocket协议,将部分浏览器不支持的 x-webkit-deflate-frame 扩展修改成 permessage-deflate */
        if(request.getHeaders().containsKey("Sec-WebSocket-Extensions")){
            request.getHeaders().set("Sec-WebSocket-Extensions", "permessage-deflate");
        }

        if (request instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
            HttpSession session = servletRequest.getServletRequest().getSession(false);
            if (session != null) {
//                String userName = (String) session.getAttribute("SESSION_USERNAME");  //一般直接保存user实体
                TSUser user = (TSUser) session.getAttribute(ResourceUtil.LOCAL_CLINET_USER);  //一般直接保存user实体
                if (user !=null) {
                    attributes.put(ResourceUtil.WEBSOCKET_USERID,user.getId());
                } else {
                    String cookies = (String)request.getHeaders().get("cookie").get(0);
                    String token = cookies.substring(cookies.indexOf("token=")+5);
                    token = token.substring(0, token.indexOf(";"));
                    attributes.put(ResourceUtil.WEBSOCKET_USERID, token);

                }
                //从请求中获取标段id与当前用户角色
                String wsType = servletRequest.getServletRequest().getParameter("ws_type");
                String sectionId = servletRequest.getServletRequest().getParameter("section_id");
                String role = servletRequest.getServletRequest().getParameter("role");
                //判断此处为客户端的WS连接
                System.out.println("#############准备建立连接#############");
                if (StringUtils.isNotBlank(wsType) && "openLobby".equals(wsType)) {
                    //传入ws session 供连接建立成功使用
                    //若标段id或当前角色为空,不建立连接
                    System.out.println("#############准备建立连接#############");
                    if (StringUtils.isNotBlank(sectionId) || StringUtils.isNotBlank(role)) {
                        System.out.println("ws_type:"+wsType);
                        System.out.println("section_id:"+sectionId);
                        System.out.println("role:"+role);
                        System.out.println("#############准备建立连接,满足连接条件#############");
                        attributes.put("ws_type",wsType);
                        attributes.put("section_id",sectionId);
                        attributes.put("role",role);
                    } else {
                        return false;
                    }
                }
            } else {
                String cookies = (String)request.getHeaders().get("cookie").get(0);
                String token = cookies.substring(cookies.indexOf("token=")+5);
                token = token.substring(0, token.indexOf(";"));
                attributes.put(ResourceUtil.WEBSOCKET_USERID, token);
            }
        }
        return super.beforeHandshake(request, response, wsHandler, attributes);
    }
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-09-03 12:18:17  更:2021-09-03 12:20:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年6日历 -2024/6/27 1:43:32-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码