深入浅出boot2.0 第13章 websocket我的整理


websocket

浏览器 与 服务器  全双工 通信
没有实现该协议的,用 STOMP协议来

引用:org.springframework.boot
spring-boot-starter-websocket

1. 定义服务端点的配置类

@Configuration
public class WebSocketConfig {
	@Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

2.定义服务端点的位置

@ServerEndpoint("/ws") //创建服务端点 地址为/ws
@Service
public class WebSocketServiceImpl {

    private static CopyOnWriteArraySet<WebSocketServiceImpl> 
            webSocketSet = new CopyOnWriteArraySet<>();
    
    private Session session;
    
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        webSocketSet.add(this);     // 加入set中
         sendMessage("有新的连接加入了!!");
    }

    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  // 从set中删除
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        // 群发消息
        for (WebSocketServiceImpl item : webSocketSet) {
                item.sendMessage(message); 
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }

    private void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
}

}

@OnMessage 就是收到客户端发的消息,
干嘛了呢?遍历所有的socket使用
session.getBasicRemote().sendText(message);
进行了群发。

3. 前端代码。需要引用 jquery-3.2.1.min.js或任意版本,可以请求引用 src="https://code.jquery.com/jquery-3.2.1.min.js"
var websocket = null;
if ('WebSocket' in window) {
websocket = new WebSocket("ws://localhost:8080/ws");
} else {
alert('Not support websocket')
}

websocket.onerror=XX 
websocket.onopen=XX 
websocket.onmessage = function(event) {
	appendMessage(event.data);
}
websocket.onclose = function() {
	appendMessage("close");
}

// 监听窗口关闭事件,主动去关闭websocket连接,
window.onbeforeunload = function() {
	websocket.close();
}
// 关闭连接
function closeWebSocket() {
	websocket.close();
}

// 显示
function appendMessage(message) {
	var context = $("#context").html() +"<br/>" + message;
	$("#context").html(context);
}
// 发送消息
function sendMessage() {
	var message = $("#message").val();
	websocket.send(message);
}

创建连接地址:
new WebSocket("ws://localhost:8080/ws");
发送消息:
websocket.send(message);
会发到后台点的@OnMessage方法。




使用 STOMP
浏览器不支持WebSocket协议,使用其子协议STOMP

1. 启用子协议。配置端点路径(/socket很重要)。
2. 配置客户端路径前缀。服务端请求前缀。
@Configuration
@EnableWebSocketMessageBroker //启用STOMP协议
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/socket").withSockJS(); //也可以支持sockJS
    }
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        // 客户端订阅路径前缀
        registry.enableSimpleBroker("/sub");
        // 服务端点请求前缀
        registry.setApplicationDestinationPrefixes("/request");
    }
}


3. Controller配置请求路径send。加上上面的配置即为:/request/send
  和 前端要监控的路径 (/sub/chat) 。sub与上面配置一样。
@Controller
@RequestMapping("/websocket")
public class WebSocketController {
    // 定义消息请求路径
    @MessageMapping("/send")
    // 定义结果发送到特定路径
    @SendTo("/sub/chat")
    public String sendMsg(String value) {
         return value;
    }
}

4. 前端js的发送端:需要引用jquery 和 sockjs.min.js
(可以请求引用 src="https://cdn.jsdelivr.net/sockjs/1/sockjs.min.js")。
和stomp.min.js ,不可请求引用(因为请求引用了就变成文本了)

var stompClient = null;
function connect() {
	var socket = new SockJS('/socket');

	stompClient = Stomp.over(socket);

	stompClient.connect({}, function(frame) {
	});
}

function disconnect() {
		if (stompClient != null) {
			stompClient.disconnect();
		}
}

function sendMsg() {
		var value = $("#message").val();
		stompClient.send("/request/send", {}, value);
}

connect();

