Netty理论四:TCP vs UDP

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Netty理论四:TCP vs UDP相关的知识,希望对你有一定的参考价值。

参考技术A

从数据结构上可以看出来,TCP比UDP要复杂的多。

我们上面说tcp是面向连接的,这是啥意思呢,简单的说,tcp要发送数据,首先得先建立连接,而udp不需要,直接发送数据就行了。

TCP是全双工的,即客户端在给服务器端发送信息的同时,服务器端也可以给客户端发送信息。
而半双工的意思是A可以给B发,B也可以给A发,但是A在给B发的时候,B不能给A发,即不同时,为半双工。

为什么采用三次握手?而不是二次?
是为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误;比如有以下场景,当客户端发出第一个
连接请求1并没有丢失,而是在某些网络节点长时间滞留了,这时客户端会认为超时,会再次发送连接请求2,服务端在接收到连接请求2以后建立了正常的连接,这时候失效请求1又到达了服务端,服务端会误以为是客户端又发出的一个连接请求,于是会再次建立连接,假定不采用三次握手,那服务端发出确认,新的连接就建立了。但是由于客户端并没有发出新的建立连接的请求,因此客户端不会再新的连接上发送数据,而服务端却以为新的连接已经建立了,在一直等待客户端的数据,由此会导致服务端许多资源的浪费。采用了三次握手后,可以防止这个现象发生,在客户端收到服务端对于自己的无效连接的应答后,并不会向服务端发出确认,服务端由于收不到确认,就可以认为此次连接是无效的。

第二次挥手完成后,客户端到服务端的连接已经释放,B不会再接收数据,A也不会再发送数据。这个时候只是客户端不再发送数据,但是 B 可能还有未发送完的数据,所以需要等待服务端也主动关闭。

为什么是四次?
关闭连接时,当Server收到FIN报文时,很可能并不会立即关闭socket,因为Server端可能还有消息未发出,所有其只能先回复一个ACK报文,告诉Client端,你的关闭请求我收到了;当Server把所有的报文都发送完以后,Server才能给Client端发送FIN报文关闭连接,Client收到后应答ASK。所以需要四次握手

在四次握手后,Server端先进入TIME_WAIT状态,然后过2MS(最大报文生存时间)才能进入CLOSE状态。为啥?
因为网络是不可靠的,客户端在第四次握手的ACK可能会丢失,所以TIME_WAIT状态就是用来重发可能丢失的ACK报文

TCP是顺序性,是通过协议中的序号来保证,每个包都有一个序号ID,
在建立连接的时候会商定起始 序号ID 是什么,然后按照 序号ID 一个个发送。

tcp是通过应答/确认/ACK 以及 重传机制来保证消息可靠传输的。

即在消息包发送后,要进行确认,当然,这个确认不是一个一个来的,而是会确认某个之前的 ID,表示都收到了,这种模式成为累计应答或累计确认。
确认是通过报文头里面的确认序号来保证的。

为了记录所有发送的包和接收的包,TCP 需要在发送端和接收端分别来缓存这些记录,发送端的缓存里是按照包的 ID 一个个排列,根据处理的情况分成四个部分

流量控制指的是发送端不能无限的往接收端发送数据(UDP就可以),为啥呢?

因为在 TCP 里,接收端在发送 ACK 的时候会带上接收端缓冲区的窗口大小,叫 Advertised window,超过这个窗口,接收端就接收不过来了,发送端就不能发送数据了。这个窗口大概等于上面的第二部分加上第三部分,即 发送未确认 + 未发送可发送。

流量控制是点对点通信量的控制,是一个端到端的问题,主要就是抑制发送端发送数据的速率,以便接收端来得及接收。

tcp接收端缓冲区的大小是可以调试的,见 Netty高级功能(五):IoT百万长连接性能调优

TCP通过一个定时器(timer)采样了RTT并计算RTO,但是,**如果网络上的延时突然增加,那么,TCP对这个事做出的应对只有重传数据,然而重传会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这就导致了恶性循环,最终形成“网络风暴” —— TCP的拥塞控制机制就是用于应对这种情况。 **
拥塞控制的问题,也是通过窗口的大小来控制的,即为了在发送端调节所要发送的数据量,定义了一个“拥塞窗口”(Congestion Window),在发送数据时,将拥塞窗口的大小与接收端ack的窗口大小做比较,取较小者作为发送数据量的上限。

所以拥塞控制是防止过多的数据注入到网络中,可以使网络中的路由器或链路不致过载,是一个全局性的过程。

