热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

NettyWebSocket协议

HTTP协议的弊端HTTP协议为半双工协议.半双工协议指数据可以在客户端和服务端两个方向上传输,但是不能同时传输.它意味这同一时刻,只有一个方向上的数据传输;客户端发送请求,服务器

HTTP 协议的弊端

  1. HTTP 协议为半双工协议. 半双工协议指数据可以在客户端和服务端两个方向上传输, 但是不能同时传输. 它意味这同一时刻, 只有一个方向上的数据传输; 客户端发送请求, 服务器等待, 直到收到完整的请求. 然后发送回应, 客户端和服务器无法同时发送.
  2. HTTP 消息冗长而繁琐. HTTP 消息包含消息头、消息头、换行符等, 通常情况下采用文本方式传输, 相比于其他的二进制通信协议, 冗长而繁琐;
  3. 针对服务器推送的黑客攻击. 例如长时间轮询.

WebSocket 入门

webSocket 是 HTML5 开始提供的一种浏览器于服务器间进行全双工通信的技术.

在 WebSocket API 中, 浏览器和服务器只需要做一个握手的动作, 然后, 浏览器和服务器之间就形成了一条快速通道, 两者就可以直接相互传送数据了. WebSocket 基于 TCP 双向全双工进行消息传递, 在同一时刻, 既可以发送消息, 也可以接收消息, 相比 HTTP 的半双工协议, 性能得到很大提升.

WebSocket 的特点:

  • 单一的 TCP 连接, 采用全双工模式通信;
  • 对代理、防火墙和路由器透明;
  • 无头部信息、COOKIE和身份验证;
  • 无安全开销;
  • 通过 ping/pong 帧保持链路激活;
  • 服务器可以主动传递消息给客户端, 不再需要客户端轮询.

WebSocket 连接建立

建立 webSocket 连接时, 需要通过客户端或浏览器发出握手请求, 类似下面的 http 报文.

clipboard.png

这个请求和通常的 HTTP 请求不同, 包含了一些附加头信息, 其中附加头信息 Upgrade:WebSocket 表明这是一个申请协议升级的 HTTP 请求.

服务器解析这些附加的头信息, 然后生成应答信息返回给客户端, 客户端和服务端的 WebSocket 连接就建立起来了, 双方可以通过这个连接通道自由的传递信息, 并且这个连接会持续存在直到客户端或服务端的某一方主动关闭连接.

服务端返回给客户端的应答消息, 类似如下报文

clipboard.png

请求消息中的 Sec-WebSocket-Key 是随机的, 服务端会用这些数据来构造出一个 SHA-1 的信息摘要, 把 Sec-WebSocket-Key 加上一个魔幻字符串 258EAFA5-E914-47DA-95CA-C5AB0DC85B11. 使用 SHA-1 加密, 然后进行 BASE-64 编码, 将结果做为 Sec-WebSocket-Accept 头的值, 返回给客户端.

WebSocket 生命周期

握手成功之后, 服务端和客户端就可以通过 messages 的方式进行通讯, 一个消息由一个或多个帧组成.

帧都有自己对应的类型, 属于同一个消息的多个帧具有相同类型的数据. 从广义上讲, 数据类型可以是文本数据(UTF-8文字)、二进制数据和控制帧(协议级信令, 例如信号).

WebSocket 连接生命周期如下:

clipboard.png

Netty WebSocket 协议开发

示例代码

public class TimeServer {public void bind(int port) throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024).handler(new LoggingHandler(LogLevel.DEBUG)).childHandler(new ChildChannelHandler());// 绑定端口, 同步等待成功ChannelFuture f = b.bind(port).sync();// 等待服务端监听端口关闭f.channel().closeFuture().sync();} finally {System.out.println("shutdownGracefully");bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}private class ChildChannelHandler extends ChannelInitializer {@Overrideprotected void initChannel(SocketChannel ch) {ch.pipeline().addLast("http-codec", new HttpServerCodec());ch.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());ch.pipeline().addLast("handler", new WebSOcketServerHandler());}}private class WebSOcketServerHandler extends SimpleChannelInboundHandler {private WebSocketServerHandshaker handshaker;@Overrideprotected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {// 传统的 HTTP 接入if (msg instanceof FullHttpRequest) {System.out.println("传统的 HTTP 接入");handleHttpRequest(ctx, (FullHttpRequest) msg);}// WebSocket 接入else if (msg instanceof WebSocketFrame) {System.out.println("WebSocket 接入");handleWebSocketFrame(ctx, (WebSocketFrame) msg);}}private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {// 如果 HTTP 解码失败, 返回HTTP异常if (!req.getDecoderResult().isSuccess() || (!"websocket".equalsIgnoreCase(req.headers().get("Upgrade")))) {sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));return;}// 构造握手响应返回, 本机测试WebSocketServerHandshakerFactory wsFactory =new WebSocketServerHandshakerFactory("ws://localhost:8080/websocket", null, false);handshaker = wsFactory.newHandshaker(req);if (handshaker == null) {WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());} else {handshaker.handshake(ctx.channel(), req);}}private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {// 判断是否是关闭链路的指令if (frame instanceof CloseWebSocketFrame) {handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());return;}// 判断是否是 ping 信息if (frame instanceof PingWebSocketFrame) {ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));return;}// 本例程仅支持文本消息, 不支持二进制消息if (!(frame instanceof TextWebSocketFrame)) {throw new UnsupportedOperationException(String.format("%s frame types not supported", frame.getClass().getName()));}// 返回应答信息String request = ((TextWebSocketFrame) frame).text();ctx.channel().write(new TextWebSocketFrame(request + " , 欢迎使用 Netty WebSocket 服务, 现在时刻: "+ new java.util.Date().toString()));}private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) {if (res.getStatus().code() != 200) {ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8);res.content().writeBytes(buf);buf.release();setContentLength(res, res.content().readableBytes());}// 如果是非 Keep-Alive, 关闭连接ChannelFuture f = ctx.channel().writeAndFlush(res);if (!isKeepAlive(req) || res.getStatus().code() != 200) {f.addListener(ChannelFutureListener.CLOSE);}}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ctx.flush();}}}

