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的一个重要项目 可伸缩的,跨语言的服务 ...
随机推荐
- Android为TV端助力 eclipse出现感叹号的解决办法
当eclipse导入项目出现红叉但无提示错误时,去看:1>菜单路径----Window/Show View/Console2>菜单路径----Window/Show View/Error ...
- IIS 配置 HTTPS
前言 HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer 或 Hypertext Transfer Protocol Secu ...
- git 入门教程之基本概念
基本概念 了解工作区,暂存区和版本库的区别和联系有助于我们更好理解 git 的工作流程,了解命令的操作意图. git 和其他版本控制系统如 svn 的不同之处就是有暂存区的概念. 基本概念 工作区 | ...
- html常用标签学习笔记
本文内容: 前言:本文讲述的内容包括几类常用标签,以及这些标签的一些常用属性(有一些属性由于已经有CSS样式来代替,所以对于一些不重要的这里选择不讲) 排版标签 段落标签:p div span 标题标 ...
- 这几天上海移动网络可以直接打开 Google Play 了
这几天上海移动网络可以直接打开 Google Play (谷歌应用商店)了. 速度还不错.基本无延迟. 想当初,为了防止国内应用市场里的木马或恶意软件,想从 Google Play 应用市场下载,折腾 ...
- 将 varchar 值 'ACCE5057EC423F7C' 转换成数据类型 int 时失败
调试别人的存储过程,然后报错了 将 varchar 值 'ACCE5057EC423F7C' 转换成数据类型 int 时失败 这让我一通找.找了一个多小时. 通过这个错可以知道,错误肯定是在联表 字段 ...
- [20190226]删除tab$记录的恢复6.txt
[20190226]删除tab$记录的恢复6.txt --//春节前几天做了删除tan$记录的测试,链接:http://blog.itpub.net/267265/viewspace-2565245/ ...
- jquery hover事件只触发一次动画
最近工作时遇到个关于动画的问题,如下: $("div").hover( function() { $(this).animate({"margin-top":& ...
- python3基础知识梳理
一.数据类型 1.数字 int(整型) long(长整型) float(浮点型) complex(复数) 2.布尔值 True或False 3.字符串 二.变量 变量命名规则: 变量名只能是 字母 ...
- LeetCode算法题-Best Time to Buy and Sell Stock
这是悦乐书的第172次更新,第174篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第31题(顺位题号是121).假设有一个数组,其中第i个元素是第i天给定股票的价格.如果 ...