前言:
1、使用netty-socketio搭建socketIo服務;
2、client連接配接socketIo服務時,儲存下該client對象;
3、使用TestController,向指定client推送消息;
一、netty-socket.io版本;
<dependency>
<groupId>com.corundumstudio.socketio</groupId>
<artifactId>netty-socketio</artifactId>
<version>1.7.16</version>
</dependency>
二、建立socketIoServer執行個體;
/**
* 建立socketIOServer執行個體
* @return
*/
@Bean
public SocketIOServer socketIOServer() {
com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();
config.setHostname(host);
config.setPort(port);
setAuthorizationListener(config);
return new SocketIOServer(config);
}
三、socketIo client連接配接校驗說明;
設想是通過服務端頒發token給client頁面,當client頁面進行連接配接時,将服務端頒發的token傳遞回來。服務端通過client傳遞的一系列參數,取出服務端儲存的token,與傳遞的token做驗證。
本文未做上述邏輯校驗,僅是将client傳遞的參數列印在控制台。
/**
* 設定socketio client連接配接時的安全校驗
* @param config
*/
private void setAuthorizationListener(com.corundumstudio.socketio.Configuration config) {
config.setAuthorizationListener(new AuthorizationListener() {
@Override
public boolean isAuthorized(HandshakeData data) {
String userId = data.getSingleUrlParam("userId");
String pageSign = data.getSingleUrlParam("pageSign");
String token = data.getSingleUrlParam("token");
System.out.println("userId:" + userId + ",pageSign:" + pageSign + ",token: " + token);
return true;
}
});
}
四、啟用netty-socket.io注解功能;
隻有打開了SpringAnnotationScanner,netty-socket.io的注解才會生效。
/**
* 開啟netty socketio的注解功能
* @param socketServer
* @return
*/
@Bean
public SpringAnnotationScanner springAnnotationScanner(SocketIOServer socketServer) {
return new SpringAnnotationScanner(socketServer);
}
五、通過校驗的client連接配接儲存下來;
儲存client的時候,辨別是哪個使用者在哪個頁面發起的連接配接,以便後續給指定使用者的指定頁面發送消息。注意:未處理同一個使用者同時在多個浏覽器登入的情況(可将token作為連接配接辨別的一部分)。
@OnConnect
public void onConnect(SocketIOClient client) {
this.socketClientComponent.storeClientId(client);
System.out.println("用戶端連接配接:" + getParamsFromClient(client));
}
六、client斷開連接配接時,從已連接配接池中移除掉;
@OnDisconnect
public void onDisconnect(SocketIOClient client) {
this.socketClientComponent.delClientId(client);
System.out.println("用戶端斷開:" + getParamsFromClient(client));
}
七、client儲存說明;
有效client目前是根據userId+pageSign作為連接配接唯一辨別,将client對象儲存在本地緩存中。本地緩存有撐爆的可能,正确的做法應該是同一個使用者在同一個浏覽器登入中,不管在多少個頁面下,使用的是同一個socket。根據不同eventName事件名稱來區分不同頁面。服務端通過socketIoClient的sessionId來擷取到真實的socketIoClient對象(socketIOServer.getClient(client.getSessionId()))。
/**
* socketio client 操作元件
* @author haishui211
*/
@Component
public class SocketClientComponent {
private Map<String, SocketIOClient> clients = new HashMap<String, SocketIOClient>();
/**
* 儲存socketio client 用戶端
* @param userId
* @param client
*/
public void storeClientId(SocketIOClient client) {
clients.put(getKeyFromClient(client), client);
}
/**
* 移除socketio client 用戶端
*/
public void delClientId(SocketIOClient client) {
clients.remove(getKeyFromClient(client));
}
/**
* 給指定client發送指定事件的資料
* @param businessName
* @param data
*/
public void send(String userId, String pageSign, String businessName, Map<String, Object> data) {
SocketIOClient client = clients.get(getKey(userId, pageSign));
if(client != null) {
client.sendEvent(businessName, data);
}
}
private String getKeyFromClient(SocketIOClient client) {
HandshakeData data = client.getHandshakeData();
String userId = data.getSingleUrlParam("userId");
String pageSign = data.getSingleUrlParam("pageSign");
return getKey(userId, pageSign);
}
private String getKey(String userId, String pageSign) {
return "userId:" + userId + ":pageSign:" + pageSign;
}
}
八、testController模拟消息發送;
@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
private SocketClientComponent socketClientComponent;
@PostMapping("/push")
public void push(@RequestBody Map<String, Object> data) {
String eventName = (String) data.get("eventName");
String userId = (String) data.get("userId");
String pageSign = (String) data.get("pageSign");
socketClientComponent.send(userId, pageSign, eventName, data);
}
}
九、頁面用戶端;
1、引用的Js;
<script
src="http://code.jquery.com/jquery-3.4.1.js"
integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU="
crossorigin="anonymous"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
2、連接配接發起;
var socket = io.connect('http://localhost:9999?userId=12311&pageSign=firstPage&token=1234');
socket.on('connect', function() {
output('<span class="connect-msg">成功連接配接到服務端!</span>');
});
socket.on('testEvent', function(data) {
output(JSON.stringify(data));
});
socket.on('disconnect', function() {
output('<span class="disconnect-msg">The client has disconnected!</span>');
});
十、測試資料;
{
"hhhh":"1234",
"eventName":"testEvent",
"pageSign":"firstPage",
"userId":"12311"
}
結果:

十一、demo位址;
https://github.com/haishui211/springRep.git
項目名:nettySocketIoDemo001,html頁面放在了static目錄