转载请注明出处:http://blog.csdn.net/llew2011/article/details/53056165
做Java开发的小伙伴们应该对Socket比较熟悉,在J2SE的Socket编程这一章节中专门对Socket通信做了详细介绍,当时自学完该章节后只知道Socket是端到端通信的,Server端根据指定端口打开Socket链接,然后等待客户端来连接;客户端根据Server端IP地址和端口创建一个Socket通道,根据该通道和Server端进行通信。后来在工作中有使用Socket通信,使用场景是聊天和推送,当时为了项目进度就在GitHub上找了一个不错的开源库autobahn-java中应用在项目中,功能实现之后并没有继续深入理解WebSocket协议。恰好现在项目又使用到了WebSocket通信,因此决定仔细研究一下Socket通信的底层相关知识并记录下来,希望能给小伙伴们一点帮助,如果你对Socket变成非常熟悉了,请跳过本文(*^__^*) ……
背景
以前,很多网站为了实现推动技术,所用的技术都是轮训(例如:AJax)。轮训是在特定的时间间隔(例如每隔5秒),由浏览器对服务器发送HTTP请求,然后服务器收到浏览器请求后把相关最新数据返回客户端浏览器(注意:也可能没有最新数据)。这种传统的轮训模式带来很明显的缺点,即浏览器需要不断的向服务器发出HTTP请求,然而HTTP请求可能包含较长的头部,其真正有效的数据可能只是很小的一部分,显然这样会浪费很多的宽带等资源;假如某次请求时服务器端并没有最新数据需要返回给客户端,那么这次请求等同于无效的,那么怎么才能实现客户端能及时的收到服务器端最新数据又能占用很少的网络宽带资源呢?
在这种情况下,HTML5定义了WebSocket协议,该协议是一种在单个TCP连接上进行全双工通讯的协议,IETF(Internet Engineering Task Force,互联网工程小组)在2011年把这套协议定为RFC 6455标准,并被RFC7936所补充规范。因此WebSocket API 也被W3C订为标准。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务器端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要要完成一个握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
根据WebSocket的概念,我们可以总结以下两点:
那么什么是全双工通信了?
通信方式可以分为两种,一种是半双工通信,一种是全双工通信。半双工通信指的是在某一个时间点上只有发送数据或者接收数据发生这一个行为发生。而全双工通信是相对于半双工通信来讲的,全双工通信允许发送数据和接收数据同时发生。
WebSocket使用ws或者是wss的统一资源标识符,类似于HTTP和HTTPS,其中wss表示在TLS之上的WebSocket,如下所示:
ws://127.0.0.1:80/wsapi wss://127.0.0.1:443/wsapiWebSocket如果没有运行在TLS之上时默认使用和HTTP相同的80端口,当运行在TLS之上时,默认使用443端口。
WebSocket的协议内容详见The WebSocket Protocol,这是最全面的官方说明,另外维基百科也有对WebSocket做了简单说明。WebSocket的适用场景可以是即时聊天,推送或者是直播中的弹幕等。
为了清楚WebSocket协议内容,先上一张图:
上图是一个数据帧,WebSocket就是按照该协议规则进行传输数据的。
void mask(byte[] origin, byte[] maskKey) { if (null == origin || null == maskKey) return; int length = origin.length; for (int i = 0; i < length; i++) { origin[i] = (byte) (origin[i] ^ maskKey[i % 4]); // 按位异或运算,相同为0,不同为1 } }
以上就是WebSocket协议规定的数据传输的帧内容,大致了解一下即可。除此之外,WebSocket协议还有一个握手的过程,通过握手发送一个HTTP请求来完成,这里基本和HTTP2.0有点类似,客户端发送一个请求协议升级的get请求给服务端,服务端支持的话会返回HTTP Code 为101的状态码,表示可以切换到对应的协议,大致流程如下:
客户端发送Get请求:
GET /chat HTTP/1.1 Host: example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-key: Sec-WebSocket-Version: 13
客户端给服务端发送握手协议包,包的报文格式必须符合HTTP报文规范,其中:
HTTP/1.1 101 "Switching Protocols" Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept:服务器端收到客户端的请求后,返回给客户端的报文必须满足一下规范:
在客户端收到服务器端发送的报文后,首先验证报文格式是否符合规范,如果符合规范则继续验证Sec-WebSocket-Accept的值,当Sec-WebSocket-Accept的值验证通过则WebSocket连接建议,否则只要其中任何一步验证不通过就不能建立WebSocket连接。
为了更清楚的了解WebSocket请求流程,网上有许多在线的WebSocket测试网站,百度一下"WebSocket在线测试"就会有许多测试网站,我们选择第一个测试即可,使用Chrome浏览器(注意:没有安装Chrome浏览器使用其他浏览器也可,只要浏览器上带有开发者工具选项即可),打开该页面,如下所示:
该测试网页有连接和断开的按钮,不用说你也明白这是测试左侧给出的测试地址,当然你也可以输入其他测试地址。这时候打开Chrome的开发者工具,选择Network选项,然后选中下边的WS选项,表示监控所有的WebSocket连接,如下图所示:
打开Chrome的开发者选项之后,这时候你点击左侧的连接按钮,就会有对该此请求的拦截,如下图所示:
然后点击拦截的IP地址,如下图所示:
上图展示了拦截当前请求的Headers的内容,我们先看一下General部分,General部分表示整体的请求和响应流程,详细解释如下所示:
了解了Headers的General部分后,我们再来看一下Request Headers部分,部分说明如下所示:
最后是Response Headers内容,如下所示: