什么是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 ChromeInternet ExplorerFirefoxSafari 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:

Work in progress:

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.
*/
@Override
public 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)
*/
@Override
public 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)
*/
@Override
public 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的握手过程:

@Override
protected 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>() {
@Override
public 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 enable
Javascript 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 { @Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
} @Override
public 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/

spring websocket源码分析的更多相关文章

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

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

  2. Spring IOC 源码分析

    Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢?阅读本文 ...

  3. 精尽Spring MVC源码分析 - 调式环境搭建

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  4. Spring Security 源码分析(四):Spring Social实现微信社交登录

    社交登录又称作社会化登录(Social Login),是指网站的用户可以使用腾讯QQ.人人网.开心网.新浪微博.搜狐微博.腾讯微博.淘宝.豆瓣.MSN.Google等社会化媒体账号登录该网站. 前言 ...

  5. spring事务源码分析结合mybatis源码(一)

    最近想提升,苦逼程序猿,想了想还是拿最熟悉,之前也一直想看但没看的spring源码来看吧,正好最近在弄事务这部分的东西,就看了下,同时写下随笔记录下,以备后查. spring tx源码分析 这里只分析 ...

  6. spring AOP源码分析(三)

    在上一篇文章 spring AOP源码分析(二)中,我们已经知道如何生成一个代理对象了,那么当代理对象调用代理方法时,增强行为也就是拦截器是如何发挥作用的呢?接下来我们将介绍JDK动态代理和cglib ...

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

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

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

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

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

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

随机推荐

  1. 未能加载文件或程序集“System.Data.SQLite.DLL”或它的某一个依赖项

    今天在部署code到测试环境的时候 出现了未能加载文件或程序集"System.Data.SQLite.DLL"或它的某一个依赖项 这个错误,其实错误的的原因有很多,1.典型的是是版 ...

  2. Socket原理与编程基础(转)

    一.Socket简介 Socket是进程通讯的一种方式,即调用这个网络库的一些API函数实现分布在不同主机的相关进程之间的数据交换. 几个定义: (1)IP地址:即依照TCP/IP协议分配给本地主机的 ...

  3. 收不到Win10正式版预订通知?一个批处理搞定

    目前,已经有不少Win7.Win8.1用户在系统右下角收到Win10正式版的预订提示窗口.点击接受预订后,系统会将Win10正式版所需的安装文件提前下载好,7月29日正式发布的时候,就可以第一时间升级 ...

  4. Mysql 如何设置字段自动获取当前时间

    应用场景: 1.在数据表中,要记录每条数据是什么时候创建的,不需要应用程序去特意记录,而由数据数据库获取当前时间自动记录创建时间: 2.在数据库中,要记录每条数据是什么时候修改的,不需要应用程序去特意 ...

  5. 关于VS2010出现“此方法显式使用的 CAS 策略已被 .NET Framework 弃用... ...请使用 NetFx40_LegacySecurityPolicy 配置开关”解决办法

    有时候VS会出现“此方法显式使用的 CAS 策略已被 .NET Framework 弃用.若要出于兼容性原因而启用 CAS 策略,请使用 NetFx40_LegacySecurityPolicy 配置 ...

  6. 【转帖】Moodle平台的5个新玩法

    [玩坏Moodle平台]Moodle平台的5个新玩法 1.RSS订阅 Moodle平台可以导入外部博客(或其他提供RSS的服务),并显示在Moodle内置的博客系统中.无论是自己的个人网站还是他人的博 ...

  7. [Javascript] Limit Built Branches on Travis

    By default, Travis will build all branches, tags, and Pull Requests. Because we're building our mast ...

  8. CvMat 矩阵的使用方法和简单程序

    一:CvMat* cvInitMatHeader( CvMat* mat, int rows, int cols, int type,void* data=NULL, int step=CV_AUTO ...

  9. 使用DOSBox在Win7_x64下搭建汇编环境

    1. 软件安装 1. debug.exe,masm.exe,link.exe,edit.com等汇编工具,一般32位的windows系统有自带,但64位系统下并没有,而且将32位系统下的工具拷贝到64 ...

  10. Office 2013 Excel 转换 Word

    最新文章:Virson's Blog 参考文章:百度百科 1.使用Excel打开需要转换的Excel文档: 2.采用另存为*.htm的方式将该Excel文档另存为网页,如下图: 3.找到保存的htm网 ...