Netty学习问题总结

目录

  • 一、HTTP协议分包
  • 二、WebSocket协议分包
  • 三、HTTP和WebSocket协议共用一个端口的问题
  • 四、TIME WAIT状态占用了什么资源
  • 五、关于

本篇记录了Netty学习过程中想到的问题和自己的一些思考,对于应用层的协议也有了更好的理解,所以在此做一个记录。

一、HTTP协议分包

TCP是作为面来流的协议,所以需要应用层协议自己去分包。常见的分包格式如下:

  1. 定长: 比如100字节每个报文,不足的前面补0,这时候每次取消息就取到100字节算整包)
  2. 分隔符: 换行符其实是一种特别的分隔符,每次读取到分隔符就知道一个包读取完毕)
  3. 指明长度: 比如前两个字节为长度字段,先读取两个字节,知道了需要多少字节,读取到对应的字节就是一个整包了

然而HTTP协议格式并不是上面的简单的一种,它结合了2和3两种来进行分包,因为请求和响应报文格式一样,所以这里针对请求报文进行说明。我们知道HTTP分为请求行,请求头,请求体。
下面是报文说明摘自HTTP RFC文档中4.1 Message Types:

HTTP报文格式

    generic-message = start-line
                      *(message-header CRLF)
                      CRLF
                      [ message-body ]

报文读取过程

  • 读取请求行:每个HTTP请求的第一行作为请求行,所以知道读取到CRLF就说明结束了
  • 读取请求头:请求头有多行,行数不是固定的,他的结尾是根据连续的两个CRLF来判断的,在message-body之前会有一个CRLF
  • 读取请求体:对于POST等带有请求体的方法来说,请求体的长度是不固定的,这时候请求头中会有个Content-Length字段说明了请求体的长度,所以只要读取完Content-Length个字节,整个HTTP请求报文也就得到了

关于报文分割一点备注:早期的HTTP 1.0时代,因为它每次请求都会经历tcp的三次握手连接过程,所以它是通过连接的关闭来判断报文已经读取完毕,但是这里还有一个问题,如果这个连接关闭时因为服务端的错误引起的那客户端就无法区分了。到了HTTP 1.1,因为很多请求会重用一个连接,所以需要用到Content-Length这个字段来做分包。另外还有一种不需要Content-Length的方法就是请求头中Transfer-Encoding为chunked,这是一种分块传输,在压缩传输,动态内容生成等响应在一开始长度未知的场景下很有用。他的报文分割也很简单,详情参见:分块传输编码

二、WebSocket协议分包

理解了HTTP协议的分包,WebSocket的协议也容易理解,道理都是想通的。一开始在谷歌的时候一直搜索不到相关的报文,最后搜索WebSocket数据帧才搜到了结果(搜索是门技巧啊)。我最关注的是opcode字段,因为在用WebSocket的时候就用到了这个字段来判断是什么帧类型。第二是Payload len字段,这是一个变长字段,为了节省字节数,含义如下:

  1. 如果数据长度小于等于125的话,那么该7位用来表示实际数据长度。
  2. 如果数据长度为126到65535(2的16次方)之间,该7位值固定为126,也就是 1111110,往后扩展2个字节(16为,第三个区块表示),用于存储数据的实际长度。
  3. 如果数据长度大于65535, 该7位的值固定为127,也就是 1111111 ,往后扩展8个字节(64位),用于存储数据实际长度。

摘录自:https://www.cnblogs.com/tugenhua0707/p/8542890.html,具体关于WebSocket可以查阅资料,有非常多的讲解

WebSocket RFC中websocket报文格式

      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-------+-+-------------+-------------------------------+
     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
     |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
     |N|V|V|V|       |S|             |   (if payload len==126/127)   |
     | |1|2|3|       |K|             |                               |
     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
     |     Extended payload length continued, if payload len == 127  |
     + - - - - - - - - - - - - - - - +-------------------------------+
     |                               |Masking-key, if MASK set to 1  |
     +-------------------------------+-------------------------------+
     | Masking-key (continued)       |          Payload Data         |
     +-------------------------------- - - - - - - - - - - - - - - - +
     :                     Payload Data continued ...                :
     + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
     |                     Payload Data continued ...                |
     +---------------------------------------------------------------+