TCP 拥塞控制主要来避免两种现象,包丢失和超时重传,一旦出现了这些现象说明发送的太快了,要慢一点。

具体的方法就是发送端慢启动,比如倒水,刚开始倒的很慢,渐渐变快。然后设置一个阈值,当超过这个值的时候就要慢下来

通过 TCP 连接传输的数据无差错,不丢失,不重复,且按顺序到达。

第一层:物理层
第二层:数据链路层 802.2、802.3ATM、HDLC、FRAME RELAY
第三层:网络层 IP 、IPX、ARP、APPLETALK、ICMP
第四层:传输层 TCP、UDP 、SPX
第五层:会话层 RPC、SQL 、NFS 、X WINDOWS、ASP
第六层:表示层 ASCLL、PICT、TIFF、JPEG、 MIDI、MPEG
第七层:应用层 HTTP,FTP ,SNMP等

netty知识总结

目录

一、tcp & udp

二、tcp如何保证更可靠

1、确认应答和序列号

2、超时重传

3、流量控制

4、拥塞控制

三、滑动窗口

四、状态流转

五、粘包、拆包

MTU(Maximun Transmission Unit)

MSS(Maximun Segment Size)

1、引起拆包、粘包的原因

2、tcp解决方案

3、netty解决方案

六、同步、异步、阻塞、非阻塞

七、网络IO模型

八、BIO、NIO、AIO

九、select 、poll 、 epoll

1、链接数

2、轮询方式

3、数据copy

十、netty 优点

十一、netty的组件

十二、什么是netty

十三、为什么使用netty

十四、应用场景

十五、重要组件


一、tcp & udp

tcp : 面向连接 ;基于流 ;可靠的;仅支持一对一;首部开销20b;全双工可靠信道

udp:无连接;基于报文;不可靠;支持一对多、多对一、多对多通信;首部开销小8b;不可靠信道

二、tcp如何保证更可靠

1、确认应答和序列号

TCP传输时,将数据流进行编号,这就是序列号 ;可以用来应答、数据排序去重

每次接收方收到数据后,对川蜀坊进行应答,也就是发送ack报文

 

2、超时重传

TCP发出一段后,启动一个定时器,等待目标端ack报文,如不能及时收到确认,则重新发送报文

3、流量控制

tcp链接双方均有一个固定大小的缓冲空间,tcp接收端只允许发送端发送缓冲区能够接纳大小的数据。

当接收方来不及处理发送方数据时,能够提示发送方降低速率,防止包丢失

tcp流量控制协议是可变大小的滑动窗口协议

4、拥塞控制

当网络阻塞时,发送方继续重发,会加重网络阻塞,所以当出现阻塞时,应该控制发送方的速率。拥塞控制是为了控制网络的阻塞程度。

 

TCP 主要通过四个算法来进行拥塞控制:慢开始、拥塞避免、快重传、快恢复。

三、滑动窗口

滑动窗口是tcp流量控制的协议。假设我们每次发送一个数据包,然后就等待ack应答 ,这样会极大的影响传输效率,所以,我们把多个数据包达成一个package,一起发送出去,然后一起等待 。我们能够一起发送的数据量大小 , 就是窗口。

 

我们每次发送固定大小的窗口的数据 ,然后等待ack ,可行吗?答案是否定的。

窗口设置小了 : 需要频繁的接收数据的ack应答

窗口设置大了: 接收端处理不过来,会阻塞我们的网络。

所以我们需要一个动态的窗口:接收方来告诉我,可以处理多少数据 , 这就是提出窗口;发送方根据提出窗口和 已发送但是未确认的量 , 来计算可用窗口的大小。

我们可以把数据看做一条链 , 有一个可以向后滑动的窗口,框柱了 已发送待确认和允许发送的数据段

TCP的包可以分为四种状态

  • 已发送并且已经确认的包。

  • 已发送但是没有确认的包。

  • 未发送但是可以发送的包。

  • 不允许被发送的包。

零窗口 :当接收方提出窗口= 0 ,发送方只能停止发送。但是会建立一个零窗口定时探测器,向接收方询问窗口大小,当不在是0时 , 就可以继续发送了。

四、状态流转

