WebSocket简单来讲就是在客户端与服务端之间建立一个长连接,实现端对端的直接通信,连接保持期间可以双发互相发送数据(双工通信)
WebSocket测试网站:http://www.websocket-test.com/
一、基本使用
下面三步照着做就可以了,主要需要自己根据要求定制的再第三步
第一步:引入WebSocket依赖
<!--websocket-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
第二步:配置WebSocketConfig
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
第三步:创建WebSocketServer
主要需要自己根据项目修改的内容,其他基本不需要修改:
①websocket接口@ServerEndpoint(value = “/ws/{userId}”)和对应的路径参数的使用
②onMessage中的接收客户端请求信息的内容
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.len.bmbhome.client.*;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import org.bmbhome.service.UserDataService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
@Slf4j
//自行设置连接接口的url,下方接收的参数userId也可以自行修改
@ServerEndpoint(value = "/ws/{userId}")
@Component
public class WebSocketServer {
private Long userId;
private Session session;
//记录所有的WebSocketServer
private static CopyOnWriteArrayList<WebSocketServer> webSocketSet = new CopyOnWriteArrayList<>();
public Session getSession() {
return this.session;
}
public Long getUserId(){
return this.userId;
}
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam(value = "userId") Long userId) {
log.info("有连接加入:" + userId);
this.userId = userId;
this.session = session;
webSocketSet.add(this);
}
/**
* 收到客户端消息后调用的方法
*/
@OnMessage
public void onMessage(String message, Session session) {
try {
log.info("来自客户端的消息:{}", message);
//添加一些自己接收到客户端发送的数据的一些处理
} catch (Exception e) {
log.error("接收用户消息出错了:", e);
}
}
/**
* 连接关闭时调用的方法
*/
@OnClose
public void OnClose() {
log.info("退出用户userId" + this.userId);
webSocketSet.remove(this);
}
/**
* 向指定的session发送数据
*/
private void sendMessage(Session session, String message) {
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
log.error("消息发送失败", e);
}
}
/**
* 群发消息,即对每一个session都进行发送
*/
private void sendMessageToAll(String message) throws IOException {
for (WebSocketServer webSocketServer : webSocketSet) {
webSocketServer.getSession().getBasicRemote().sendText(message);
}
}
/**
* 向指定userId用户发送信息
* 利用遍历的方法,判断userId是否匹配
*/
private void sendMessgaeByUserId(Long userId,String message) throws IOException {
for (WebSocketServer webSocketServer : webSocketSet) {
if(webSocketServer.getUserId() == userId){
webSocketServer.getSession().getBasicRemote().sendText(message);
}
}
}
/**
* 主动断开连接
*/
public void close() throws IOException {
//主动断开连接
this.session.close();
this.onClose();
}
}
补充:
①客户端也要通过实现websocket才能进行通信,而不是使用原始的http协议
②sendMessage可以发送map、collection等等数据,但是要通过先转json,再转string。
客户端接收到string后,转换为json
以发送Map数据为例
Map<String,Object> map = new HashMap<>();
map.put("userName","lnz");
map.put("userId","199");
map.put("gsr","10");
sendMessage(this.session,JSONObject.toJSON(map).toString());
二、实战案例
实例测试:
①服务端从客户端接收用户的各种健康数据(步数、皮肤电等等),并保存到数据库中
②服务端向客户端发送数据
传递JSON格式数据{”type":“xx”,xxx},利用onMessage中switch来判断type进行接收还是发送数据
type = 2、3时,进行保存数据到服务器
type=100时,进行服务器向客户端发送数据
package org.bmbhome.config;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.len.bmbhome.client.*;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import org.bmbhome.service.UserDataService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
@Slf4j
@ServerEndpoint(value = "/ws/{userId}")
@Component
public class WebSocketServer {
private Long userId;
private Session session;
//记录所有的WebSocketServer
// 因为同一个userId可能有多个接入所以不可以使用Map<userId,WebSocketServer>或Map<userId,Session>这样的容器
//这边可以自行设计存储的容器,但注意要为static
private static CopyOnWriteArrayList<WebSocketServer> webSocketSet = new CopyOnWriteArrayList<>();
private static UserDataService userDataService;
@Autowired
public void setUserDataService(UserDataService userDataService) {
WebSocketServer.userDataService = userDataService;
}
public Session getSession() {
return this.session;
}
public Long getUserId(){
return this.userId;
}
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam(value = "userId") Long userId) {
log.info("有连接加入:" + userId);
this.userId = userId;
this.session = session;
webSocketSet.add(this);
}
/**
* 收到客户端消息后调用的方法
*/
@OnMessage
public void onMessage(String message, Session session) {
try {
log.info("来自客户端的消息:{}", message);
int type = JSON.parseObject(message).getInteger("type");
System.out.println(type);
JSONObject jsonObject = JSONObject.parseObject(message);
switch (type) {
/**
* 步数统计
* json格式: {"type":"2",data:"xxx"}
*/
case 2:
MessageDTO<StepDTO> stepDTOMessageDTO = new MessageDTO<>();
stepDTOMessageDTO.setType(jsonObject.getInteger("type"));
StepDTO stepDTO = new StepDTO();
stepDTO.setCount(jsonObject.getLong("data"));
stepDTOMessageDTO.setData(stepDTO);
System.out.println(stepDTOMessageDTO.getType());
System.out.println(stepDTOMessageDTO.getData());
userDataService.saveUserStep(userId, stepDTOMessageDTO);
break;
/**
* 皮肤电数据信息
* json格式: {"type":"3","data":"xxx"}
*/
case 3:
GsrDTO gsrDTO = new GsrDTO();
gsrDTO.setSensorValue(jsonObject.getInteger("data"));
userDataService.saveUserGsr(userId, gsrDTO);
System.out.println(gsrDTO);
break;
/**
* 用户请求数据
* 测试服务器向客户端发送数据
* json格式: {"type":"100"}
*/
case 100:
sendMessage(this.session,"恭喜你以及获得到数据");
sendMessgaeByUserId(3L,"3号你很容易被选中");
Map<String,Object> map = new HashMap<>();
map.put("userName","lnz");
map.put("userId","199");
map.put("gsr","10");
sendMessage(this.session,JSONObject.toJSON(map).toString());
break;
default:
log.error("未知消息类型");
}
} catch (Exception e) {
log.error("接收用户消息出错了:", e);
}
}
@OnClose
public void OnClose() {
log.info("退出用户userId" + this.userId);
webSocketSet.remove(this);
}
/**
* 向指定的session发送数据
*/
private void sendMessage(Session session, String message) {
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
log.error("消息发送失败", e);
}
}
/**
* 群发消息,即对每一个session都进行发送
*/
private void sendMessageToAll(String message) throws IOException {
for (WebSocketServer webSocketServer : webSocketSet) {
webSocketServer.getSession().getBasicRemote().sendText(message);
}
}
/**
* 向指定userId用户发送信息
* 利用遍历的方法
*/
private void sendMessgaeByUserId(Long userId,String message) throws IOException {
for (WebSocketServer webSocketServer : webSocketSet) {
if(webSocketServer.getUserId() == userId){
webSocketServer.getSession().getBasicRemote().sendText(message);
}
}
}
}
请求服务器发送数据测试:
向服务器发送数据测试: