深入淺出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");


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