三、HTTP和WebSocket协议共用一个端口的问题

之前对这个现象十分不理解,很多Web服务器例如Tomcat都支持HTTP和WebSocket共用一个端口,他是怎么做到的?

其实理解了报文的解析就很容易理解了,HTTP和WebSocket协议的下层都是TCP连接,他们是应用层连接,所以在处理TCP的字节流时,可以先获取几个字节,如果前几个字节解析出来是GET,POST等HTTP协议的用到的,那么就根据HTTP报文的分包规则获取一个HTTP报文然后流转到后端处理。如果是WebSocket协议就根据WebSocket报文来解析然后做响应的处理。

补充:很多的协议栈都是以魔数打头,这样就更容易实现同端口多协议的支持,如Dubbo协议栈前两个字节是魔数,只需要判断报文的前两个字节就知道是不是Dubbo协议了,Dubbo协议栈头报文如下图(摘抄自http://ifeve.com/dubbo-protocol/):

四、TIME WAIT状态占用了什么资源

我们知道TCP四次挥手时主动发起方会经历一个TIME WAIT状态,也正是因为这个原因我们尽量让客户端主动关闭连接。对于这个状态有人说是占用了文件描述符,有人说是端口,那么究竟是占用了什么资源?

根据我自己的实验,Windows系统下,TIME WAIT状态占用了端口,该端口不能作为客户端端口使用,但仍然可以作为服务端端口使用。实验端口:服务端8080,客户端6060 。情况如下:

  1. 客户端主动关闭,客户端重启时报BindException,服务端用6060端口仍可正常启动
  2. 服务端主动关闭,服务端重启正常,客户端重启也正常,但是如果停掉服务端8080,客户端用6060报BindException

实验代码如下,可根据需要自己修改试验:

服务端主动关闭

public class ServerSocketCloseTest {
    public static void main(String[] args) throws IOException, InterruptedException {
        Runnable runnable = () -> {
            try {
                ServerSocket serverSocket = null;
                serverSocket = new ServerSocket(8080, 10);
                while (true) {
                    Socket accept = serverSocket.accept();
                    accept.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        };
        new Thread(runnable).start();
        Socket socket = new Socket(InetAddress.getLocalHost(), 8080, InetAddress.getLocalHost(), 6060);
        Thread.sleep(1000);
        socket.close();
        System.in.read();
    }
}

客户端主动关闭场景

public class ClientSocketCloseTest {

    public static void main(String[] args) throws IOException, InterruptedException {
        Runnable runnable = () -> {
            try {
                ServerSocket serverSocket = new ServerSocket(8080, 10);
                while (true) {
                    Socket accept = serverSocket.accept();
                    Thread.sleep(1000);
                    accept.close();
                }
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
        };
        new Thread(runnable).start();

        Socket socket = new Socket(InetAddress.getLocalHost(), 8080, InetAddress.getLocalHost(), 6060);
        socket.close();
        Thread.sleep(1000);
        System.in.read();
    }

}

结论:处于TIME WAIT状态下的端口不能作为客户端端口使用。对于服务端端口没有影响,服务端是仍然是可以正常绑定,接收到客户端连接后本地端口和监听端口是同一个所以不存在端口占用。另外通过查阅资料,TIME WAIT是释放了文件描述符,但是TCP连接的五元组并未释放,还占用一定的内存。参考地址如下:https://stackoverflow.com/questions/1803566/what-is-the-cost-of-many-time-wait-on-the-server-side/1806033#1806033

五、关于

待补充

原文地址:https://www.cnblogs.com/chenfangzhi/p/10353920.html

时间: 2024-08-28 17:43:52

Netty学习问题总结的相关文章

netty 学习资料

最近在做一个网页遥控器的项目,用到了netty,但还未发现比较系统完整的netty博客教程,所以打算自己写一个netty学习教程,会每天更新一篇,欢迎交流. 先给大家提供一些资料: 1. 比较简短易懂的有实例的系列教程,涉及到了netty关键特性:但个人觉得比较速成,不系统,不深入 http://www.coderli.com/netty-course-hello-world http://www.coderli.com/netty-two-concepts http://www.coderli

netty学习资源收集

Netty学习笔记 Netty+In+Action中文版.pdf

Netty学习篇--整合springboot

经过前面的netty学习,大概了解了netty各个组件的概念和作用,开始自己瞎鼓捣netty和我们常用的项目的整合(很简单的整合) 项目准备 工具:IDEA2017 jar包导入:maven 项目框架:springboot+netty 项目操作 右键创建一个maven项目,项目名称: hetangyuese-netty-03(项目已上传github) 项目完整结构 ? maven导包 <!-- netty start --> <dependency> <groupId>

Netty学习——基于netty实现简单的客户端聊天小程序

Netty学习——基于netty实现简单的客户端聊天小程序 效果图,聊天程序展示 (TCP编程实现) 后端代码: package com.dawa.netty.chatexample; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEven

Netty学习——通过websocket编程实现基于长连接的双攻的通信

Netty学习(一)基于长连接的双攻的通信,通过websocket编程实现 效果图,客户端和服务器端建立起长连接,客户端发送请求,服务器端响应 但是目前缺少心跳,如果两个建立起来的连接,一个断网之后,另外一个是感知不到对方已经断掉的.以后使用心跳技术来进行连接检测 须知: 状态码101,代表 协议转换,从HTTP协议升级为WebSocket协议 HTTP协议,一般访问的时候:是 Http://localhost:8080/ws WebSocket协议,访问的时候,需要是:ws://localho

Netty学习——Netty和Protobuf的整合(一)

Netty学习——Netty和Protobuf的整合 Protobuf作为序列化的工具,将序列化后的数据,通过Netty来进行在网络上的传输 1.将proto文件里的java包的位置修改一下,然后再执行一下protoc 异常捕获:启动服务器端正常,在启动客户端的时候,发送消息,报错 警告: An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the l

netty学习:UDP服务器与Spring整合(2)

上一篇文章中,介绍了netty实现UDP服务器的栗子,本文将会对UDP服务器与spring boot整合起来,并使用RedisTemplate的操作类访问Redis和使用JPA链接MySQL,其中会使用多线程.异步等知识. 本人使用的编辑器是IntelliJ IDEA 2017.1.exe版本(链接:http://pan.baidu.com/s/1pLODHm7 密码:dlx7):建议使用STS或者是idea编辑器来进行spring的学习. 1)项目目录结构 整个项目的目录结构如下: 2)jar

(二)Netty学习笔记之服务端启动

本文将不会对netty中每个点分类讲解,而是一个服务端启动的代码走读,在这个过程中再去了解和学习,这也是博主自己的学习历程.下面开始正文~~~~ 众所周知,在写netty服务端应用的时候一般会有这样的启动代码: (代码一) 1 EventLoopGroup bossGroup = new NioEventLoopGroup(1); 2 EventLoopGroup workerGroup = new NioEventLoopGroup(); 3 try { 4 ServerBootstrap b

netty 学习记录一

最近在学习netty相关知识,觉得<netty 权威指南>这本书还是挺好的,适合我这种初学者.加上netty本身自带的许多例子,学起来还是挺有兴趣的.简单记录下, 一般服务器代码如下: public void run() throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerB

Netty学习总结(1)--Netty入门介绍

1.Netty是什么? Netty是一个基于JAVA NIO类库的异步通信框架,它的架构特点是:异步非阻塞.基于事件驱动.高性能.高可靠性和高可定制性. 2.使用Netty能够做什么? 开发异步.非阻塞的TCP网络应用程序: 开发异步.非阻塞的UDP网络应用程序: 开发异步文件传输应用程序: 开发异步HTTP服务端和客户端应用程序: 提供对多种编解码框架的集成,包括谷歌的Protobuf.Jboss marshalling.Java序列化.压缩编解码.XML解码.字符串编解码等,这些编解码框架可