1、websocket理论与实践
1.1、什么是websocket协议
Websocket是HTML5开始提供的一种在单个TCP连接上进行的全双工的网络通信协议
1.2、为什么要用websocket
答案很简单,因为http协议有一个缺点:通信只能由客户端发起。举个例子,假如我们了解今天的天气,那么我们只能由客户端向服务端发送请求,来获取天气信息。
这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。
轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此,工程师们一直在思考,有没有更好的方法。WebSocket 就是这样发明的。
1.3、应用场景
1.社交聊天
最著名的就是微信,QQ,这一类社交聊天的app。这一类聊天app的特点是低延迟,高即时。即时是这里面要求最高的,如果有一个紧急的事情,通过IM软件通知你,假设网络环境良好的情况下,这条message还无法立即送达到你的客户端上,紧急的事情都结束了,你才收到消息,那么这个软件肯定是失败的。
2.弹幕
说到这里,大家一定里面想到了A站和B站了。确实,他们的弹幕一直是一种特色。而且弹幕对于一个视频来说,很可能弹幕才是精华。发弹幕需要实时显示,也需要和聊天一样,需要即时。
3.多玩家游戏
4.协同编辑
现在很多开源项目都是分散在世界各地的开发者一起协同开发,此时就会用到版本控制系统,比如Git,SVN去合并冲突。但是如果有一份文档,支持多人实时在线协同编辑,那么此时就会用到比如WebSocket了,它可以保证各个编辑者都在编辑同一个文档,此时不需要用到Git,SVN这些版本控制,因为在协同编辑界面就会实时看到对方编辑了什么,谁在修改哪些段落和文字。
5.股票基金实时报价
金融界瞬息万变——几乎是每毫秒都在变化。如果采用的网络架构无法满足实时性,那么就会给客户带来巨大的损失。几毫秒钱股票开始大跌,几秒以后才刷新数据,一秒钟的时间内,很可能用户就已经损失巨大财产了。
6.体育实况更新
全世界的球迷,体育爱好者特别多,当然大家在关心自己喜欢的体育活动的时候,比赛实时的赛况是他们最最关心的事情。这类新闻中最好的体验就是利用Websocket达到实时的更新!
7.视频会议/聊天
视频会议并不能代替和真人相见,但是他能让分布在全球天涯海角的人聚在电脑前一起开会。既能节省大家聚在一起路上花费的时间,讨论聚会地点的纠结,还能随时随地,只要有网络就可以开会。
8.基于位置的应用
越来越多的开发者借用移动设备的GPS功能来实现他们基于位置的网络应用。如果你一直记录用户的位置(比如运行应用来记录运动轨迹),你可以收集到更加细致化的数据。
9.在线教育
在线教育近几年也发展迅速。优点很多,免去了场地的限制,能让名师的资源合理的分配给全国各地想要学习知识的同学手上,Websocket是个不错的选择,可以视频聊天、即时聊天以及其与别人合作一起在网上讨论问题…
1.4、Demo-在线聊天系统
基于websocket的网页聊天室流程:
前端:
// WebSocket客户端流程
//创建websocket对象
var ws = new WebSocket("ws://localhost:8088/chat");
//建立连接后触发
ws.onopen = function () {
//在建立来连接后需要做什么事情,将用户名显示在界面上
$('#chatMeu').html('<p>用户:' + username + "<span style='float: right;color: greenyellow; height: 20px'>在线</span></p>")
};
//接收到服务端的推送后触发
ws.onmessage = function (evt) {
//获取服务端推送过来的数据
var dataStr = evt.data;
//jsonData 格式举例(需要判断是否是系统消息):{“systemMsgFlag”: false, "fromName": "YYJ", "message": “你在哪里呀?”}
var jsonData = JSON.parse(dataStr);
//判断是否是系统消息
if (jsonData.systemMsgFlag) {
//是系统消息,处理
//1.好友列表处理
var friendsList = "";
var systemMsg = "";
var allNames = jsonData.message;
for (var name of allNames) {
if (username !== name) {
friendsList += "<li><a style='text-decoration: underline' οnclick='chatWith(\"" + name + "\")'>" + name + "</a></li>";
systemMsg += "<li>" + name + " 上线啦!</li>";
}
}
//渲染页面
$("#friendsList").html(friendsList);
$("#systemMsg").html(systemMsg);
} else {
//不是系统消息,是发送给指定用户的消息,示例值:{“systemMsgFlag”: false, "fromName": "YYJ", "message": “你在哪里呀?”}
var data = jsonData.message;
var cnt = "<div class=\"atalk\"><span id=\"asay\">" + data + "</span></div>"
if (toName === jsonData.fromName) {
$("#chatCnt").append(cnt);
}
var chatData = sessionStorage.getItem(jsonData.fromName);
if (chatData != null) {
cnt = chatData + cnt;
}
sessionStorage.setItem(jsonData.fromName, cnt);
}
};
//关闭连接触发
ws.onclose = function () {
$('#chatMeu').html('<p>用户' + username + "<span style='float: right;color: #d50a0a; height: 20px'>离线</span></p>")
};
后端:
1.1、导入webSocket依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
1.2、配置WebSocketConfig
package com.yyj.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
/** 扫描注解了@ServerEndpoint注解的类 */
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
界面:
源码:https://gitee.com/userforgitee/websocket-demo