LISTEN:等待从任何远端TCP 和端口的连接请求。
​
SYN_SENT:发送完一个连接请求后等待一个匹配的连接请求。
​
SYN_RECEIVED:发送连接请求并且接收到匹配的连接请求以后等待连接请求确认。
​
ESTABLISHED:表示一个打开的连接,接收到的数据可以被投递给用户。连接的数据传输阶段的正常状态。
​
FIN_WAIT_1:等待远端TCP 的连接终止请求,或者等待之前发送的连接终止请求的确认。
​
FIN_WAIT_2:等待远端TCP 的连接终止请求。
​
CLOSE_WAIT:等待本地用户的连接终止请求。
​
CLOSING:等待远端TCP 的连接终止请求确认。
​
LAST_ACK:等待先前发送给远端TCP 的连接终止请求的确认(包括它字节的连接终止请求的确认)
​
TIME_WAIT:等待足够的时间过去以确保远端TCP 接收到它的连接终止请求的确认。
TIME_WAIT 两个存在的理由:
          1.可靠的实现tcp全双工连接的终止;
          2.允许老的重复分节在网络中消逝。
​
CLOSED:不在连接状态(这是为方便描述假想的状态,实际不存在)

五、粘包、拆包

tcp是流协议,本身是无界限的。tcp会根据实际情况划分发送,一个完整的数据包可能拆分成多个,也可以整合成一个进行发送,这就是拆包、粘包

MTU(Maximun Transmission Unit)

最大传输单元,在数据链路层中,规定MTU大小,以太网 设置为1500字节

MSS(Maximun Segment Size)

最大保温段 , MSS = MTU - IP首部(20)- TCP首部(20) = 1460

1、引起拆包、粘包的原因

1、应用程序写入的数据 超过了套接字缓存大小 , 发生拆包

2、应用程序写入的数据小于套接字缓存,缓存会整合多个数据,统一发送到网络上,发生粘包

3、接收方法不能即时读取套接字缓存区数据,发生粘包

2、tcp解决方案

1、消息定长

2、分隔符

3、消息头,规定数据长度

3、netty解决方案

1、FixedLengthFrameDecoder 消息定长

2、LineBasedFrameDecoder 换行符 和 DelimiterBasedFrameDecoder (用户自定义风格福)

3、LengthFieldBasedFrameDecoder 数据包中添加一个长度的字段

4、自定义编码、解码器

六、同步、异步、阻塞、非阻塞

  1. 同步,就是我调用一个功能,该功能没有结束前,我死等结果。

  2. 异步,就是我调用一个功能,不需要知道该功能结果,该功能有结果后通知我(回调通知)

  3. 阻塞,就是调用我(函数),我(函数)没有接收完数据或者没有得到结果之前,我不会返回。

  4. 非阻塞,就是调用我(函数),我(函数)立即返回,通过select通知调用者

同步IO和异步IO的区别就在于:数据拷贝的时候进程是否阻塞

阻塞IO和非阻塞IO的区别就在于:应用程序的调用是否立即返回

七、网络IO模型

八、BIO、NIO、AIO

1、BIO , 同步且阻塞 , 一个请求一个线程,对服务器要求高,适用于连接数比较小且固定的架构,开发难度小

2、NIO,同步非阻塞 ,客服端的链接请求注册到多路复用器上,多路复用器轮询到有链接IO时,转给一个线程处理,自己继续阻塞等待,jdk1.4开始支持

3、AIO ,异步非阻塞,客户端的IO请求由内内核理完成,然后通知业务线程处理,适用于连接数多 且 链接较长的架构。jdk7 开始支持

九、select 、poll 、 epoll

1、链接数

select : 1024个 , 单个进程能打开的最大连接数 有FD_ZETSIZE 决定

poll : 没有上限,采用链表的方式

epoll : 链接数有上限,但是很大,1G内存支持10万的链接

2、轮询方式

select : 普通轮询 , 当连接数过大时,性能较差

poll : 同上

epoll : 采用回调的方式 , 链接的socket注册到epoll上 , 当有数据读写时,调用callBack

3、数据copy

select: 内核空间拷贝至用户空间 ,非常耗时

poll : 同上

epoll : 内核空间和用户空间 共享一块内存空间,相当于是零拷贝

十、netty 优点

NIO:

1、nio类库、api学习成本高,复杂

2、失败重试、数据丢失、拆包粘包等问题

3、selectk空轮训,内存飚增

netty:

1、架构设计优雅简单,耦合度低,底层模型可以使用不同网络协议

2、提供多种标准协议、安全、编码解码的支持

3、降低了NIO使用难度

4、很多开源框架都在使用,dubbo /rocketmq/spark

5、零拷贝、统一的api、标准可扩展的时间模型

十一、netty的组件

1、channel

2、buffer

3、EventLoop

4、ChannelHandler 和 ChannelPipeline

5、Bootstrap 和 ServerBootstrap

十二、什么是netty

