EE颠覆者第七章2 webSocket

浏览器和服务端 提供双工异步通信,相互发送数据

通过一个socket来实现通信,直接使用WebSocket开发特别繁琐,我们用它的子协议 STOMP。

使用一个基于帧的格式来定义消息。

		<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>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

application.properties
logging.level.org.springframework.web= DEBUG

@SpringBootApplication
public class Ch76Application {

    public static void main(String[] args) {
        SpringApplication.run(Ch76Application.class, args);
    }
}

webMvcConfig

@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");
	   }

}

广播式

即 服务端有消息时,会将消息发送给所有连接了当前endpoint的浏览器

STOMP 协议来传输基于代理message braker的消息,支持@MessageMapping

WebSocketConfig

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{

	@Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/endpointWisely").withSockJS(); //js new SockJS('/endpointWisely'); 的路径
        registry.addEndpoint("/endpointChat").withSockJS();//1
    }


    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/queue","/topic"); //2 topic 广播式。 queue 点对点式 
    }

}

EnableWebSocketMessageBroker 开启webSocke
集成 AbstractWebSocketMessageBrokerConfigurer 进行配置

定义浏览器 向 服务器 发送的对象

public class WiselyMessage {
    private String name;

    public String getName(){
        return name;
    }
}

定义 服务端 发送给 浏览器的

public class WiselyResponse {
    private String responseMessage;
    public WiselyResponse(String responseMessage){
        this.responseMessage = responseMessage;
    }
    public String getResponseMessage(){
        return responseMessage;
    }
}

控制器

@Controller
public class WsController {

	@MessageMapping("/welcome")//相当于RequestMapping,浏览器 向 服务器发送
	@SendTo("/topic/getResponse") //服务器有消息时,对订阅了@SendTo中的路径的浏览器发送消息
	public WiselyResponse say(WiselyMessage message) throws Exception {
		Thread.sleep(3000);
		return new WiselyResponse("Welcome, " + message.getName() + "!");
	}

	@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);
		}
	}
	
}

公用js

stomp.min.js STOMP协议的客户端脚本

sockjs.min.js SockJS 的客户端脚本

JS 放在 src/main/resources/static/jquery.js 下

演示页面

src/main/resources/templates/ws.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8" />
    <title>Spring Boot+WebSocket+广播式</title>

</head>
<body onload="disconnect()"> //加在的时候关闭
<noscript><h2 style="color: #ff0000">貌似你的浏览器不支持websocket</h2></noscript>
<div>
    <div>
        <button id="connect" onclick="connect();">连接</button>
        <button id="disconnect" disabled="disabled" onclick="disconnect();">断开连接</button>
    </div>
    <div id="conversationDiv">
        <label>输入你的名字</label><input type="text" id="name" />
        <button id="sendName" onclick="sendName();">发送</button>
        <p id="response"></p>
    </div>
</div>
<script th:src="@{sockjs.min.js}"></script>
<script th:src="@{stomp.min.js}"></script>
<script th:src="@{jquery.js}"></script>
<script type="text/javascript">
    var stompClient = null;

    function setConnected(connected) {
        document.getElementById('connect').disabled = connected;
        document.getElementById('disconnect').disabled = !connected;
        document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';
        $('#response').html();
    }
	
    function connect() {
        var socket = new SockJS('/endpointWisely'); //1 连接 SockJs 的 endpoint
        stompClient = Stomp.over(socket); //使用 stomp 子协议
        stompClient.connect({}, function(frame) { //连接webSocket服务端, 
            setConnected(true);
            console.log('Connected: ' + frame); 
            //stompClient 订阅/topic/getResponse,@SendTo 定义
            stompClient.subscribe('/topic/getResponse', function(respnose){ //2
                showResponse(JSON.parse(respnose.body).responseMessage);
            });
        });
    }
	
	
    function disconnect() {
        if (stompClient != null) {
            stompClient.disconnect();
        }
        setConnected(false);
        console.log("Disconnected");
    }

    function sendName() {
        var name = $('#name').val();
   		//3 向 /welcome 发送消息 。与: @MessageMapping 对应
        stompClient.send("/welcome", {}, JSON.stringify({ 'name': name }));
    }

    function showResponse(message) {
          var response = $("#response");
          response.html(message);
    }
</script>
</body>
</html>

http://localhost:8080/ws

一个浏览器 发送消息,其他浏览器也能收到

连接参数:
>>> CONNECT
accept-version:1.1,1.0
heart-beat:10000,10000

连接成功的返回
<<< CONNECTED
version:1.1
heart-beat:0,0
user-name:wyf


订阅目标为:
>>> SUBSCRIBE
id:sub-0
destination:/topic/getResponse

向目标发送为:
>>> SEND
destination:/welcome
content-length:18

{"name":"huahaha"}

从目标 接受为的格式为
<<< MESSAGE
destination:/topic/getResponse
content-type:application/json;charset=UTF-8
subscription:sub-0
message-id:sbt2akvw-7
content-length:39

{"responseMessage":"Welcome, huahaha!"}

点对点式

消息有谁推送,消息有谁接收

引入security

spring-boot-starter-security

配置security

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        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 {
    		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/**");
    }

}

配置websocket(见上面)

控制器(见上面)

登录页面

src/main/resources/templates/login.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<meta charset="UTF-8" />
<head>
    <title>登陆页面</title>
</head>
<body>
<div th:if="${param.error}">
    无效的账号和密码
</div>
<div th:if="${param.logout}">
    你已注销
</div>
<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>
</body>
</html>


聊天页面

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8" />
<head>
    <title>Home</title>
    <script th:src="@{sockjs.min.js}"></script>
    <script th:src="@{stomp.min.js}"></script>
    <script th:src="@{jquery.js}"></script>
</head>
<body>
<p>
    聊天室
</p>

<form id="wiselyForm">
    <textarea rows="4" cols="60" name="text"></textarea>
    <input type="submit"/>
</form>

<script th:inline="javascript">
    $('#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()});
</script>

<div id="output"></div>
</body>
</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值