WebSocket
WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
实例
实例要求: 
- Http协议是无状态的, 浏览器和服务器间的请求响应一次,下一次会重新创建连接.
- 要求:实现基于webSocket的长连接的全双工的交互
- 改变Http协议多次请求的约束,实现长连接了, 服务器可以主动发送消息给浏览器
- 客户端浏览器和服务器端会相互感知,比如服务器关闭了,浏览器会感知,同样浏览器关闭了,服务器会感知。
服务端

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | pipeline.addLast(new HttpServerCodec());
 
 pipeline.addLast(new ChunkedWriteHandler());
 
 
 pipeline.addLast(new HttpObjectAggregator(8192));
 
 
 
 
 
 
 pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 
 | public class MyServer {
 public static void main(String[] args) {
 
 NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
 NioEventLoopGroup workerGroup = new NioEventLoopGroup(8);
 
 try {
 ServerBootstrap bootstrap = new ServerBootstrap();
 bootstrap.group(bossGroup, workerGroup)
 .channel(NioServerSocketChannel.class)
 .handler(new LoggingHandler(LogLevel.INFO))
 .childHandler(new ChannelInitializer<SocketChannel>() {
 
 @Override
 protected void initChannel(SocketChannel ch) throws Exception {
 ChannelPipeline pipeline = ch.pipeline();
 
 pipeline.addLast(new HttpServerCodec());
 
 pipeline.addLast(new ChunkedWriteHandler());
 
 
 pipeline.addLast(new HttpObjectAggregator(8192));
 
 
 
 
 
 
 pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));
 
 
 pipeline.addLast(new MyTextWebSocketFrameHandler());
 }
 });
 
 
 ChannelFuture channelFuture = bootstrap.bind(7000).sync();
 
 channelFuture.channel().closeFuture().sync();
 
 } catch (InterruptedException e) {
 e.printStackTrace();
 } finally {
 bossGroup.shutdownGracefully();
 workerGroup.shutdownGracefully();
 }
 }
 }
 
 | 
自定义处理器

以TextWebSocketFrame文本桢的形式交互
channel的ID有两种形式
- ctx.channel().id().asLongText()唯一
 
- ctx.channel().id().asShortText()不唯一
 
客户端页面
HTML5 WebSocket:https://www.runoob.com/html/html5-websocket.html

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 
 | <!DOCTYPE html><html lang="en">
 <head>
 <meta charset="UTF-8">
 <title>Title</title>
 </head>
 <body>
 <script>
 var socket;
 
 if(window.WebSocket) {
 
 socket = new WebSocket("ws://localhost:7000/hello");
 
 socket.onmessage = function (ev) {
 var rt = document.getElementById("responseText");
 rt.value = rt.value + "\n" + ev.data;
 }
 
 
 socket.onopen = function (ev) {
 var rt = document.getElementById("responseText");
 rt.value = "连接开启了.."
 }
 
 
 socket.onclose = function (ev) {
 
 var rt = document.getElementById("responseText");
 rt.value = rt.value + "\n" + "连接关闭了.."
 }
 } else {
 alert("当前浏览器不支持websocket")
 }
 
 
 function send(message) {
 if(!window.socket) {
 return;
 }
 if(socket.readyState == WebSocket.OPEN) {
 
 socket.send(message)
 } else {
 alert("连接没有开启");
 }
 }
 </script>
 <form onsubmit="return false">
 <textarea name="message" style="height: 300px; width: 300px"></textarea>
 <input type="button" value="发生消息" onclick="send(this.form.message.value)">
 <textarea id="responseText" style="height: 300px; width: 300px"></textarea>
 <input type="button" value="清空内容" onclick="document.getElementById('responseText').value=''">
 </form>
 </body>
 </html>
 
 | 
测试
启动服务器
 
 
访问页面

发送消息
 )
)
服务器可以发送消息给浏览器
关闭页面,断开连接(这里服务器我重启了ID所以不一样)。

关闭服务器

客户端浏览器和服务器端会相互感知,比如服务器关闭了,浏览器会感知,同样浏览器关闭了,服务器会感知。
细节
WebSocket服务器通过101状态码切换协议由http->ws。
