一、websocket简单介绍
WebSocket是一种在单个TCP连接上进行全双工通信的协议。该协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范,同时WebSocket API也被W3C定为标准。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。
在WebSocket中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。这种持久连接使得服务器可以主动向客户端推送数据,而不需要等待客户端的请求。这种特性使得WebSocket在实时通信、多人在线游戏、在线教育、音视频聊天等领域具有广泛的应用。
WebSocket的工作原理是在HTTP协议上建立一种全双工的通信方式,使得客户端和服务器之间可以建立一次连接,然后保持这个连接的开放状态,而不需要在每次通信后关闭连接。WebSocket通过HTTP/1.1协议的101状态码进行握手,并通过浏览器发出请求,之后服务器进行回应来建立连接。
二、websocket优缺点
WebSocket具有以下优点:
1.实时性:由于协议是全双工的,所以服务器可以随时主动给客户端下发数据,而不需要等待客户端的请求。
2.较少的控制开销:在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小,从而显著减少了开销。
3.更好的二进制支持:Websocket定义了二进制帧,相对HTTP,可以更轻松地处理二进制内容。
4.保持连接状态:与HTTP不同的是,Websocket需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。
然而,WebSocket也存在一些缺点:
1.兼容性问题:WebSocket协议在一些旧版本的浏览器上不被支持,需要通过polyfill或者其他技术手段来解决兼容性问题。
2.服务器资源占用:由于WebSocket的长连接特性,服务器需要维护大量的连接,这可能会占用较多的服务器资源。
3.安全性问题:WebSocket连接需要特殊的安全设置,以防止恶意攻击和数据泄漏。
总的来说,WebSocket是一种强大的通信协议,适用于需要实时通信和双向数据传输的应用场景。在选择使用WebSocket时,需要根据具体的需求和场景来评估其优缺点。
三、简单使用
1.服务端
1.创建springboot项目,并导入常用依赖
2.导入websocket依赖
<!--websocket依赖包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
3.创建一个websockt配置类
package com.etime.websocket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* @Description websocket 配置类
* @Date 2024/5/20 10:28
* @Author liukang
**/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverendpointExporter(){
return new ServerEndpointExporter();
}
}
4.创建websocket服务终端类
package com.etime.websocket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Description 监听websocket地址 /myWs
* @Date 2024/5/18 20:10
* @Author liukang
**/
@ServerEndpoint("/myWs") // websocket终端地址
@Component // 将该类交给spring管理
@Slf4j // 用于输出日志信息
public class WsServerEndPoint {
// ConcurrentHashMap 和hashMap的区别是 ConcurrentHashMap是线程安全的
static Map<String,Session> sessionMap = new ConcurrentHashMap<>();
/**
* websocket建立链接时触发方法
* @author liukang
* @date 20:15 2024/5/18
* @param session 每一个websocket的链接 对于服务端都是一个session
**/
@OnOpen
public void onOpen(Session session){
// 建立连接时,将session存入map中
sessionMap.put(session.getId(),session);
log.info("websocket is open");
}
/**
* 收到了客户端消息时触发方法
* @author liukang
* @date 20:20 2024/5/18
* @param text 客户端传来的消息内容
* @return java.lang.String
**/
@OnMessage
public String onMessage(String text){
log.info("接受到一条消息:"+text);
return "已经接收到消息";
}
/**
* 连接关闭时触发方法
* @author liukang
* @date 20:21 2024/5/18
* @param session 每一个websocket的链接 对于服务端都是一个session
**/
@OnClose
public void onClose(Session session){
sessionMap.remove(session.getId());
log.info(("websocket is close"));
}
/**
* 定时任务模拟服务器给客户端主动发消息
* @author liukang
* @date 20:24 2024/5/18
**/
@Scheduled(fixedRate = 2000) // fixedRate = 2000 每隔2s触发一次定时任务
public void sendMessage() throws IOException {
// 遍历sessionMap中的所有会话,给每个客户端都发送消息
Set<String> keySet = sessionMap.keySet();
for (String sessionId : keySet) {
Session session = sessionMap.get(sessionId);
RemoteEndpoint.Basic basicRemote = session.getBasicRemote();
basicRemote.sendText("心跳");
}
}
}
5.启动类
由于第4点中,用spring内置的定时任务向客户端定时发送心跳,启动类上需要加上@EnableScheduling 注解,用于开启定时任务。
package com.etime;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* @Author liukang
* @Date 2022/7/4 11:32
*/
@EnableScheduling // 开启定时任务
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
2.客户端
新建一个vue文件,与服务端进行websocket 连接
代码示例如下:
<template>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
}
},
mounted(){
let ws = new WebSocket("ws://localhost:8090/myWs")
ws.onopen = function(){
console.log('open')
ws.send("hello")
}
ws.onmessage = function(msg){
console.log(msg.data)
}
ws.onclose = function(){
console.log('close')
}
},
}
</script>
<style scoped>
</style>
注: 此处的ws://localhost:8090/myWs与服务端对应;myWs为后端websocket终端类中@ServerEndpoint(“/myWs”)定义的值
let ws = new WebSocket("ws://localhost:8090/myWs")
3.效果展示
访问前端的vue页面,效果如下:
客户端控制台:
服务端控制台: