1、依賴引入?
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2、啟動類添加bean
public class Application {
/**
* 會自動注冊使用了@ServerEndpoint注解聲明的Websocket endpoint
* 要注意,如果使用獨立的servlet容器,
* 而不是直接使用springboot的內置容器,
* 就不要注入ServerEndpointExporter,因為它將由容器自己提供和管理。
*/
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
3、websocket服務創(chuàng)建
1、注解@ServerEndpoint("/client/websocket/{deviceId}")
2、地址參數(shù)與restful 風格一致
3、方法上通過獲取地址參數(shù) @PathParam( value = "deviceId")
4、方法getRemoteAddress() 可以獲取客戶端IP,如果是本機請求 則返回0.0.0.0.0.1
5、只能通過本地緩存對象sessionMap 存儲session信息。
6、如果需要集群、分布式,則使用Nginx 做負載均衡(IP hash)
7、如果需要bean注入其他對象,必須使用構造函數(shù)手動申明SpringUtils.getBean(RedisCache.class);文章來源:http://www.zghlxwxcb.cn/news/detail-830209.html
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
* 與客戶端進行socket通信服務
* @author xuancg
*/
@ServerEndpoint("/client/websocket/{deviceId}")
@Component
@Slf4j
public class ClientSocketService {
private RedisCache redisCache;
private ClientProperites clientProperites;
/** 用于存儲當前服務器連接的socket deviceId-session */
private static Map<Long, Session> sessionMap = new ConcurrentHashMap<>(32);
/**
* 必須通過構造函數(shù)引入bean
*/
public ClientSocketService(){
this.redisCache = SpringUtils.getBean(RedisCache.class);
this.clientProperites = SpringUtils.getBean(ClientProperites.class);
log.info("websocket準備完成");
}
/**
* 連接事件,加入注解
* @param deviceId
* @param session
*/
@OnOpen
public void onOpen(@PathParam( value = "deviceId") Long deviceId, Session session ) {
// 設置消息體最大大小及session空閑時間
int MAX_MESSAGE_SIZE 2 * 1024 * 1024;
session.setMaxTextMessageBufferSize(MAX_MESSAGE_SIZE);
session.setMaxBinaryMessageBufferSize(MAX_MESSAGE_SIZE);
session.setMaxIdleTimeout(1 * 1000 * 60);
log.info("客戶端發(fā)起連接deviceId={}", deviceId);
}
/**
* 連接事件,加入注解
* 用戶斷開鏈接
* 此處不允許執(zhí)行刪除sessionMap操作。由于deviceId 可能是惡意構造,需要做其他參數(shù),
或者請求token 驗證,或者通過接收消息關閉onMessage
* @param deviceId
* @param session
*/
@OnClose
public void onClose(@PathParam ( value = "deviceId") Long deviceId, Session session ) {
log.info("客戶端關閉連接deviceId={}", deviceId);
close(session);
}
/**
* 當接收到用戶上傳的消息
* @param deviceId
* @param session
*/
@OnMessage
public void onMessage(@PathParam ( value = "deviceId") Long deviceId, Session session ,String message) {
log.info("接收客戶端請求 deviceId=,message={}", deviceId, message);
}
/**
* 給單個用戶推送消息
* @param session
* @param message
*/
private void sendMessage(Session session, ClientNotifyResp message){
if(session == null){
return;
}
// 同步
RemoteEndpoint.Async async = session.getAsyncRemote();
async.sendText(JSONUtil.toJsonStr(message));
}
/**
* 處理用戶活連接異常
* @param session
* @param throwable
*/
@OnError
public void onError(Session session, Throwable throwable) {
try {
session.close();
} catch (IOException e) {
e.printStackTrace();
}
throwable.printStackTrace();
}
private void close(Session session){
try {
session.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static String getRemoteAddress(Session session) {
if (session == null) {
return null;
}
RemoteEndpoint.Async async = session.getAsyncRemote();
//在Tomcat 8.0.x版本有效
//InetSocketAddress addr0 = (InetSocketAddress) getFieldInstance(async,"base#sos#socketWrapper#socket#sc#remoteAddress");
//System.out.println("clientIP0" + addr0);
//在Tomcat 8.5以上版本有效
Object obj = getFieldInstance(async, "base#socketWrapper#socket#sc#remoteAddress");
if(null == obj){
return "127.0.0.1";
}
InetSocketAddress addr = (InetSocketAddress) obj;
String ip = addr.toString().replace("/", "");
int idx = ip.lastIndexOf(":");
if(idx > 0){
return ip.substring(0, idx);
}
return ip;
}
private static Object getFieldInstance(Object obj, String fieldPath) {
String fields[] = fieldPath.split("#");
for (String field : fields) {
obj = getField(obj, obj.getClass(), field);
if (obj == null) {
return null;
}
}
return obj;
}
private static Object getField(Object obj, Class<?> clazz, String fieldName) {
for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
try {
Field field;
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(obj);
} catch (Exception e) {
}
}
return null;
}
}
4、攔截器放行
或者添加自定義攔截器文章來源地址http://www.zghlxwxcb.cn/news/detail-830209.html
httpSecurity
// CSRF禁用,因為不使用session TODO
.csrf().disable()
// 認證失敗處理類
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// 基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// 過濾請求
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/client/websocket/**").permitAll()
5、客戶端調用demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Java后端WebSocket的Tomcat實現(xiàn)</title>
</head>
<body>
Welcome<br/><input id="text" type="text"/>
<button onclick="send()">發(fā)送消息</button>
<hr/>
<button onclick="closeWebSocket()">關閉WebSocket連接</button>
<hr/>
<div id="message"></div>
</body>
<script type="text/javascript">
var websocket = null;
//判斷當前瀏覽器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket('ws://localhost:8080/mood-service/client/websocket/200013');
}
else {
alert('當前瀏覽器 Not support websocket')
}
//連接發(fā)生錯誤的回調方法
websocket.onerror = function () {
setMessageInnerHTML("WebSocket連接發(fā)生錯誤");
};
//連接成功建立的回調方法
websocket.onopen = function () {
setMessageInnerHTML("WebSocket連接成功");
}
//接收到消息的回調方法
websocket.onmessage = function (event) {
setMessageInnerHTML(event.data);
}
//連接關閉的回調方法
websocket.onclose = function () {
setMessageInnerHTML("WebSocket連接關閉");
}
//監(jiān)聽窗口關閉事件,當窗口關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉窗口,server端會拋異常。
window.onbeforeunload = function () {
closeWebSocket();
}
//將消息顯示在網(wǎng)頁上
function setMessageInnerHTML(innerHTML) {
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
//關閉WebSocket連接
function closeWebSocket() {
websocket.close();
}
//發(fā)送消息
function send() {
var message = document.getElementById('text').value;
websocket.send(message);
}
</script>
</html>
到了這里,關于Springboot-接入WebSocket服務的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!