HttpServerCodec: 将请求和应答消息编码或解码为 HTTP 消息.
HttpObjectAggregator: 它的目的是将 HTTP 消息的多个部分组合成一条完整的 HTTP 消息. Netty 可以聚合 HTTP 消息, 使用 FullHttpResponseFullHttpRequestChannelPipeline 中的下一个 ChannelHandler, 这就消除了断裂消息, 保证了消息的完整.
ChunkedWriteHandler: 来向客户端发送 HTML5 文件, 主要用于支持浏览器和服务端进行 WebSocket 通信.

第一次握手请求消息由 HTTP 协议承载, 所以它是一个 HTTP 消息, 执行 handleHttpRequest 方法来处理 WebSocket 握手请求. 通过判断请求消息判断是否包含 Upgrade 字段或它的值不是 websocket, 则返回 HTTP 400 响应.

握手请求校验通过之后, 开始构造握手工厂, 创建握手处理类 WebSocketServerHandshaker, 通过它构造握手响应消息返回给客户端.

添加 WebSocket Encoder 和 WebSocket Decoder 之后, 服务端就可以自动对 WebSocket 消息进行编解码了, 后面的 handler 可以直接对 WebSocket 对象进行操作.

handleWebSocketFrame 对消息进行判断, 首先判断是否是控制帧, 如果是就关闭链路. 如果是维持链路的 Ping 消息, 则构造 Pong 消息返回. 由于本例程的 WebSocket 通信双方使用的都是文本消息, 所以对请求新消息的类型进行判断, 而不是文本的抛出异常.

最后, 从 TextWebSocketFrame 中获取请求消息字符串, 对它处理后通过构造新的 TextWebSocketFrame 消息返回给客户端, 由于握手应答时, 动态增加了 TextWebSocketFrame 的编码类, 所以可以直接发送 TextWebSocketFrame 对象.



推荐阅读
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • 如何使用PLEX播放组播、抓取信号源以及设置路由器
    本文介绍了如何使用PLEX播放组播、抓取信号源以及设置路由器。通过使用xTeve软件和M3U源,用户可以在PLEX上实现直播功能,并且可以自动匹配EPG信息和定时录制节目。同时,本文还提供了从华为itv盒子提取组播地址的方法以及如何在ASUS固件路由器上设置IPTV。在使用PLEX之前,建议先使用VLC测试是否可以正常播放UDPXY转发的iptv流。最后,本文还介绍了docker版xTeve的设置方法。 ... [详细]
  • Shodan简单用法Shodan简介Shodan是互联网上最可怕的搜索引擎,与谷歌不同的是,Shodan不是在网上搜索网址,而是直接进入互联网的背后通道。Shodan可以说是一款“ ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 本文介绍了在rhel5.5操作系统下搭建网关+LAMP+postfix+dhcp的步骤和配置方法。通过配置dhcp自动分配ip、实现外网访问公司网站、内网收发邮件、内网上网以及SNAT转换等功能。详细介绍了安装dhcp和配置相关文件的步骤,并提供了相关的命令和配置示例。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • t-io 2.0.0发布-法网天眼第一版的回顾和更新说明
    本文回顾了t-io 1.x版本的工程结构和性能数据,并介绍了t-io在码云上的成绩和用户反馈。同时,还提到了@openSeLi同学发布的t-io 30W长连接并发压力测试报告。最后,详细介绍了t-io 2.0.0版本的更新内容,包括更简洁的使用方式和内置的httpsession功能。 ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • 本文介绍了在使用Python中的aiohttp模块模拟服务器时出现的连接失败问题,并提供了相应的解决方法。文章中详细说明了出错的代码以及相关的软件版本和环境信息,同时也提到了相关的警告信息和函数的替代方案。通过阅读本文,读者可以了解到如何解决Python连接服务器失败的问题,并对aiohttp模块有更深入的了解。 ... [详细]
  • 本文介绍了在mac环境下使用nginx配置nodejs代理服务器的步骤,包括安装nginx、创建目录和文件、配置代理的域名和日志记录等。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • 本文分享了一位Android开发者多年来对于Android开发所需掌握的技能的笔记,包括架构师基础、高级UI开源框架、Android Framework开发、性能优化、音视频精编源码解析、Flutter学习进阶、微信小程序开发以及百大框架源码解读等方面的知识。文章强调了技术栈和布局的重要性,鼓励开发者做好学习规划和技术布局,以提升自己的竞争力和市场价值。 ... [详细]
  • 动态多点××× 单云双HUB
    动态多点是一个高扩展的IPSEC解决方案传统的ipsecS2S有如下劣势1.中心站点配置量大,无论是采用经典ipsec***还是采用greoveripsec多一个分支 ... [详细]
author-avatar
小群群zheng
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有