Netty解决TCP粘包和拆包问题的四种方案
原因
操作系统在发送TCP数据的时候,底层会有一个缓冲区,例如1024个字节大小,如果一次请求发送的数据量比较小,没达到缓冲区大小,TCP则会将多个请求合并为同一个请求进行发送,这就形成了粘包问题;如果一次请求发送的数据量比较大,超过了缓冲区大小,TCP就会将其拆分为多次发送,这就是拆包,也就是将一个大的包拆分为多个小包进行发送
四种解决方案
也即netty的原理
netty底层是一个单线程的nio(reactor响应式)模型,他用一个线程selector来处理所有的连接【牺牲一个线程(selector线程是阻塞的)来实现整个框架的非阻塞】,对于各个频道的处理(读写)交给线程池来做。
传统bio在进行socket.accept()、socket.read()、socket.write()三个主要函数时都是同步阻塞的,nio的Selector也是阻塞的java 同步非阻塞的nio中包含阻塞的selector的理解
彻底理解同步,异步,阻塞,非阻塞
IO分两阶段(一旦拿到数据后就变成了数据操作,不再是IO):
1.数据准备阶段
2.内核空间复制数据到用户进程缓冲区(用户空间)阶段
线程要取数据,首先肯定要进行数据准备,阻塞就在这里开始等待。同步属于线程调用,A线程把数据发过来,B线程如果需要等待数据从内核考到用户空间,这是同步,如果不需等待,而是操作系统拷完后通知B线程直接到用户空间取数据这就是异步
线程内部调用而言:
阻塞调用:该调用如果未得到结果前就会阻塞线程
非阻塞调用:即使未得到结果,也不会阻塞线程
针对于线程间调用而言
同步:需要等待结果的返回
异步:结果通过事件,回调等机制通知调用者结果
假如共享信道传输的码片序列是:(-1,1,-3,1,-1,-3,1,1),A1,B1,C1各自唯一的码片序列是(-1-1-1+1+1-1+1+1),(+1+1-1+1+1-1-1-1),(-1+1-1-1+1-1+1+1),那么根据内积和=0公式可以判断出哪些站发送了数据,哪些站没有发,发的数据是1还是0
链路层解决三个问题:
网络地址=ip地址&子网掩码
公式的原因:以前网络分A类,B类,C类,现在更细分A类(子网1,子网2…),B类(子网1,子网2…),A类下子网1和子网2不是同一网段
127划分A,B网络
192划分B,C网络
ARP协议—网络层的协议:将ip地址转物理地址
假设用斜杠记法的IP地址表示如下: 200.15.13.12/22,求子网掩码。
/22表示子网掩码有22位二进制数1,即:
11111111.11111111.11111100.00000000
转换成十进制就是255.255.252.0
即子网掩码为:255.255.252.0
子网掩码:指出subnet-id与host-id的分界线【全1的部分是网络号+子网号,全0的部分是主机号】
例题1:
将B类IP地址168.195.0.0划分成若干子网,每个子网内有主机700台,求子网掩码:
(1) 700=1010111100
(2)该二进制为十位数,N = 10
(3)将该B类地址的子网掩码255.255.0.0的主机地址全部置1,得到255.255.255.255
(5)然后再从后向前将后10位置0,即为: 11111111.11111111.11111100.00000000
(6)即子网掩码为: 255.255.252.0。
例题2:
将B类IP地址168.195.0.0划分成27个子网:
(1)27=11011
(2)该二进制为五位数,N = 5
(3)将B类地址的子网掩码255.255.0.0的主机地址前5位置1(B类地址的主机位包括后两个字节,所以这里要把第三个字节的前5位置1),得到 255.255.248.0
(4)即子网掩码为: 255.255.248.0
例题3:
Ip:211.68.114.*,每个子网分30台主机,求子网掩码
也可以这样算:30=11110,n=5,则将255.255.255.255的二进制,将后5为全部置0,即11111111.11111111.11111111.11100000
即得到255.255.255.224
211.68.114.1/25表示前25位为是网络号,那么7位是主机号,再进行子网划分,就可能是前27位是主机号,后5位是主机号。子网掩码:27位1(网络号)+2位1(子网号)+3位0(主机号)
有了滑动窗口,为什么还要有拥塞窗口:origin link
假如没有拥塞窗口,那么发方会按照收方的要求发数据,但如果遇到网络拥塞,(即当前网络只支持1M/s)你一次传了3M的数据,需要3秒,必然会进行tcp的超时重传,那么一份数据就要被传几次,性能太低,所以引入了拥塞窗口,由拥塞窗口和收方窗口的大小二者共同决定此次发方要发送的数据的大小
接收到连续的三个重复冗余ACK(其实是收到4个同样的ACK,第一个是正常的,后三个才是冗余的),发送端便知晓哪个报文段在传输过程中丢失了,于是重发该报文段,不需要等待超时重传定时器溢出,大大提高了效率。这便是快速重传机制
状态:【关闭状态----》同步已收到-----》同步已确认------》连接建立】
SYN同步位
ACK确认位
seq序列号
ack确认号,它是对客户端发过来的序列号的回应
服务端从关闭状态被动打开到监听状态,客户端启动并发送建立连接的请求,从关闭状态到同步已发送状态,服务端收到并返回确认进入同步收到状态,客户端给出确认的确认。双方进入连接建立状态。
状态:【连接建立状态—》终止等待-1状态—》关闭等待状态—》终止等待-2状态----》时间等待状态(time-wait)—》最后确认状态----》关闭状态】
客户端发起断开连接的请求,从连接建立状态到终止等待1状态,服务端收到后给出确认,进入关闭等待状态,客户端收到服务端的确认后进入终止等待2状态。服务端发起断开连接的请求进入最后确认状态,客户端收到后进入时间等待状态并给出确认(经过2msl时间后释放掉连接),服务端收到后释放掉连接,进入关闭状态
用来释放一个连接。FIN = 1
表明此报文段的发送端的数据已发送完毕,并要求释放运输连接
• B 若同意,则发出确认。
其 ACK=1,确认号ack =u+1,而这个报文段自己的序号 seq=v。 • TCP 服务器进程通知高层应用进程。
• 从 A 到 B 这个方向的连接就释放了,TCP 连接处于半关闭状
态。
(1)若 B 已经没有要向 A 发送的数据,其应用进程就通知
TCP 释放连接。
其报文段首部FIN=1,ACK=1,序号seq=w, ack=u+1。
A收到连接释放报文段后,必须发出确认。
其报文段首部ACK=1,序号seq= u+1, ack= w+1
经过时间 2MSL(最长报文段寿命) 连接真正释放掉
因为是全双工的,发送方和接收方都需要FIN报文和ACK报文,也就是说,这传递的过程,发送方和接收方都得各自发送两次,加在一起就是四次了。
当客户端发送过来一个FIN报文之后,服务端没有发送ACK,或者来确认FIN报文,说白了,就是对方关闭socket连接,我方(服务器)忙于读或写,没有及时关闭连接。
一般这种情况都是程序中有bug,我们需要检查释放资源的代码
TCP和UDP都是全双工,在发送信息的同时,可以接收信息。socket是基于TCP和UDP的所以也是全双工的。
http协议是单向通信的(单向数据流),websocket协议是双向通信的
DNS:
domain name system分布式域名系统,完成主机名和IP地址的映射。一个服务器所负责管辖的(或有权限的)范围叫做区(zone)。
本地域名服务器(也称为默认域名服务器。)
当一个主机发出 DNS 查询请求时,这个查询
请求报文就发送给本地域名服务器。(//路由器)
根域名服务器
知道所有的顶级域名服务器的域名和 IP 地址。
一般情况下,并不负责把待查询域名直接转换
成 IP 地址。当收到 DNS 查询请求时,就给出
相应的回答,内容是下一步应当找的顶级域名服
务器的 IP 地址(//服务器所在的城市/国家)
顶级域名服务器
负责管理在该顶级域名服务器注册的所有二域名。
当收到 DNS 查询请求时,就给出相应的回答。
可能是最后的结果,也可能是下一步应当找的
域名服务器的 IP 地址。(//可理解成全球网络)
权限域名服务器
负责一个区的域名服务器。
将其辖区内的主机的域名转换成 IP 地址,或
者告诉发出查询请求的 DNS 客户,下一步应当
找哪一个域名服务器。
服务器收到DNS请求后,如不能解答,则以 DNS 客户的身份代
替发来请求的客户进行后续查询。(递归查询)
服务器收到DNS请求后,如不能解答,则告诉客户下一步应当查询的域
名服务器,让发来请求的客户自己完成后续查询I(迭代查询)