1、netty是基于java nio(IO多路复用)的网络通信框架 ,极大的简化了TCP、UDP套接字服务的网络编程, 并且性能与安全性、使用性上更好。

2、特点:采用的reactor线程模型,是异步非阻塞、基于事件驱动、高性能、高可靠性和高定制型

3、支持多种协议、多种粘包拆包及二次编码机制。

4、使用范围广泛,比如dubbo/rocketMQ , Elasticsearch/gRPC等等

十三、为什么使用netty

1、简单易用,为我们屏蔽jdk自带的java nio的复杂度。

2、优秀的线程模型 reactor , 性能高 ,且零维护

3、功能强大,预置了多种编解码器,支持多种主流协议

4、高定制性,通过ChannelHandler 对通信框架进行灵活扩展

5、安全性高,有完整的SSL/TLS等支持

6、社区活动、网上教程多

7、使用广泛,在互联网、大数据、网络游戏等领取广泛应用,且满足商业标准

十四、应用场景

理论上NIO可以做的事情,使用netty都可以做。

1、作为RPC框架的网络通信工具:DUBBO、GRPC

2、用作HTTP服务器,目前我们常见的web服务器都是基于toncat或者jetty的 , 是基于serlet的 , NIO支持更底层的TCP协议,所以也可以用作支撑HTTP服务器,目前netty 就是http的协议支持。

3、即时的通讯系统,比如聊天工具 或者 聊天室,这块开源项目比较多的

4、消息推送系统。比如rocketMQ

十五、重要组件

1、bootstrap / serverbootstrap

bootstrap是netty的引导程序,采用了构造者模式,包含了线程组、通道channel、handler等配置信息。

引导netty的启动:

1、new NioEventLoop的构造方法里打开了一个多路复用器

2、serverBootstrap.bind()

2.1、创建一个ServerSocketChannel (initAndRegister())

2.2、绑定一个端口,先注册0事件 ,因为这时 项目还未完全启动

AbstractChannel对象的

selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);

2.3、真正的绑定 DefaultChannelPipeline.HeadContext -> AbstractChannel.Head

javaChannel().bind(localAddress, config.getBacklog());

ServerBootstrapAcceptor请求接收器,将boss线程接收到的链接请求封装转发给worker线程

public void channelRead(ChannelHandlerContext ctx, Object msg) {
    try {
        childGroup.register(child).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if (!future.isSuccess()) {
                    forceClose(child, future.cause());
                }
            }
        });
    } catch (Throwable t) {
        forceClose(child, t);
    }
}

2、nioEventLoopGroup/nioEventLoop

netty的线程池和执行线程。

nioEventLoopGroup : 提供了创建、获取nioEventLoop、注册channel的方法。

nioEventLoop : 我们可以看做是线程的扩展,该对象主要负责监听网络事件 并调用事件处理器进行相关的操作。

3、channel

channel接口是netty 对网络操作的抽象 , 包含了基本的IO操作,如bind()/connet()/read()/write()等 , 其主要实现如下:

NioServerSocketChannel , 对应 java nio的 ServerSocketChannel

NioSocketChannel , 对应 java nio的 SocketChannel

4、channelFuture

netty是异步非阻塞 , 我们在发起调用后 , 有可能不能立刻得到我们期待的结果, 所以我们通过channelFuture.addListener ,添加一个回调。

另外我们还可以通过sync , 是的异步非阻塞变为同步阻塞式调用

5、channelPipeline

channelPipeline是channelHandler的双向链表,提供了一个容器定义并处理流转与处理器链上的channel。

一个channel只能绑定于一个channelPipeline , 也就只能在此容器内流转 ,局部范围内是串行的,没有线程安全问题

channelPipeline 定义了handler的head和tail , 可以沿着责任链依次处理。

6、channelHandler

是netty消息的具体处理器,负责处理连接、读、写等操作。

十三、高性能

基于I/O多路复用模型

零拷贝

基于NIO的Buffer

基于内存池的缓冲区重用机制

无锁化的串行设计理念

I/O操作的异步处理

提供对protobuf等高性能序列化协议支持

可以对TCP进行更加灵活地配置

以上是关于Netty理论四:TCP vs UDP的主要内容,如果未能解决你的问题,请参考以下文章

做了Android这么多年,TCP/UDP协议了解了没?

理论经典:TCP协议的3次握手与4次挥手过程详解

Netty之1-TCP_UDP协议

浅谈UDP(数据包长度,收包能力,丢包及进程结构选择)

iOS Socket理论知识

netty知识总结