什么是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是一个基于tcp的双工协议,分客户端和服务器端,不同于http的协议。作用是使浏览器和站点之间的交互更便利,使实时数据可以传送到或者发送出服务器端。

什么是sockjs

SockJS 是一个浏览器上运行的 JavaScript 库,如果浏览器不支持 WebSocket,该库可以模拟对 WebSocket 的支持,实现浏览器和 Web 服务器之间低延迟、全双工、跨域的通讯通道【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定义了初始化一个websocket请求的规范。当应用启动时,启动一个websocket连接到预配置的url时可以考虑使用WebSocketConnectionManager。

WebSocketConnectionManager在指定uri,WebsocketClient和websocketHandler时通过start()和stop()方法连接到websocket服务器。如果setAutoStartup(boolean)设置为true,spring ApplicationContext刷新时会自动连接。

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通过标准的java websocket api编程式初始化一个连接到websocket服务器的websocket请求。

它有两个私有成员:

    private final WebSocketContainer webSocketContainer;private AsyncListenableTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();

其中,WebSocketContainer使用java本身提供的api获取:

    public StandardWebSocketClient() {this.webSocketContainer = ContainerProvider.getWebSocketContainer();}

其中,SimpleAsyncTaskExecutor为每个task触发一个新的线程来异步的执行。

/*** 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服务器端

注解: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.服务端程序【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();}}

参考文献:

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

转载于:https://www.cnblogs.com/davidwang456/p/5321413.html

spring websocket源码分析相关推荐

  1. spring websocket源码分析续Handler的使用

    1. handler的定义 spring websocket支持的消息有以下几种: 对消息的处理就使用了Handler模式,抽象handler类AbstractWebSocketHandler.jav ...

  2. Spring Cloud源码分析(二)Ribbon(续)

    因文章长度限制,故分为两篇.上一篇:<Spring Cloud源码分析(二)Ribbon> 负载均衡策略 通过上一篇对Ribbon的源码解读,我们已经对Ribbon实现的负载均衡器以及其中 ...

  3. Spring AOP 源码分析 - 拦截器链的执行过程

    1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...

  4. Spring AOP 源码分析 - 创建代理对象

    1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...

  5. Spring AOP 源码分析 - 筛选合适的通知器

    1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析.本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出 ...

  6. spring AOP源码分析(一)

    spring AOP源码分析(一) 对于springAOP的源码分析,我打算分三部分来讲解:1.配置文件的解析,解析为BeanDefination和其他信息然后注册到BeanFactory中:2.为目 ...

  7. 一步一步手绘Spring AOP运行时序图(Spring AOP 源码分析)

    相关内容: 架构师系列内容:架构师学习笔记(持续更新) 一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程) 一步一步手绘Spring IOC运行时序图二(基于XM ...

  8. 精尽Spring Boot源码分析 - 内嵌Tomcat容器的实现

    概述 我们知道 Spring Boot 能够创建独立的 Spring 应用,内部嵌入 Tomcat 容器(Jetty.Undertow),让我们的 jar 无需放入 Servlet 容器就能直接运行. ...

  9. 【Spring】Spring AOP源码分析-导读(一)

    文章目录 1.简介 2.AOP 原理 3.AOP 术语及相应的实现 3.1 连接点 - Joinpoint 3.2 切点 - Pointcut 3.3 通知 - Advice 3.4 切面 - Asp ...

最新文章

  1. 2021年大数据Spark(二十二):内核原理
  2. Python基础知识(第六天)
  3. 【C/C++】实型变量
  4. 泛型实现List(ListT)排序
  5. python标准库sys模块常用函数
  6. 用组策略彻底禁止USB存储设备、光驱、软驱、ZIP软驱
  7. CALayer 新建
  8. VMRC 控制台的连接已断开
  9. 超强合集:OCR 文本检测干货汇总(含论文、源码、demo 等资源)
  10. 百度离线地图API v3.0开发解决方案
  11. 详细图解,一眼就能看懂!卷帘快门(Rolling Shutter)与全局快门(Global Shutter)的区别
  12. python程序memory error_科学网—[转载]解决Python memory error的问题(四种解决方案) - 高琳琳的博文...
  13. T检验三种方法的区分
  14. python可以ps吗_Python功能确实非常强大!不止PS可以美化照片Python也可以!满分...
  15. matlab从无到有系列(五):基本图形图像处理功能
  16. pcfg 自然语言处理_自然语言处理的笔记
  17. 东方时尚网上约车的用户名密码是什么_路由器的登录名与密码忘了怎么解决【解决方法】...
  18. c语言程序设计学籍信息,c语言学籍信息管理系统设计
  19. Qt实现word文档转html
  20. 新通用顶级域名:继根服务器后,又一场互联网空间资源的争夺战

热门文章

  1. su组件在什么窗口_Su与Rhino互导注意事项
  2. python泰勒展开_如何利用sympy对未知函数$f(x)$进行符号泰勒展开
  3. stm32怎么加载字库_收藏 | STM32单片机超详细学习汇总资料(二)
  4. 列表框QListWidget 类
  5. matlab 高斯一阶导,将Matlab高斯导数转换为Opencv
  6. linux体验服务器,体验Ubuntu做服务器
  7. android android studio单元测试实例
  8. pyspark github算例 计算平均数
  9. 风控项目-收集基础知识2
  10. ML+DM顶会时间(2020)