1. 简介
-
双工异步通信的功能
- sockJs (浏览器不支持webSocket的时候,兼容支持)
- 通常用 STOMP,更高级别的协议
- 使用一个 基于帧(frame)的格式来定义
- @MessageMapping
-
引入pom
spring-boot-starter-websocket
2. 广播式 配置
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
//注册一个 STOMP 协议的节点 endpoint,并映射指定的 URL。和使用 SockJS
//前端 new SockJS ("XXX") ,要用
registry.addEndpoint("/endpointWisely").withSockJS();
registry.addEndpoint("/endpointChat").withSockJS();//1
}
//@SendTo("/topic/getResponse") //服务器有消息时,给浏览器的 这个地址。
//前端 订阅位置的前缀
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue","/topic"); //2
}
}
2.1 浏览器 向 服务端 和 服务端 向 浏览器
public class WiselyMessage {
private String name;
//get
}
public class WiselyResponse {
private String responseMessage;
//有参 构造
//get
}
2.2 演示控制器
@Controller
public class WsController {
@MessageMapping("/welcome") //浏览器 给 后端发消息,用这个地址
@SendTo("/topic/getResponse") //服务器有消息时,给浏览器的 这个地址。
public WiselyResponse say(WiselyMessage message) throws Exception {
Thread.sleep(3000);
return new WiselyResponse("Welcome, " + message.getName() + "!");
}
}
2.3 前端
sockjs.min.js
stomp.min.js
jquery.js
断开连接:
<body onload="disconnect()">
var stompClient = null;
function disconnect() {
if (stompClient != null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
function connect() {
//创建
var socket = new SockJS('/endpointWisely'); //1
//使用 子协议 Stomp
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
//通过client,订阅目标。就是服务器 给后端发消息的。地址。
stompClient.subscribe('/topic/getResponse', function(respnose){ //2
showResponse(JSON.parse(respnose.body).responseMessage);
});
});
}
function sendName() {
var name = $('#name').val();
//3
stompClient.send("/welcome", {}, JSON.stringify({ 'name': name }));
}
3. 点对点
3.1 MVC配置
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter{
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/ws").setViewName("/ws");
registry.addViewController("/login").setViewName("/login");
registry.addViewController("/chat").setViewName("/chat");
}
}
3.2 security
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
//所有的请求都需要权限。/login不用。其他都需要。
//登录,后成功的页面为 chat。退出也是 都有权限。
http
.authorizeRequests()
.antMatchers("/", "/login").permitAll()//1根路径和/login路径不拦截
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login") //2登陆页面
.defaultSuccessUrl("/chat") //3登陆成功转向该页面
.permitAll()
.and()
.logout()
.permitAll();
}
//4
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//内存模式,wyf 和 wisely
auth
.inMemoryAuthentication()
.withUser("wyf").password("wyf").roles("USER")
.and()
.withUser("wisely").password("wisely").roles("USER");
}
//5忽略静态资源的拦截
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/static/**");
}
}
3.3 增加端点
- endpoint
registerStompEndpoints
/endpointChat
- 消息代理
configureMessageBroker
/queue
3.4 控制器
@Autowired
private SimpMessagingTemplate messagingTemplate;//1 向浏览器 发送消息
@MessageMapping("/chat")
public void handleChat(Principal principal, String msg) { //2 principal 包含当前用户的信息。
if (principal.getName().equals("wyf")) {//3 如果发送人是 wyf 则发给 wisely
messagingTemplate.convertAndSendToUser
("wisely","/queue/notifications", principal.getName() + "-send:"+ msg);
} else {
messagingTemplate.convertAndSendToUser //向用户发送消息,第一个参数接收消息的用户。浏览器订阅的地址。消息本身。
("wyf","/queue/notifications", principal.getName() + "-send:"+ msg);
}
}
3.5 前端
<form th:action="@{/login}" method="post">
<div><label> 账号 : <input type="text" name="username"/> </label></div>
<div><label> 密码: <input type="password" name="password"/> </label></div>
<div><input type="submit" value="登陆"/></div>
</form>
$('#wiselyForm').submit(function(e){
e.preventDefault();
var text = $('#wiselyForm').find('textarea[name="text"]').val();
sendSpittle(text);
});
var sock = new SockJS("/endpointChat"); //1 endpoint
var stomp = Stomp.over(sock);
stomp.connect('guest', ' $('#wiselyForm').submit(function(e){
e.preventDefault();
var text = $('#wiselyForm').find('textarea[name="text"]').val();
sendSpittle(text);
});
var sock = new SockJS("/endpointChat"); //1 endpoint
var stomp = Stomp.over(sock);
stomp.connect('guest', 'guest', function(frame) {
stomp.subscribe("/user/queue/notifications", handleNotification);
//2 订阅 /user 下的,user是必须的
});
function handleNotification(message) {
$('#output').append("<b>Received: " + message.body + "</b><br/>")
}
function sendSpittle(text) {
stomp.send("/chat", {}, text);//3
}
$('#stop').click(function() {sock.close()});', function(frame) {
stomp.subscribe("/user/queue/notifications", handleNotification);
//2 订阅 /user 下的,user是必须的
});
function handleNotification(message) {
$('#output').append("<b>Received: " + message.body + "</b><br/>")
}
function sendSpittle(text) {
stomp.send("/chat", {}, text);//3
}
$('#stop').click(function() {sock.close()});
4. 空白项目,放入前端
src/main/resources/static/action.html
src/main/resources/static/bootstrap/css/bootstrap.css
<!DOCTYPE html>
<html lang="zh-cn" ng-app="actionApp">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>实战</title>
<!-- Bootstrap的CSS -->
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="jqueryui/jquery-ui.min.css" rel="stylesheet">
<style type="text/css">
.content {
padding: 100px 15px;
text-align: center;
}
</style>
<!--[if lt IE 9]>
<script src="js/html5shiv.min.js"></script>
<script src="js/respond.min.js"></script>
<![endif]-->
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
</nav>
<script src="js/jquery.min.js"></script>
<script src="jqueryui/jquery-ui.min.js"></script>
<script src="bootstrap/js/bootstrap.min.js"></script>
<script src="js/angular.min.js"></script>
<script src="js/angular-route.min.js"></script>
</body>
</html>
访问
http://localhost:8080/action.html //直接访问即可