目錄:
css
一,Spring Boot集成WebSockethtml
二,聲明WebSocket服務,發送接收消息前端
三,JavaScript建立WebSocket客戶端java
四,增長SocketController,返回前端頁面jquery
五,Spring Boot集成TioWebSocketgit
六,常見問題和解決方法github
WebSocket是創建在TCP協議上的全雙工通訊鏈接。不一樣於HTTP只能有客戶端發送請求消息,WebSocket在客戶端和後臺服務創建鏈接後,雙方均可以主動發送實時消息。web
在不少業務場景下,後臺服務須要實時將數據推送到客戶端,好比代駕定位系統,司機客戶端定時發送位置信息到服務器,後臺管理系統頁面實時更新顯示,這時就要使用WebSocket數據推送方式,並且不增長服務器負載壓力。spring
項目下載:https://github.com/jextop/StarterApi瀏覽器
示例代碼:https://github.com/rickding/HelloJava/tree/master/HelloSocket
代碼文件 |
功能要點 |
|
Spring Boot集成WebSocket |
pom.xml |
引入WebSocket依賴:spring-boot-starter-websocket |
SocketConfig.java |
配置Bean,啓動WebSocketServer |
|
SocketServer.java |
聲明WebSocket服務,接收和發送消息 |
|
JavaScript建立WebSocket客戶端 |
socket.html |
在前端頁面中接收和發送消息 |
前端頁面 |
SocketController.java |
返回前端頁面 |
一,Spring Boot集成WebSocket
1. 新建Spring Boot項目時,選中WebSocket,將自動添加依賴。
2. 已有Spring Boot項目,能夠在pom.xml中直接引用WebSocket Starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
3. 增長SocketConfig.java,啓動WebSocketServer
@Configuration
public class SocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
二,聲明WebSocket服務,接收和發送消息
1,增長SocketServer.java,聲明服務地址
2,聲明@OnMessage,接收消息
3,聲明@OnOpen,客戶端鏈接成功時,將session保存起來
4,聲明@OnClose,客戶端斷開鏈接時,將session信息刪除
5,封裝sendMessage()函數,經過session推送消息到客戶端
@ServerEndpoint("/ws/{uid}")
@Component
public class SocketServer {
private static ConcurrentHashMap<String, SocketServer> webSocketMap = new ConcurrentHashMap<>();
private Session session;
private String uid;
public static void sendMessage(String uid, String msg) {
System.out.printf("Send message: %s, %s\n", uid, msg);
if (StringUtils.isNotBlank(uid) && webSocketMap.containsKey(uid)) {
webSocketMap.get(uid).sendMessage(msg);
} else {
System.err.printf("Offline: %s\n", uid);
}
}
public void sendMessage(String msg) {
try {
session.getBasicRemote().sendText(msg);
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
@OnMessage
public void onMessage(String msg, Session session) {
System.out.printf("Receive message: %s, %s, %s\n", uid, session.getId(), msg);
for (String uid : webSocketMap.keySet()) {
sendMessage(uid, String.format("%s消息: %s", uid.equals(this.uid) ? "本身" : "轉發", msg));
}
}
@OnOpen
public void onOpen(Session session, @PathParam("uid") String uid) {
this.session = session;
this.uid = uid;
webSocketMap.put(uid, this);
System.out.printf("Online: %d, %s\n", webSocketMap.size(), uid);
sendMessage("鏈接成功");
}
@OnClose
public void onClose() {
webSocketMap.remove(uid);
System.out.printf("Offline 1, %s, online: %d\n", uid, webSocketMap.size());
}
}
三,JavaScript建立WebSocket客戶端
1,判斷瀏覽器是否支持WebSocket
2,建立客戶端,鏈接服務器
3,增長消息接收函數
4,增長髮送消息功能
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>HelloSocket</title>
</head>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
var socket;
function openSocket() {
if (typeof(WebSocket) === "undefined") {
$("#status").html("瀏覽器不支持WebSocket");
} else {
if (socket !== undefined) {
socket.close();
socket = undefined;
}
// 創建鏈接
var socketUrl = "ws://localhost:8080/ws/" + $("#uid").val();
socket = new WebSocket(socketUrl);
socket.onmessage = function (msg) {
$("#status").html(msg.data);
};
}
}
function sendMessage() {
if (socket === undefined) {
$("#status").html("請先鏈接Socket");
} else {
socket.send($("#msg").val() + ", " + new Date().getTime());
}
}
</script>
<body>
${msg}
<br/>uid: <div><input id="uid" name="uid" value="user_id"></div>
<br/>msg: <div><input id="msg" name="msg" value="Hello WebSocket"></div>
<br/>
<div>
<button onclick="openSocket()">鏈接Socket</button>
</div>
<br/>
<div>
<button onclick="sendMessage()">發送消息</button>
</div>
<br/>
<div><label id="status">Status</label></div>
</body>
</html>
四,增長SocketController,返回前端頁面
@Controller
public class SocketController {
@RequestMapping("/ws")
public String ws(Model model){
model.addAttribute("msg","Web Socket!");
return "socket";
}
}
啓動項目,瀏覽器打開頁面,鏈接WebSocket服務,接收和發送消息。
五,Spring Boot集成TioWebSocket
tio-websocket是基於T-io 網絡通信框架實現的開源websocket服務器,易於使用,沒必要關心鏈接的維護問題,只要作好業務處理便可。
代碼文件 |
功能要點 |
|
Spring Boot集成TioWebSocket |
pom.xml |
引入TioWebSocket依賴:tio-websocket-spring-boot-starter |
application.xml |
配置端口、超時等屬性 |
|
TioMsgHandler.java |
實現IWsMsgHandler,接收和發送消息 |
|
JavaScript建立WebSocket客戶端 |
tio.html |
在前端頁面中接收和發送消息 |
前端頁面 |
SocketController.java |
返回前端頁面 |
1,在pom.xml中引入依賴
<dependency>
<groupId>org.t-io</groupId>
<artifactId>tio-websocket-spring-boot-starter</artifactId>
<version>3.6.0.v20200315-RELEASE</version>
</dependency>
2,在application.xml中配置端口、超時等屬性
tio:
websocket:
server:
port: 8200
heartbeat-timeout: 3600000
3,增長TioMsgHandler.java,實現接收和發送消息功能
@Component
public class TioMsgHandler implements IWsMsgHandler {
@Autowired
private TioWebSocketServerBootstrap tioServer;
public void sendMessage(String msg) {
Tio.sendToAll(tioServer.getServerTioConfig(), WsResponse.fromText(msg, "utf-8"));
}
@Override
public Object onText(WsRequest wsRequest, String msg, ChannelContext channelContext) throws Exception {
System.out.printf("收到文本消息:%s\n", msg);
sendMessage(String.format("轉發消息: %s", msg));
return String.format("收到消息: %s", msg);
}
@Override
public Object onBytes(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception {
System.out.printf("收到二進制數據:%d\n", bytes.length);
return null;
}
}
4,開發前端頁面,JavaScript建立WebSocket客戶端
5,SocketController.java返回前端頁面
6,啓動項目,看到WebSocket服務信息
六,常見問題和解決方法
l 運行單元測試時錯誤,不能建立Bean 'serverEndpointExporter'
IllegalStateException: javax.websocket.server.ServerContainer not available
解決:聲明@SpringBootTest時指定webEnvironment參數。
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
緣由:WebSocket運行在servlet容器中,因此須要加載servlet容器。
webEnvironment爲Spring Boot指定啓動時的ApplicationContext, SpringBootTest.WebEnvironment.DEFINED_PORT表示內嵌的服務器將會在定義的端口啓動。
l 運行單元測試時不能啓動,Failed to load ApplicationContext
Web server failed to start. Port 8011 was already in use.
解決:服務端口被佔用,若是項目已經啓動,先停掉再運行測試。