WebSocket
为什么使用进阶版呢? 新的技术肯定是解决了 技术的问题
Http协议都的缺陷 :
通信只能由客户端发起,需要一种服务端能够主动推送到能力 –websocket
**websocket:**这是一种双向通信的能力 也叫做 : “全双工”
websocket是由浏览器发起的
通常我们使用 http叫做 : 短连接 性能好一点
用 websocket 叫做 : 长连接 复用性高一些
适用于不同场景
这里我们还是使用 http来链连接 只不过 连接之后就传输信息 直到断开为止
应为本质上 http 底层是 tcp 是支持实现全双工的
协议标识符 http://127.0.0.1:8080 ws://127.0.0.1:7777
GET ws://127.0.0.1:7777 HTTP/1.1 Host: 127.0.0.1 Upgrade: websocket # 升级为ws Connection: Upgrade # 此链接需要升级 Sec-WebSocket-key: client-random-string ... # 标识加密相关信息
响应结果
HTTP/1.1 101 Upgrade: websocket Connection: Upgrade
响应码 101 代表本次协议需要更改为websocket
连接建立后,支持文本信息及二进制信息。
Websocket实现的原理:
通过http协议进行连接的建立(握手和回答),建立连接后不再使用http,而tcp自身是支持双向通信的,所以能达到“全双工”的效果。
Websocket 应用demo
服务端代码
public class WebSocketServer { public static void main(String[] args) { //可以自定义线程的数量 EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 默认创建的线程数量 = CPU 处理器数量 *2 EventLoopGroup workerGroup = new NioEventLoopGroup(); ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler()) //当前连接被阻塞的时候,BACKLOG代表的事 阻塞队列的长度 .option(ChannelOption.SO_BACKLOG, 128) //设置连接保持为活动状态 .childOption(ChannelOption.SO_KEEPALIVE, true) .childHandler(new WebSocketInitialzer()); try { ChannelFuture future = serverBootstrap.bind(7777).sync(); future.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
服务端初始化器
public class WebSocketInitialzer extends ChannelInitializer<Channel> { @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); //增加编解码器 的另一种方式 pipeline.addLast(new HttpServerCodec()); // 块方式写的处理器 适合处理大数据 pipeline.addLast(new ChunkedWriteHandler()); //聚合 pipeline.addLast(new HttpObjectAggregator(512 * 1024)); /* * 这个时候 我们需要声明我们使用的是 websocket 协议 * netty为websocket也准备了对应处理器 设置的是访问路径 * 这个时候我们只需要访问 ws://127.0.0.1:7777/hello 就可以了 * 这个handler是将http协议升级为websocket 并且使用 101 作为响应码 * */ pipeline.addLast(new WebSocketServerProtocolHandler("/hello")); pipeline.addLast(new WebSocketHandler()); } }
服务端处理器
通信使用的单位叫帧 frame
客户端:发送时将消息切割成多个帧
服务端:接收时,将关联的帧重新组装
/* * 泛型 代表的是处理数据的单位 * TextWebSocketFrame : 文本信息帧 * */ public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> { @Override protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame textWebSocketFrame) throws Exception { //可以直接调用text 拿到文本信息帧中的信息 System.out.println("msg:" + textWebSocketFrame.text()); Channel channel = ctx.channel(); //我们可以使用新建一个对象 将服务端需要返回的信息放入其中 返回即可 TextWebSocketFrame resp = new TextWebSocketFrame("hello client from websocket server"); channel.writeAndFlush(resp); } }
websocket前端编写
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script> var socket; // 判断当前浏览器是否支持websocket if (!window.WebSocket) { alert("不支持websocket") } else { <!-- 创建websocket 连接对象--> socket = new WebSocket("ws://127.0.0.1:7777/hello"); //设置开始连接的方法 socket.onopen = function (ev) { var tmp = document.getElementById("respText"); tmp.value = "连接已经开启"; } //设置关闭连接的方法 socket.onclose = function (ev) { var tmp = document.getElementById("respText"); tmp.value = tmp.value + "\n" + "连接已经关闭"; } //设置接收数据的方法 socket.onmessage = function (ev) { var tmp = document.getElementById("respText"); tmp.value = tmp.value + "\n" + ev.data; } } function send(message) { if (!window.socket) { return } /* * 判断socket的状态 * connecting 正在连接 closing 正在关闭 * closed 已经关闭或打开连接失败 * open 连接可以 已经正常通信 * */ if (socket.readyState == WebSocket.OPEN) { socket.send(message); } else { alert("连接未开启"); } } </script> <!--防止表单自动提交--> <form onsubmit="return false"> <textarea name="message" style="height: 400px;width: 400px"></textarea> <input type="button" value="发送" onclick="send(this.form.message.value)"> <textarea id="respText" style="height: 400px;width: 400px"></textarea> </form> </body> </html>
【客户端】
var ws = new WebSocket("ws://127.0.0.1:7777/hello"); ws.onopen = function(ev){ ws.send("hello"); //建立连接后发送数据 }
设计一个样式
左右两个各有一个文本框,中间放一个发送按钮。
左侧文本框用来发送数据,右侧文本框用来显示数据。
演示效果
启动服务发送消息
服务器接受到的消息
小结
websocket 一般用于做可复用连接,http一般做短链接
websocket解决了http连接只能客户端发起的痛点