spring websocket源码分析
什么是websocket?
摘錄于wiki【1】:
WebSocket?is a protocol providing?full-duplex?communication channels over a single?TCP?connection. The WebSocket protocol was standardized by the?IETF?as?RFC 6455?in 2011, and the WebSocket?API?in?Web IDL?is being standardized by theW3C.
WebSocket is designed to be implemented in?web browsers?and?web servers, but it can be used by any client or server application. The WebSocket Protocol is an independent TCP-based protocol. Its only relationship to?HTTP?is that its?handshake?is interpreted by HTTP servers as an?Upgrade request.[1]?The WebSocket protocol makes more interaction between a browser and a website possible, facilitating the real-time data transfer from and to the server. This is made possible by providing a standardized way for the server to send content to the browser without being solicited by the client, and allowing for messages to be passed back and forth while keeping the connection open. In this way a two-way (bi-directional) ongoing conversation can take place between a browser and the server. The communications are done over TCP?port?number 80, which is of benefit for those environments which block non-web Internet connections using a?firewall. Similar two-way browser-server communications have been achieved in non-standardized ways using stopgap technologies such as?Comet.
The WebSocket protocol is currently supported in most major browsers including?Google Chrome,?Internet Explorer,?Firefox,?Safari?and?Opera. WebSocket also requires web applications on the server to support it.
Unlike HTTP, WebSocket provides full-duplex communication.?Additionally, WebSocket enables streams of messages on top of TCP. TCP alone deals with streams of bytes with no inherent concept of a message. Before WebSocket, port 80 full-duplex communication was attainable using Comet channels; however, Comet implementation is nontrivial, and due to the TCP handshake and HTTP header overhead, it is inefficient for small messages. WebSocket protocol aims to solve these problems without compromising security assumptions of the web.
The WebSocket protocol specification defines?ws?and?wss?as two new uniform resource identifier?(URI) schemes?that are used for unencrypted and encrypted connections, respectively. Apart from the scheme name and fragment (#?is not supported), the rest of the URI components are defined to use?URI generic syntax.
Using the Google Chrome Developer Tools, developers can inspect the WebSocket handshake as well as the WebSocket frames.
簡單來說,websocket是一個(gè)基于tcp的雙工協(xié)議,分客戶端和服務(wù)器端,不同于http的協(xié)議。作用是使瀏覽器和站點(diǎn)之間的交互更便利,使實(shí)時(shí)數(shù)據(jù)可以傳送到或者發(fā)送出服務(wù)器端。
什么是sockjs
SockJS 是一個(gè)瀏覽器上運(yùn)行的 JavaScript 庫,如果瀏覽器不支持 WebSocket,該庫可以模擬對 WebSocket 的支持,實(shí)現(xiàn)瀏覽器和 Web 服務(wù)器之間低延遲、全雙工、跨域的通訊通道【2】。
SockJS family:
- SockJS-client JavaScript client library
- SockJS-node Node.js server
- SockJS-erlang Erlang server
- SockJS-tornado Python/Tornado server
- SockJS-twisted Python/Twisted server
- vert.x Java/vert.x server
Work in progress:
- SockJS-ruby
- SockJS-netty
- SockJS-gevent (SockJS-gevent fork)
- pyramid-SockJS
- wildcloud-websockets
- SockJS-cyclone
- wai-SockJS
- SockJS-perl
- SockJS-go
websocket消息
抽象接口WebSocketMessage:A message that can be handled or sent on a WebSocket connection.
public interface WebSocketMessage<T> {/*** Returns the message payload. This will never be {@code null}.*/T getPayload();/*** Return the number of bytes contained in the message.*/int getPayloadLength();/*** When partial message support is available and requested via* {@link org.springframework.web.socket.WebSocketHandler#supportsPartialMessages()},* this method returns {@code true} if the current message is the last part of the* complete WebSocket message sent by the client. Otherwise {@code false} is returned* if partial message support is either not available or not enabled.*/boolean isLast();}其中,Payload有效信息
?
?
websocket客戶端
抽象接口WebSocketClient定義了初始化一個(gè)websocket請求的規(guī)范。當(dāng)應(yīng)用啟動時(shí),啟動一個(gè)websocket連接到預(yù)配置的url時(shí)可以考慮使用WebSocketConnectionManager。
WebSocketConnectionManager在指定uri,WebsocketClient和websocketHandler時(shí)通過start()和stop()方法連接到websocket服務(wù)器。如果setAutoStartup(boolean)設(shè)置為true,spring ApplicationContext刷新時(shí)會自動連接。
ConnectionManagerSupport是WebSocketConnectionManager的基類
/*** Start the websocket connection. If already connected, the method has no impact.*/@Overridepublic final void start() {synchronized (this.lifecycleMonitor) {if (!isRunning()) {startInternal();}}}protected void startInternal() {synchronized (lifecycleMonitor) {if (logger.isDebugEnabled()) {logger.debug("Starting " + this.getClass().getSimpleName());}this.isRunning = true;openConnection();}}protected abstract void openConnection();
我們來看看StandardWebSocketClient怎么工作的?
?StandardWebSocketClient通過標(biāo)準(zhǔn)的java websocket api編程式初始化一個(gè)連接到websocket服務(wù)器的websocket請求。
?它有兩個(gè)私有成員:
private final WebSocketContainer webSocketContainer;private AsyncListenableTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();其中,WebSocketContainer使用java本身提供的api獲取:
?
public StandardWebSocketClient() {this.webSocketContainer = ContainerProvider.getWebSocketContainer();}?
其中,SimpleAsyncTaskExecutor為每個(gè)task觸發(fā)一個(gè)新的線程來異步的執(zhí)行。
/*** Executes the given task, within a concurrency throttle* if configured (through the superclass's settings).* @see #doExecute(Runnable)*/@Overridepublic void execute(Runnable task) {execute(task, TIMEOUT_INDEFINITE);}/*** Executes the given task, within a concurrency throttle* if configured (through the superclass's settings).* <p>Executes urgent tasks (with 'immediate' timeout) directly,* bypassing the concurrency throttle (if active). All other* tasks are subject to throttling.* @see #TIMEOUT_IMMEDIATE* @see #doExecute(Runnable)*/@Overridepublic void execute(Runnable task, long startTimeout) {Assert.notNull(task, "Runnable must not be null");if (isThrottleActive() && startTimeout > TIMEOUT_IMMEDIATE) {this.concurrencyThrottle.beforeAccess();doExecute(new ConcurrencyThrottlingRunnable(task));}else {doExecute(task);}}/*** Template method for the actual execution of a task.* <p>The default implementation creates a new Thread and starts it.* @param task the Runnable to execute* @see #setThreadFactory* @see #createThread* @see java.lang.Thread#start()*/protected void doExecute(Runnable task) {Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));thread.start();}接下來,我們看一下StandardWebSocketClient的握手過程:
@Overrideprotected ListenableFuture<WebSocketSession> doHandshakeInternal(WebSocketHandler webSocketHandler,HttpHeaders headers, final URI uri, List<String> protocols,List<WebSocketExtension> extensions, Map<String, Object> attributes) {int port = getPort(uri);InetSocketAddress localAddress = new InetSocketAddress(getLocalHost(), port);InetSocketAddress remoteAddress = new InetSocketAddress(uri.getHost(), port);final StandardWebSocketSession session = new StandardWebSocketSession(headers,attributes, localAddress, remoteAddress);final ClientEndpointConfig.Builder configBuilder = ClientEndpointConfig.Builder.create();configBuilder.configurator(new StandardWebSocketClientConfigurator(headers));configBuilder.preferredSubprotocols(protocols);configBuilder.extensions(adaptExtensions(extensions));final Endpoint endpoint = new StandardWebSocketHandlerAdapter(webSocketHandler, session);Callable<WebSocketSession> connectTask = new Callable<WebSocketSession>() {@Overridepublic WebSocketSession call() throws Exception {webSocketContainer.connectToServer(endpoint, configBuilder.build(), uri);return session;}};if (this.taskExecutor != null) {return this.taskExecutor.submitListenable(connectTask);}else {ListenableFutureTask<WebSocketSession> task = new ListenableFutureTask<WebSocketSession>(connectTask);task.run();return task;}}?websocket服務(wù)器端
?注解:EnableWebSocketMessageBroker
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(DelegatingWebSocketMessageBrokerConfiguration.class) public @interface EnableWebSocketMessageBroker {}注解:EnableWebSocket
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(DelegatingWebSocketConfiguration.class) public @interface EnableWebSocket { }示例程序
1.客戶端【3】index.html
<!DOCTYPE html> <html> <head><title>Hello WebSocket</title><script src="sockjs-0.3.4.js"></script><script src="stomp.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';document.getElementById('response').innerHTML = '';}function connect() {var socket = new SockJS('/hello');stompClient = Stomp.over(socket);stompClient.connect({}, function(frame) {setConnected(true);console.log('Connected: ' + frame);stompClient.subscribe('/topic/greetings', function(greeting){showGreeting(JSON.parse(greeting.body).content);});});}function disconnect() {if (stompClient != null) {stompClient.disconnect();}setConnected(false);console.log("Disconnected");}function sendName() {var name = document.getElementById('name').value;stompClient.send("/app/hello", {}, JSON.stringify({ 'name': name }));}function showGreeting(message) {var response = document.getElementById('response');var p = document.createElement('p');p.style.wordWrap = 'break-word';p.appendChild(document.createTextNode(message));response.appendChild(p);}</script> </head> <body onload="disconnect()"> <noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being enabled. Please enableJavascript and reload this page!</h2></noscript> <div><div><button id="connect" onclick="connect();">Connect</button><button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button></div><div id="conversationDiv"><label>What is your name?</label><input type="text" id="name" /><button id="sendName" onclick="sendName();">Send</button><p id="response"></p></div> </div> </body> </html>2.服務(wù)端程序【3】WebSocketConfig.java:
package hello;import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry;@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {@Overridepublic void configureMessageBroker(MessageBrokerRegistry config) {config.enableSimpleBroker("/topic");config.setApplicationDestinationPrefixes("/app");}@Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {registry.addEndpoint("/hello").withSockJS();}}?
參考文獻(xiàn):
【1】https://en.wikipedia.org/wiki/WebSocket
【2】http://www.oschina.net/p/sockjs/similar_projects?lang=0&sort=time&p=1
【3】https://spring.io/guides/gs/messaging-stomp-websocket/
轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/p/5321413.html
總結(jié)
以上是生活随笔為你收集整理的spring websocket源码分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux文件查找工具之find “大宝
- 下一篇: windows dos命令窗口的环境变量