平时我们都是由客户端浏览器主动发送请求到服务端,然后服务器处理请求后返回结果,服务器无法主动向客户端浏览器发送消息。
但是在某些业务场景下我们需要由服务器主动发送消息到客户端浏览器,如当客户用户下订单后,服务器需要主动发消息提醒商户用户有新的订单被创建,请及时处理。在这种需求下我们就需要使用到websocket。
WebSocket是一种在单个TCP连接上进行全双工通信的协议。
下面来看一看websocket的使用示例:
前端代码
创建一个发送消息到服务端的页面sendMessage.html,模拟用户下订单
通过ajax方式发送消息到服务端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
输入需要发送的消息:<input type="text" id="sendText">
<input type="button" id="sendBtn" value="发送消息">
</body>
<script>
var sendText = document.getElementById('sendText');
var sendBtn = document.getElementById('sendBtn');
//按下按钮后通过ajax发送消息到服务端
sendBtn.onclick = function () {
var send = sendText.value;
var xmlHttp = new XMLHttpRequest();
var url = "/sendMessage?msg=" + send;
console.log(url);
xmlHttp.open("get",url,true);
xmlHttp.send();
}
</script>
</html>
创建一个接收服务端消息的页面websocketPage.html,模拟商户接收到消息
这里只是将接收到的消息展示在页面上,也可以做成弹出弹窗、消息图标显示角标等其它形式。
注意:websocket协议的url是以ws开头的而不是http!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
消息展示区:<br/>
<div id="textArea"></div>
</body>
<script>
var textArea = document.getElementById('textArea');
var websocket = null;
//如果浏览器支持websocket就建立一个websocket,否则提示浏览器不支持websocket
if('websocket' in window){
websocketPage = new WebSocket('ws://localhost:8080/websocketurl');
}else{
alert('浏览器不支持websocket!');
}
//建立websocket时自动调用
websocketPage.onopen = function (event) {
console.log('建立连接');
}
//关闭webscoket时自动调用
websocketPage.oncolse = function (event){
console.log('关闭连接');
}
//websocket接收到消息时调用
websocketPage.onmessage = function (event){
//将接收到的消息展示在消息展示区
textArea.innerText += event.data;
textArea.innerHTML += "<br/>";
}
//websocket出错自动调用
websocketPage.onerror = function () {
alert('websocket出错');
}
//关闭窗口前关闭websocket连接
window.onbeforeunload = function (){
websocketPage.close();
}
</script>
</html>
后端代码
在springboot项目中引入websocket依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
创建一个websocket服务点导出器
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebsocketConfig {
@Bean
public ServerEndpointExporter getServerEndpointExporter(){
return new ServerEndpointExporter();
}
}
创建一个websocket服务类
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.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
@Component
@ServerEndpoint("/websocketurl")//设置websocket连接url,就是前端创建websocket需要提供的url
public class MyWebSocketService {
private Session session;
//存放所有的websocket连接
private static CopyOnWriteArraySet<MyWebSocketService> myWebSocketServicesSet = new CopyOnWriteArraySet<>();
//建立websocket连接时自动调用
@OnOpen
public void onOpen(Session session){
this.session = session;
myWebSocketServicesSet.add(this);
System.out.println("有新的websocket连接进入,当前连接总数为" + myWebSocketServicesSet.size());
}
//关闭websocket连接时自动调用
@OnClose
public void onClose(){
myWebSocketServicesSet.remove(this);
System.out.println("连接断开,当前连接总数为" + myWebSocketServicesSet.size());
}
//websocket接收到消息时自动调用
@OnMessage
public void onMessage(String message){
System.out.println("this:" + message);
}
//通过websocket发送消息
public void sendMessage(String message){
for (MyWebSocketService webSocketService : myWebSocketServicesSet){
try {
webSocketService.session.getBasicRemote().sendText(message);
} catch (IOException e) {
System.err.println(this + "发送消息错误:" + e.getClass() + ":" + e.getMessage());
}
}
}
}
创建一个接收创建订单信息的控制器
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import xyz.syyrjx.websockettest.websocketService.MyWebSocketService;
import javax.annotation.Resource;
@Controller
public class MyController {
@Resource
MyWebSocketService webSocketService;
@RequestMapping("/sendMessage")
@ResponseBody
public Object sendMessage(String msg){
webSocketService.sendMessage(msg);
return null;
}
}
规定url和视图的映射关系
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MVCConfiguration implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/websocketPage").setViewName("websocketPage");
registry.addViewController("/sendMessagePage").setViewName("sendMessage");
}
}
启动项目进行测试
启动一个edge浏览器,进入websocketPage页面等待消息,服务端接收到websocket连接,打印当前连接数
启动一个chrome浏览器,进入sendMessagePage页面发送消息
在sendMessagePage页面输入消息并发送,在websocketPage页面就会接收到消息并展示,此时websocketPage没有主动发送请求给服务器,而是服务器主动发送消息给浏览器