前言:
使用websocket的原因,是因为项目中会出现无法服务器调用前端的情况,基于此TCP长连接协议,所以用了ws协议封装了一层,流反向调用。
使用websocket 前端需要连接对应的服务域名,域名替换为ws,打到集群主机。
前端方法
function openSocket() {
// 建立长连接
var socketUrl="http://localhost:8080/demo/ws_server/"+$("#userId").val();
socket = new WebSocket(socketUrl);
socketUrl=socketUrl.replace("https","ws").replace("http","ws");
// 自动打开前后端长连接事件
socket.onopen = function() {
console.log("websocket已打开");
};
// 自动收到后端消息事件
socket.onmessage = function(msg) {
console.log("websocket获得消息",msg.data);
};
// 后端服务关闭自动关闭事件
socket.onclose = function() {
console.log("websocket已关闭");
};
//发生了错误事件
socket.onerror = function() {
console.log("websocket发生了错误");
}
}
后端代码:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
注册使用bean 用来扫描端点
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
配置端点
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
// 和前端对应的域名加端点 一致
@ServerEndpoint("/ws_server/{userId}")
@Component
@Slf4j
public class WebSocketServer {
/**用来存放每个客户端对应的MyWebSocket对象。*/
private static ConcurrentHashMap<String,WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
/**与某个客户端的连接会话*/
private Session session;
/**接收userId*/
private String userId="";
/**
* 连接建立成功调用的方法
* */
@OnOpen
public void onOpen(Session session,@PathParam("userId") String userId) {
this.session = session;
this.userId=userId;
if(webSocketMap.containsKey(userId)){
webSocketMap.remove(userId);
webSocketMap.put(userId,this);
//加入set中
}else{
webSocketMap.put(userId,this);
//在线数加1
addOnlineCount();
}
/**
* 服务器主动推送到客户端
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/**
* 收到客户端消息后 --- 自动调用的方法
*
* @param message 客户端发送过来的消息*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("用户消息:"+userId+",报文:"+message);
}
/**
* 客户端关闭 -- 连接关闭自动回调的方法
*/
@OnClose
public void onClose() {
if(webSocketMap.containsKey(userId)){
webSocketMap.remove(userId);
//从set中删除
subOnlineCount();
}
log.info("用户退出:"+userId+",当前在线人数为:" + getOnlineCount());
}
}
总结
基于连接可以做鉴权,前提必须有同一个域名,ws协议下可以长连接,其他的连接配置参数可以在后端校验。完成连接后,后端可以发送消息到前端,用于消息传输,但是websocket是不稳定的,netty有稳定版本的。
可能会出现传输乱码问题,需要对http消息进行转换,网上copy了一份
/**
* 添加fastjson的转换
*/
@Configuration
public class FastjsonConverter {
@Bean
public HttpMessageConverters customConverters() {
// 定义一个转换消息的对象
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
// 添加fastjson的配置信息 比如 :是否要格式化返回的json数据
FastJsonConfig fastJsonConfig = new FastJsonConfig();
//fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
List<MediaType> fastMediaTypes = new ArrayList<MediaType>();
// 处理中文乱码问题1
// 处理中文乱码问题2
fastJsonConfig.setCharset(Charset.forName("UTF-8"));
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
//fastMediaTypes.add(MediaType.valueOf("text/plain;charset=UTF-8"));
//fastMediaTypes.add(MediaType.valueOf("text/html;charset=UTF-8"));
fastConverter.setSupportedMediaTypes(fastMediaTypes);
// 在转换器中添加配置信息
fastConverter.setFastJsonConfig(fastJsonConfig);
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
stringConverter.setDefaultCharset(Charset.forName("UTF-8"));
stringConverter.setSupportedMediaTypes(fastMediaTypes);
// 将转换器添加到converters中
return new HttpMessageConverters(stringConverter,fastConverter);
}
}
出现乱码的原因很简单就是前后端字符编码不同, 场景的如服务端返回 ISO-8859-1,而客户端的编码默认是UTF-8
解决的办法就是让服务端返回的结果的编码与客户端的编码保持一致 最直接有效的方法是在request的header中增加一个项 Accept:application/json;charset=UTF-8
或者后端解码