核心代码会,创建socketJs
var socket = new SockJS('/socket');
使用stomp协议
stompClient = Stomp.over(socket);
发送消息:
stompClient.send("/request/send", {}, value);
会请求到后台@MessageMapping("/send")


5. 前端接收的js

var noticeSocket = function() {

        var s = new SockJS('/socket');

        var stompClient = Stomp.over(s);

        stompClient.connect({}, function() {

            stompClient.subscribe('/sub/chat', function(data) {

                $('#receive').html(data.body);

            });
        });
    };
    noticeSocket();

接收也要创建 stompClient,代码和路径同样。
发送的js只定义了往后台发,而接收端,则定义了 等待后台发消息的路径。
 stompClient.subscribe('/sub/chat', 函数)
很显然,这个路径与后台的:    @SendTo("/sub/chat") 匹配。


stomp指定用户发送消息:


sockJS 是一个 第三方关于 支持 WebSocket请求的 Js 框架
boot 提供 SimpMessaging Template对象

1.配置
在创建一个服务端点
 registry.addEndpoint("/wsuser").withSockJS();
客户端的订阅路径也创建一个(原来只有一个sub)
 registry.enableSimpleBroker("/sub", "/queue");

2.引入security,并配置2个用户
extends WebSecurityConfigurerAdapter
重写方法:
configure(AuthenticationManagerBuilder auth)

        PasswordEncoder pe= new BCryptPasswordEncoder();
	// 可以通过 pe.encode("p1")这样获得加密后的密码

        auth.inMemoryAuthentication().passwordEncoder(pe)

            .withUser("user1") .password("XXX").roles("USER")
	.and()
            .withUser("user2").password("XXX").roles("ADMIN");



3. controler 路径
  @Autowired 
    private SimpMessagingTemplate simpMessagingTemplate;

    @MessageMapping("/sendUser")
    public void sendToUser(Principal principal, String body) {
        String srcUser = principal.getName();
        String []args = body.split(",");
        String desUser = args[0];
        String message =  srcUser + "发来消息:" + args[1];
        // 发送到用户和监听地址
        simpMessagingTemplate.convertAndSendToUser(desUser, 
            "/queue/customer", message);    
    }

/queue 与配置订阅路径对应



4. js发送端
    var stompClient = null;
    // 开启socket连接 
function connect() {
    // 连接/wsuser服务端点
        var socket = new SockJS('/wsuser');
        // stomp客户端
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function(frame) {
            
        });
    }
    // 断开socket连接
    function disconnect() {
        if (stompClient != null) {
            stompClient.disconnect();
        }

    }
    // 向‘/request/sendUser’服务端发送消息
    function sendMsg() {
        var value = $("#message").val();
        var user = $("#user").val();
        // 用户和消息组成的字符串
        var text = user +"," + value;
        stompClient.send("/request/sendUser", {}, text);
    }
    connect();

代码和上个几乎一样。
只是:获取了 消息 和用户名。用逗号拼接,发送到
        stompClient.send("/request/sendUser", {}, text);
服务端的前缀依然没变/request
与后台的 @MessageMapping("/sendUser")对应
后台用 String body 接收了 text,并且拆分 ,然后得到用户名进行发送。

messagingTemplate 发送需要3个参数,至此都有了。
simpMessagingTemplate.convertAndSendToUser(desUser, 
            "/queue/customer", message);    
下面看这个接收地址:/customer


5.  js接收端
	var noticeSocket = function() {
		var s = new SockJS('/wsuser');
		var stompClient = Stomp.over(s);
		stompClient.connect({}, function() {
			stompClient.subscribe('/user/queue/customer', function(data) {
				$('#receive').html(data.body);
			});
		});
	};
	noticeSocket();
代码按照后台指定的位置接收消息,但是需要自己加路径 /user

由此我们可以发现,客户端是可以没必要配置的,为了更加简洁,便注释了此代码。
 registry.enableSimpleBroker("/sub", "/queue");


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章