Websocket简介:
Websocket是一种在单个TCP连接上进行全双工通信的协议,已被定为W3C标准。其允许服务端主动向客户端推送数据。
WebSocket协议中,浏览器和服务器只需要完成一次握手,2者之间就可以直接创建持久性的连接,进行双向数据传输。
WebSocket使用了HTTP/1.1的协议升级特性。一个WebSocket请求首先使用非正常的HTTP、HTTPS。在一个请求头中有一个Connection:Upgrade字段,表示客户端想要对协议进行升级。还有一个Upgrade:websocket字段,表示客户端想要将请求的协议升级为WebSocket协议。
- Connection:Upgrade
- Upgrade:websocket
这2个字段共同告诉服务器将连接升级为Websocket全双工协议。如果服务端同意协议升级,那么握手完成后,文本消息或者2进制消息就可以同时在2个方向上进行发送,而不需要关闭和重建连接。此时的客户端和服务端的关系是对等的,可以互相向对方主动发送消息。
为什么需要Websocket:
Websocket的主要优点:
- 使用时需要先创建连接,这使得Websocket成为一种有状态的协议。在之后的通信过程中可以省略部分状态信息。(身份认证等)
- 连接在端口80(ws)/443(wss)上创建,与http使用的端口相同。基本上所有的防火墙都不会阻止Websocket连接。
- 使用HTTP协议进行握手。可以自然的集成到网络浏览器和HTTP服务器中,不需要额外的成本。
- 心跳消息(ping、pong)将被反复的发送,进而保持Websocket连接一只处于活跃的状态。
- 当消息启动或者到达的时候,服务端和客户端都可以知道。
- 连接关闭时将发送一个特殊的关闭消息。
- 支持跨域,避免Ajax的限制。
- HTTP规范要求浏览器将并发连接数限制为每个主机名2个连接,但是当使用websocket的时候,当握手完后该限制就不存在 了,此时的连接不再是http连接了。
- 支持扩展,用户可以扩展协议,实现部分自定义的子协议。
- 更好的2进制支持以及更好的压缩效果。
SpringBoot整合Websocket:
1.创建项目并添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator-core</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>sockjs-client</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>stomp-websocket</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1</version>
</dependency>
依赖说明:
spring-boot-starter-websocket: websocket的相关依赖
其余的是前端库,jar包形式管理前端库。webjar添加到项目中的前端库。SpringBoot默认添加了静态资源过滤。
2.配置Websocket:
Spring提供了基于Websocket的STOMP支持。STOMP是一个简单的可互操作的协议,常用于通过中间服务器在客户端之间进行异步消息传递。
3.定义Controller:
根据websocket的配置,可以直到,@MessageMapping("/hi")是用来接收"/app/hi"路径发送来的消息,方法对消息进行处理后,再将消息转发到@SendTo定义的路径上。而/topic/hellos的前缀是/topic,根据websocket的配置,该消息会被交给消息代理broker,由broker进行广播。
4.定义实体类:
5.编写前端页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>一起聊天吧!</title>
<script src="/webjars/jquery/jquery.min.js"></script>
<script src="/webjars/sockjs-client/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/stomp.min.js"></script>
</head>
<body>
<div>
<label for="name">输入用户名:</label>
<input type="text" id="name" placeholder="username">
</div>
<div>
<button id="connect" type="button">上线</button>
<button id="disconnect" type="button" disabled="disabled">下线</button>
</div>
<div id="chat" style="display:none">
</div>
<div>
<label for="content">Edit your communication's content:</label>
<input id="content" type="text" placeholder="待发送的内容">
</div>
<button id="send" type="button">发送</button>
<div id="hi">
<div id="conversation" style="display: none">you are Chatting...</div>
</div>
<script>
let stompClient = null;
function setConnected(connected) {
$("#connect").prop("disabled", connected);
$("#disconnect").prop("disabled",!connected);
if(connected){
$("#conversation").show()
$("#chat").show();
}else{
$("#conversation").hide()
$("#chat").hide();
}
$("#hi").html("");
}
//建立一个websocket连接。用户必须先输入用户名,才能建立连接
function connect() {
if(!$("#name").val()){
return;
}
let socket = new SockJS('/chat');
stompClient = Stomp.over(socket);
stompClient.connect({},function (frame) {
setConnected(true);
stompClient.subscribe('/topic/hellos',function (data) {//订阅服务端发送回来的消息
showHi(JSON.parse(data.body));
});
});
}
function disconnect() {
if(stompClient !== null){
stompClient.disconnect();//断开一个WebSocket连接
}
setConnected(false);
}
function sendName() {
stompClient.send("/app/hi",{},JSON.stringify({'name':$("#name").val(),'content':$("#content").val()}));
}
function showHi(message) {
$("#hi").append("<div>"+message.name+" : "+message.content + "</div>");
}
$(function () {
$("#connect").click(function () {
connect();
});
$("#disconnect").click(function () {
disconnect();
});
$("#send").click(function () {
sendName();
});
});
</script>
</body>
</html>
效果:
css可以自己美化
番外语:
在http协议中,所有的请求都是客户端发起的,由服务端进行响应,服务端无法向客户端推送消息。
在一些IM应用中,需要服务端向客户端推送消息。
解决方案:
1.轮询
轮询,就是客户端在固定的时间间隔下不停的向服务器发送请求,查看服务端是否有最新的数据,若服务端有最新数据,则返回给客户端,反之返回一个空的json或者xml文档。
缺点:
客户端每次都要新建HTTP请求,服务端要处理大量的无效请求,在高并发场景下严重拖慢服务端的运行效率,服务端的资源也被浪费。
2.长轮询
是传统轮询的升级版。在长轮询中,服务端不是每次都立即响应客户端的请求,只有在服务端有最新数据的时候才立即响应客户端的请求,否则服务端会持有这个请求而不返回,直到有最新的数据时才返回。
缺点:
如果浏览器在服务器响应之前有新数据要发送,只能创建一个新的并发请求,or先尝试断掉当前请求,再创建新的请求。
TCP和HTTP规范中有连接超时。所谓的长轮询并不能一直持续,服务端和客户端的连接需要定期的连接和关闭再连接。
3.Applet和Flash
这2个技术都Out了。不过,他们以前可以让HTML更加绚丽,还解决消息推送问题。
使用Applet和Flash来模拟全双工通信,通过创建一个只有1像素点大小的透明的Applet or Flash,然后内嵌到网页中,再从Applet or Flash代码中创建一个Socket连接进行双向通信。消除了HTTP协议中的很多限制。当服务器有消息发送到客户端的时候,可以在Applet or Flash调用JS的Function将数据显示在页面上,当浏览器有数据要发送到服务器时也一样,通过Applet or Flash来传递,真正的实现了全双工通信。
缺点:
浏览器必须能运行Java orFlash.无论是Applet或者Flash都存在安全问题。随着H5的支持,Flash下架被提上日程(2020年正式停止支持Flash)
4.Websocket.
见文章首部说明。