WebSocket整合SpringBoot并传输JSON数据进行操作
文章目录
1、引入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<springboot.version>2.2.1.RELEASE</springboot.version>
<java.version>1.8</java.version>
<swagger.version>2.7.0</swagger.version>
<lombok.version>1.18.10</lombok.version>
</properties>
<dependencies>
<!--Springboot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<!--swagger ui-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<!--lombok用来简化实体类:需要安装lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!-- webSocket 集成 springboot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
</dependencies>
2、创建Configuration配置类注入WebSocket
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
/**
* 向spring容器中注入这个ServerEndpointExporter对象
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
3、编写WebSocketServer服务类
decoders = {OperatorDecoder.class},
encoders = {OperatorEncoder.class}
是进行webSocketMessage和实体类之间映射关系的类
import com.camera.entity.LoginCamera;
import com.camera.entity.Operator;
import com.camera.entity.OperatorDecoder;
import com.camera.entity.OperatorEncoder;
import com.camera.service.CloudService;
import com.camera.service.LoginService;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
@ServerEndpoint(value = "/ptzControl",decoders = {OperatorDecoder.class},encoders = {OperatorEncoder.class})
@Component
public class WebSocketServer {
//在WebSocket也是这样注入,因 SpringBoot+WebSocket 对每个客户端连接都会创建一个 WebSocketServer(@ServerEndpoint 注解对应的)对象,
// Bean 注入操作会被直接略过,因而手动注入一个全局变量
private static final AtomicInteger OnlineCount = new AtomicInteger(0);
// concurrent包的线程安全Set,用来存放每个客户端对应的Session对象。
private static CopyOnWriteArraySet<Session> SessionSet = new CopyOnWriteArraySet<Session>();
private static CloudService cloudService = new CloudServiceImpl();
@PostConstruct
public void init(){
System.out.println("websocket loading");
}
/**
* 连接建立成功调用方法
* @param session
*/
@OnOpen
public void onOpen(Session session){
SessionSet.add(session);
OnlineCount.incrementAndGet(); // 在线数+1
SendMessage(session,"连接成功"); // 告知客户端连接成功
}
/**
* 连接关闭调用方法
*/
@OnClose
public void onClose(Session session){
loginService.logOutAndCleanUp();
SessionSet.remove(session);
OnlineCount.decrementAndGet();
}
/**
* 收到客户端消息后调用的方法
* (这一部分有些丑啊 别骂 别骂)
*/
@OnMessage
public void onMessage(Operator operator, Session session){
SendMessage(session,"接收到消息,消息内容:"+ operator);
System.out.println(operator.toString());
if ("moveLeft".equals(operator.getOperate()))
cloudService.moveLeft(operator.getNChannelID(), operator.getIparam2());
if ("moveRight".equals(operator.getOperate()))
cloudService.moveRight(operator.getNChannelID(), operator.getIparam2());
if ("moveUp".equals(operator.getOperate()))
cloudService.moveUp(operator.getNChannelID(), operator.getIparam2());
if ("moveDown".equals(operator.getOperate()))
cloudService.moveDown(operator.getNChannelID(), operator.getIparam2());
}
/**
* 出现错误
*/
@OnError
public void onError(Session session,Throwable error){
error.printStackTrace();
}
/**
* 发送消息 实践表明 每次浏览器刷新 session会发生变化
*/
public static void SendMessage(Session session,String message){
try {
session.getBasicRemote().sendText(String.format("%s (From Server,Session ID=%s)",message,session.getId()));
session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 群发消息
*/
public static void BroadCastInfo(String message) throws IOException {
for (Session session : SessionSet) {
if (session.isOpen())
SendMessage(session,message);
}
}
/**
* 指定Session发送消息
* @param sessionId
* @param message
* @throws IOException
*/
public static void SendMessage(String message,String sessionId) throws IOException {
Session session = null;
for (Session s : SessionSet) {
if(s.getId().equals(sessionId)){
session = s;
break;
}
}
if(session!=null)
SendMessage(session, message);
}
}
4、编写entity->json的编码类
import com.alibaba.fastjson.JSON;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
/**
* 将JSON编码为entity
*/
public class OperatorEncoder implements Encoder.Text<Operator> {
/**
* 发送编码 entity->json
* @param object
* @return
* @throws EncodeException
*/
@Override
public String encode(Operator object) throws EncodeException {
return JSON.toJSONString(object);
}
@Override
public void init(EndpointConfig endpointConfig) {
}
@Override
public void destroy() {
}
}
5、编写json->entity的解码类
import com.alibaba.fastjson.JSON;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
/**
* 解码 json-> entity映射
*/
public class OperatorDecoder implements Decoder.Text<Operator> {
/**
* 转换json->entity
* @param move
* @return
*/
@Override
public Operator decode(String move) {
return JSON.parseObject(move, Operator.class);
}
/**
* 是否进行转换
* @param s
* @return
*/
@Override
public boolean willDecode(String s) {
return true;
}
@Override
public void init(EndpointConfig endpointConfig) {
}
@Override
public void destroy() {
}
}
至此,即可使用
6、可能出现的疑问或错误
1、空指针
在WebSocket也是这样注入,因 SpringBoot+WebSocket
对每个客户端连接都会创建一个WebSocketServer对象,Bean
注入操作会被直接略过,因而手动注入一个全局变量
2、主动发送消息时如果需要发送entity类型的数据使用
session.getBasicRemote().sendObject();
3、解码时报错无法解析
willDecode()会在decode()之前执行,查看是否被拦截导致无法转换