websocket
- websocket概述
- websocket入门
- springboot整合Websocket
websocket概述
1 为什么需要websocket?
我们在上网时经常会遇到一些情况,就是页面没有刷新,但是一些数据会发生了改变,比如:在邮箱页面当收到新邮件时,我们并没有刷新页面,但是却会主动提示有新邮件达到,并会将未读邮件的数量+1。或者是在一些购物网站,当有新消息时并没有刷新页面,却提示了我们有新消息未读。
那像这样的业务场景是怎么实现的呢?为什么页面没有刷新,浏览器却提醒我们有新消息呢?
解决方案:
1、采用轮询的方式。即:通过js不断的请求服务器,查看是否有新数据,如果有,就获取到新数据。这种解决方法是否存在问题呢?
当然是有的,如果服务端一直没有新的数据,那么js也是需要一直的轮询查询数据,这就是一种资源的浪费。
2、使用webSocket来解决
那什么是websocket呢?
2 什么是WebSocket?
WebSocket是一种网络通信协议,它允许在单个TCP连接上进行全双工通信。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就可以直接创建持久性的连接,并进行双向数据传输。这种技术通过HTTP/1.1协议的101状态码进行握手,实现了一种长连接,使得双方可以实时地进行信息交换。
全双工(Full Duplex)、半双工(Half Duplex)和单工(Simplex Communication)是数据通信中描述数据在线路上传输方向的不同方式。
- 全双工(Full Duplex): 全双工允许数据在两个方向上同时传输,即通信的双方可以同时发送和接收数据。这相当于两个单工通信方式的结合,数据在两个方向上可以同时流动。全双工通信在发送数据的同时也能够接收数据,两者同步进行。例如,我们平时打电话时,可以同时说话并听到对方的声音,这就是全双工通信的一个典型例子。
- 半双工(Half Duplex): 半双工通信是指数据可以沿两个方向传送,但同一时刻一个信道只允许单方向传送,因此又被称为双向交替通信。在半双工通信中,虽然数据可以在两个方向上传输,但在任何时刻只能由其中的一方发送数据,另一方接收数据。若要改变传输方向,需由开关进行切换。例如,无线对讲机就是一种半双工设备,在同一时间内只允许一方讲话。
- 单工(Simplex Communication): 单工通信模式的数据传输是单向的。通信双方中,一方固定为发送端,一方则固定为接收端。信息只能沿一个方向传输,使用一根传输线。移动通信按照用户的通话状态和频率使用的方法,可分为单工制、半双工制和双工制。例如,计算机与打印机之间的通信是单工模式,因为只有计算机向打印机传输数据,而没有相反方向的数据传输。
总结来说,全双工、半双工和单工是数据通信中描述数据在线路上传输方向的不同方式。全双工允许数据在两个方向上同时传输,半双工允许数据在两个方向上交替传输,而单工则只允许数据在一个方向上传输。
WebSocket具有以下特点:
- 双向通信:WebSocket协议支持客户端和服务器之间的双向通信,使得双方可以实时地进行信息交换。
- 实时性:WebSocket协议的设计目标就是提供实时的通信体验,因此它能够有效地减少延迟,提高通信效率。
- 持久连接:WebSocket协议使用TCP的持久连接,这意味着一旦建立了连接,服务器就可以主动推送信息给客户端,而不需要每次都进行握手和建立连接的过程。
- 兼容性:WebSocket协议具有良好的兼容性,它可以在不同的设备和浏览器上使用,包括移动设备和PC浏览器。
- 事件驱动:WebSocket协议是基于事件驱动的,这意味着它能够更好地处理异步操作和实时事件。
WebSocket的应用场景包括但不限于实时聊天、股票交易、实时地图等需要实时数据传输的应用。它相对于传统的轮询和Comet技术,能够更节省服务器资源和带宽,并且能够更实时地进行通讯。
3 http和webSocket的区别
http协议是短连接,因为请求之后,都会关闭连接,下次重新请求数据,需要再次打开链接。
WebSocket协议是一种长链接,只需要通过一次请求来初始化链接,然后所有的请求和响应都是通过这个TCP链接进行通讯。
总结:
1、http和websocket的相同点
都是一样基于TCP的,都是可靠性传输协议。
都是应用层协议。
2、 http和WebSocket的区别
WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息。HTTP是单向的。
WebSocket是需要浏览器和服务器握手进行建立连接的。而http是浏览器发起向服务器的连接,服务器预先并不知道这个连接。
联系
WebSocket在建立握手时,数据是通过HTTP传输的。但是建立之后,在真正传输时候是不需要HTTP协议的。
4 浏览器对于webSocket的支持情况
目前,基本上所有的主流浏览器都支持WebSocket。具体来说,Chrome从Chrome 4开始支持WebSocket,Firefox从Firefox 4开始支持,Safari从Safari 5开始支持,而Edge也支持WebSocket。这些浏览器都内置了对WebSocket协议的实现,使得开发者可以在网页中轻松地使用WebSocket进行实时通信。
网址: https://caniuse.com/?search=websocket
三 webSocket入门
1 websocket的相关注解说明
-
@ServerEndpoint("/websocket/{uid}")这个注解是写在类上面的申明这是一个websocket服务需要指定访问该服务的地址,在地址中可以指定参数,需要通过{}进行占位
-
@OnOpen用法:public void onOpen(Session session, @PathParam("uid") String uid) throws IOException{}该方法将在建立连接后执行,会传入session对象,就是客户端与服务端建立的长连接通道通过@PathParam获取url传递中的参数
-
@OnClose用法:public void onClose() {}该方法是在连接关闭后执行
-
@OnMessage用法:public void onMessage(String message, Session session) throws IOException {}该方法用于接收客户端发来的消息message:发来的消息数据session:会话对象(也是通道)
-
发送消息到客户端用法:session.getBasicRemote().sendText("你好");通过session进行发送。
2 入门实现
创建一个Web项目
导包
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
编写webSocket入门代码
package cn.ronghuanet;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint("/websocket/{uid}")
public class MyWebSocket {
@OnOpen
public void onOpen(Session session, @PathParam("uid")String uid)throws Exception{
session.getBasicRemote().sendText(uid+"欢迎你!成功连接到websocket");
}
@OnClose
public void onClose(){
System.out.println("关闭连接");
}
@OnMessage
public void onMessage(String message,Session session)throws Exception{
System.out.println("接收到消息"+message);
session.getBasicRemote().sendText("你好,我已收到你的消息");
}
@OnError
public void onError(Session session,Throwable throwable){
System.out.println("出错了!!!!!");
throwable.printStackTrace();
}
}
写前端页面用来做测试
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script type="text/javascript">
const socket = new WebSocket("ws://localhost:8080/websocket/1");
socket.onopen = (ws) =>{
console.log("建立连接!", ws);
}
socket.onmessage = (ws) =>{
console.log("接收到消息 >> ",ws.data);
}
socket.onclose = (ws) =>{
console.log("连接已断开!", ws);
}
socket.onerror = (ws) => {
console.log("发送错误!", ws);
}
// 2秒后向服务端发送消息
setTimeout(()=>{
socket.send("发送一条消息试试");
},2000);
// 5秒后断开连接
setTimeout(()=>{
socket.close();
},5000);
</script>
</body>
</html>
将项目部署到tomcat,访问页面测试
3 springboot整合WebScoekt
3.1 基本实现
创建新项目
导入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies>
启动类
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
消息处理类,在spring中,处理消息的具体业务逻辑需要实现WebSocketHandler接口
/**
* 在本类中接收前端传递的消息,进行处理
*/
@Component
public class MyHandler extends TextWebSocketHandler {
/**
* 处理文本消息
* @param session
* @param message
* @throws Exception
*/
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
System.out.println("接收到前端传递的消息-->"+payload);
session.sendMessage(new TextMessage("===你好,我已经收到你的消息了======="));
if(message.getPayload().equals("10")){
for (int i = 0; i < 10; i++) {
session.sendMessage(new TextMessage("返回第"+i+"条消息到前端"));
Thread.sleep(1000);
}
}
}
/**
* 建立连接之后
* @param session
* @throws Exception
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
System.out.println("uid==>"+session.getAttributes().get("uid"));
System.out.println("欢迎你连接到websocket服务啊");
}
/**
* 断开连接之后
* @param session
* @param status
* @throws Exception
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
System.out.println("连接已经被关闭了..............");
}
}
websocket配置类,在该类中配置handler的访问path以及允许访问的源等
/**
* 注册websocket的处理类
*/
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Autowired
private MyHandler myHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler, "ws").setAllowedOrigins("*");
}
}
编写前端用于测试
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>websocket测试页面</title>
</head>
<body>
<script type="text/javascript">
const socket = new WebSocket("ws://localhost:8080/ws");
socket.onopen = (ws) =>{
console.log("和后端建立连接==>", ws);
}
socket.onmessage = (ws) =>{
console.log("接收到后端返回来的消息==> ",ws.data);
}
socket.onclose = (ws) =>{
console.log("连接已断开!", ws);
}
socket.onerror = (ws) => {
console.log("发送错误!", ws);
}
// 2秒后向服务端发送消息
setTimeout(()=>{
socket.send("10");
},5000);
window.setInterval(() => {
socket.send("后端你好,我是前端");
},1000)
// 5秒后断开连接
/*setTimeout(()=>{
socket.close();
},5000);*/
</script>
</body>
</html>
启动项目,在浏览器访问前端页面用于测试
3.2 webSocket拦截器
在Spring中提供了websocket拦截器,可以在建立连接之前写些业务逻辑,比如校验登录等。
@Component
public class MyHandleSnakeInterceptor implements HandshakeInterceptor {
/**
* 握手前
* @param request
* @param response
* @param wsHandler
* @param attributes
* @return true 建立连接 false 不满足条件,不建立连接
* @throws Exception
*/
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
System.out.println("准备握手了");
attributes.put("uid", 10001);
return true;
}
/**
* 握手后
* @param request
* @param response
* @param wsHandler
* @param exception
*/
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
System.out.println("握手成功了!!!!!!!");
}
}
在配置类中配置该拦截器要作用于哪些Handler
java
/**
* 注册websocket的处理类
*/
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Autowired
private MyHandler myHandler;
@Autowired
private MyHandleSnakeInterceptor myHandleSnakeInterceptor;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler, "ws").setAllowedOrigins("*")
.addInterceptors(myHandleSnakeInterceptor);
}
}