Netty学习问题总结
本篇记录了Netty学习过程中想到的问题和自己的一些思考,对于应用层的协议也有了更好的理解,所以在此做一个记录。
一、HTTP协议分包
TCP是作为面来流的协议,所以需要应用层协议自己去分包。常见的分包格式如下:
- 定长: 比如100字节每个报文,不足的前面补0,这时候每次取消息就取到100字节算整包)
- 分隔符: 换行符其实是一种特别的分隔符,每次读取到分隔符就知道一个包读取完毕)
- 指明长度: 比如前两个字节为长度字段,先读取两个字节,知道了需要多少字节,读取到对应的字节就是一个整包了
然而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字段,这是一个变长字段,为了节省字节数,含义如下:
- 如果数据长度小于等于125的话,那么该7位用来表示实际数据长度。
- 如果数据长度为126到65535(2的16次方)之间,该7位值固定为126,也就是 1111110,往后扩展2个字节(16为,第三个区块表示),用于存储数据的实际长度。
- 如果数据长度大于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 。情况如下:
- 客户端主动关闭,客户端重启时报BindException,服务端用6060端口仍可正常启动
- 服务端主动关闭,服务端重启正常,客户端重启也正常,但是如果停掉服务端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
五、关于
待补充
Netty学习问题总结的更多相关文章
- netty学习资料
netty学习资料推荐官方文档和<netty权威指南>和<netty in action>这两本书.下面收集下网上分享的资料 netty官方参考文档 Netty 4.x Use ...
- Netty学习之客户端创建
一.客户端开发时序图 图片来源:Netty权威指南(第2版) 二.Netty客户端开发步骤 使用Netty进行客户端开发主要有以下几个步骤: 1.用户线程创建Bootstrap Bootstrap b ...
- netty学习资源收集
Netty学习笔记 Netty In Actions CSDN专栏 一起学Netty-CSDN专栏 Netty In Action中文版
- Netty 学习 一、初识Netty【原创】
在过去几年的工作和学习中,比较关注高层次的应用开发,对底层探究较少.实现Web应用的开发,主要依赖Tomcat.Apache等应用服务器,程序员无需了解底层协议,但同样限制了应用的性能和效率.现在开始 ...
- Netty学习笔记(二) 实现服务端和客户端
在Netty学习笔记(一) 实现DISCARD服务中,我们使用Netty和Python实现了简单的丢弃DISCARD服务,这篇,我们使用Netty实现服务端和客户端交互的需求. 前置工作 开发环境 J ...
- Netty 学习笔记(1)通信原理
前言 本文主要从 select 和 epoll 系统调用入手,来打开 Netty 的大门,从认识 Netty 的基础原理 —— I/O 多路复用模型开始. Netty 的通信原理 Netty 底层 ...
- Netty 学习资料
Netty 学习资料 Netty 学习资料 链接网址 说明 Netty 4.x 用户指南 http://wiki.jikexueyuan.com/project/netty-4-user-guide/ ...
- Netty学习第一节Netty的总体概况
一.Netty简介 什么是Netty? 1.高性能事件驱动,异步非阻塞的IO加载开源框架. 它是由JBoss提供,用于建立TCP等底层链接.基于Netty可以建立高性能的HTTP服务器,快速开发高性能 ...
- Netty学习——Thrift的入门使用
Netty学习——Thrift的入门使用 希望你能够,了解并使用它.因为它是一个效率很高的框架 官网地址:http://thrift.apache.org/ 1.Thrift数据类型 一门技术如果需要 ...
- Netty学习——Apache Thrift 简介和下载安装
Netty学习——Apache Thrift 简介和下载安装 Apache Thrift 简介 本来由Facebook开发,捐献给了Apache,成了Apache的一个重要项目 可伸缩的,跨语言的服务 ...
随机推荐
- 自定义View类
一.如何创建自定义的View类 ①.创建一个继承android.view.View类的Java类,并且重写构造方法(至少需要重写一个构造方法) ②.根据需要重写其他方法 ③.在项目的活动中,创建并实例 ...
- weui textarea超出字符被截断
HTML: <div class="weui-cells weui-cells_form" style="margin-top: 0;"> < ...
- CSS之表格边框合并、兄弟标签外边距合并、父子标签的外边距合并
本文内容: 表格边框合并 兄弟标签外边距合并 父子标签的外边距合并 首发日期:2018-05-01 表格边框合并: 发生情况: 当设置了cellpadding="0" cellsp ...
- Django 登录验证-自动重定向到登录页
Web项目有些场景需要做用户登录验证,以便访问不同页面. 方法一:login_required装饰器 适用于函数视图. from django.contrib.auth.decorators impo ...
- Bean named '...' is expected to be of type [...] but was actually of type [com.sun.proxy.$Proxy7解决方法
报错 三月 07, 2017 8:09:52 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepare ...
- 使用Jenkins遇到的问题
前言 本文记录我在使用jenkins过程中遇到的一些问题. 无法执行bat 如果出现bat执行出错,解决办法如下: 1. 打开 服务,找到 jenkins的服务 2. 修改jenkins的属性如下:( ...
- 简单的bfs
这里主要是写的一个简单的bfs,实例运行了RMAT10无向图,总共有1024个顶点.这种简单的bfs算法存在很明显的缺陷,那就是如果图数据过大,那么进程将会直接被系统杀死. 代码如下: #includ ...
- 在模态框(Modal)中使用UEditor全屏显示的一个坑
根据这个问题很简单就能查到一些文章明确说明了解决问题的方法,就是如下一段代码: var isModal = false; //判断该dom是否为modal var classes = $(contai ...
- SpringCloud之初识Robbin---负载均衡
在上一篇中讲解Eureka注册中心的案例,我们启动了一个user-service,然后通过DiscoveryClient来获取服务实例信息,然后获取ip和端口来访问. 但是实际环境中,我们往往会开启很 ...
- 06.Python网络爬虫之requests模块(2)
今日内容 session处理cookie proxies参数设置请求代理ip 基于线程池的数据爬取 知识点回顾 xpath的解析流程 bs4的解析流程 常用xpath表达式 常用bs4解析方法 引入 ...