環(huán)境:Springboot3.0.5
WebSocket協(xié)議RFC 6455提供了一種標(biāo)準(zhǔn)化的方式,通過(guò)一個(gè)TCP連接在客戶端和服務(wù)器之間建立全雙工、雙向的通信通道。它是一個(gè)不同于HTTP的TCP協(xié)議,但設(shè)計(jì)為在HTTP之上工作,使用80和443端口,并允許重用現(xiàn)有的防火墻規(guī)則。
WebSocket交互開(kāi)始于一個(gè)HTTP請(qǐng)求,使用HTTP Upgrade header進(jìn)行升級(jí),在本例中是切換到WebSocket協(xié)議。下面的例子展示了這種交互:
GET /spring-websocket-portfolio/portfolio HTTP/1.1Host: localhost:8080Upgrade: websocket // ①Connection: Upgrade // ②Sec-WebSocket-Key: Uc9l9TMkWGbHFD2qnFHltg==Sec-WebSocket-Protocol: v10.stomp, v11.stompSec-WebSocket-Version: 13Origin: http://localhost:8080
①:Upgrade header頭部信息
②:使用 Upgrade 連接
支持WebSocket的服務(wù)器會(huì)返回類似下面的輸出,而不是通常的200狀態(tài)碼:
HTTP/1.1 101 Switching Protocols Upgrade: websocketConnection: UpgradeSec-WebSocket-Accept: 1qVdfYHU9hPOl4JYYNXF623Gzn0=Sec-WebSocket-Protocol: v10.stomp
握手成功后,HTTP upgrade請(qǐng)求的TCP套接字保持打開(kāi),客戶端和服務(wù)器可以繼續(xù)發(fā)送和接收消息。
如果WebSocket服務(wù)器運(yùn)行在web服務(wù)器(例如nginx)后面,你可能需要配置它來(lái)將WebSocket升級(jí)請(qǐng)求傳遞給WebSocket服務(wù)器。同樣,如果應(yīng)用程序運(yùn)行在云環(huán)境中,請(qǐng)查看云提供商提供的有關(guān)WebSocket支持的說(shuō)明。
盡管WebSocket在設(shè)計(jì)上是與HTTP兼容的,而且從HTTP請(qǐng)求開(kāi)始,但重要的是要明白,這兩種協(xié)議導(dǎo)致了非常不同的架構(gòu)和應(yīng)用程序編程模型。
在HTTP和REST中,應(yīng)用程序被建模為多個(gè)url。為了與應(yīng)用程序交互,客戶端以請(qǐng)求-響應(yīng)的方式訪問(wèn)這些url。服務(wù)器根據(jù)HTTP URL、方法和首部將請(qǐng)求路由到適當(dāng)?shù)奶幚沓绦颉?span style="display:none">SS828資訊網(wǎng)——每日最新資訊28at.com
相比之下,在websocket中,初始連接通常只有一個(gè)URL。隨后,所有應(yīng)用程序消息都在同一個(gè)TCP連接上流動(dòng)。這是一種完全不同的異步、事件驅(qū)動(dòng)的消息傳遞架構(gòu)。
WebSocket也是一種底層傳輸協(xié)議,與HTTP不同,它對(duì)消息內(nèi)容沒(méi)有任何語(yǔ)義規(guī)定。這意味著除非客戶端和服務(wù)器在消息語(yǔ)義上達(dá)成一致,否則無(wú)法路由或處理消息。
WebSocket客戶端和服務(wù)器可以通過(guò)HTTP握手請(qǐng)求的Sec-WebSocket-Protocol頭部來(lái)協(xié)商使用更高級(jí)別的消息傳遞協(xié)議(例如STOMP)。在這種情況下,他們需要制定自己的慣例。
WebSockets可以使網(wǎng)頁(yè)具有動(dòng)態(tài)性和交互性。然而,在許多情況下,Ajax和HTTP流或長(zhǎng)輪詢的組合可以提供簡(jiǎn)單而有效的解決方案。
例如,新聞、郵件和社交源需要?jiǎng)討B(tài)更新,但每隔幾分鐘更新一次完全沒(méi)問(wèn)題。另一方面,協(xié)作、游戲和金融應(yīng)用需要更接近實(shí)時(shí)。
延遲本身并不是決定性因素。如果消息量相對(duì)較少(例如監(jiān)視網(wǎng)絡(luò)故障),HTTP流或輪詢可以提供有效的解決方案。低延遲、高頻率和高容量的組合才是WebSocket的最佳選擇。
還要記住,在互聯(lián)網(wǎng)上,你無(wú)法控制的限制性代理可能會(huì)阻止WebSocket交互,要么是因?yàn)樗鼈儧](méi)有配置為傳遞Upgrade header,要么是因?yàn)樗鼈冴P(guān)閉了看起來(lái)空閑的長(zhǎng)連接。這意味著對(duì)防火墻內(nèi)的內(nèi)部應(yīng)用程序使用WebSocket比面向公眾的應(yīng)用程序更直接。
Spring框架提供了一個(gè)WebSocket API,可以用它來(lái)編寫(xiě)處理WebSocket消息的客戶端和服務(wù)器端應(yīng)用程序。
創(chuàng)建WebSocket服務(wù)器很簡(jiǎn)單,只需實(shí)現(xiàn)WebSocketHandler,或者擴(kuò)展TextWebSocketHandler或BinaryWebSocketHandler。下面的例子使用了TextWebSocketHandler:
public class MessageHandler extends TextWebSocketHandler { @Override public void handleTextMessage(WebSocketSession session, TextMessage message) { System.out.printf("SessionId: %s, 接收到消息: %s%n", session.getId(), message.getPayload()) ; try { session.sendMessage(new TextMessage("服務(wù)端接收到消息 - " + message.getPayload())) ; } catch (IOException e) { e.printStackTrace(); } } @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { System.out.printf("連接成功, 會(huì)話Id: %s, Attribute: %s%n", session.getId(), session.getAttributes()) ; } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { System.out.printf("連接關(guān)閉, 會(huì)話Id: %s, 關(guān)閉狀態(tài): %s%n", session.getId(), status.getCode() + " - " + status.getReason()) ; }}
WebSocket配置
@Configuration@EnableWebSocketpublic class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(messageHandler(), "/message") } @Bean public WebSocketHandler messageHandler() { return new MessageHandler(); }}
要定制初始的HTTP WebSocket握手請(qǐng)求,最簡(jiǎn)單的方法是使用HandshakeInterceptor,它提供了握手前和握手后的方法。你可以使用這樣的攔截器來(lái)阻止握手,或者讓 WebSocketSession可以訪問(wèn)任何屬性。下面的例子使用內(nèi)置的攔截器將HTTP會(huì)話屬性傳遞給WebSocket會(huì)話:
@Configuration@EnableWebSocketpublic class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry .addHandler(messageHandler(), "/message") .setHandshakeHandler(handshakeHandler()) // 添加捂手?jǐn)r截器 .addInterceptors(new HandshakeInterceptor() { // 如果該方法返回false,則不允許建立連接 @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { // todo attributes.put("uid", uid) ; return true ; } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { // todo } }) ; }}
Spring WebSocket API很容易集成到Spring MVC應(yīng)用程序中,DispatcherServlet可以同時(shí)處理HTTP WebSocket握手和其他HTTP請(qǐng)求。調(diào)用
WebSocketHttpRequestHandler也很容易集成到其他HTTP處理場(chǎng)景中。這樣既方便又容易理解。但是,對(duì)于JSR-356運(yùn)行時(shí),需要特別注意。
Java WebSocket API (JSR-356)提供兩種部署機(jī)制。第一種方法涉及啟動(dòng)時(shí)的Servlet容器類路徑掃描(Servlet 3特性)@ServerEndpoint。另一個(gè)是Servlet容器初始化時(shí)使用的注冊(cè) API(ServletContainerInitializer)。這兩種機(jī)制都不可能對(duì)所有HTTP處理使用單個(gè)“前端控制器”?—?包括WebSocket握手和所有其他HTTP請(qǐng)求?—?如Spring MVC的DispatcherServlet。
這是JSR-356的一個(gè)重要限制,Spring的WebSocket支持通過(guò)特定于服務(wù)器的RequestUpgradeStrategy實(shí)現(xiàn)來(lái)解決這個(gè)問(wèn)題,即使運(yùn)行在JSR-356運(yùn)行時(shí)也是如此。Tomcat、Jetty、GlassFish、WebLogic、WebSphere和Undertow(以及WildFly)目前都存在這樣的策略。
每個(gè)底層WebSocket引擎都公開(kāi)了控制運(yùn)行時(shí)特征的配置屬性,例如消息緩沖區(qū)大小、空閑超時(shí)等。
對(duì)于Tomcat、WildFly和GlassFish,可以在WebSocket Java配置中添加
ServletServerContainerFactoryBean,如下面的例子所示:
@Beanpublic ServletServerContainerFactoryBean servletServerContainerFactoryBean() { ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean() ; container.setMaxTextMessageBufferSize(8192) ; container.setMaxBinaryMessageBufferSize(8192) ; return container ;}
從Spring Framework 4.1.5開(kāi)始,WebSocket和SockJS的默認(rèn)行為是只接受同源請(qǐng)求。也可以允許所有或指定的來(lái)源列表。這個(gè)檢查主要是為瀏覽器客戶端設(shè)計(jì)的。沒(méi)有什么能阻止其他類型的客戶端修改Origin首部值。
三種可能的行為是:
@Configuration@EnableWebSocketpublic class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry .addHandler(messageHandler(), "/message") .setAllowedOriginPatterns("*") ; }}
通過(guò)上面的介紹和配置,WebSocket環(huán)境就算是簡(jiǎn)單的配置完成了,接下來(lái)通過(guò)Postman進(jìn)行測(cè)試。
圖片
連接成功
發(fā)送消息及接收消息
服務(wù)端接收到消息
本文鏈接:http://www.www897cc.com/showinfo-26-5136-0.htmlSpringBoot整合WebSocket詳解
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com