Android 网络编程之TCP、UDP详解

本章目录

  • 1、TCP,UDP是什么?
  • 2、传输层
  • 3、TCP连接的特点有哪些?
  • 4、TCP包头格式
  • 5、TCP状态机
  • 6、三次握手?
  • 7、四次挥手?
  • 8、TCP连接是怎么保证可靠性的?
  • 9、UDP包头格式是啥?
  • 10、UDP连接的特点有哪些?
  • 11、UDP连接的使用场景有哪些?

本章结构图

1、TCP,UDP是什么?

1.1、为什么要进行网络分层?

在上一篇文章里讲到了网络的分层,提到了TCP/IP组模型,

如下:

但是是否有这样的疑问,为什么网络要进行分层呢?

计算机网络是一个非常复杂的系统,要想在其中两台主机建立连接,必须要考虑非常多的场景,假如我们要在两台主机之间传送一个文件,那么我们需要考虑的方法具体有下面几点:

  • (1)线路:首先发起通讯的主机必须要先激活传送的通讯线路,保证要传送的数据在这条线路上可以正常的发送和接受;
  • (2)接收主机:要告诉网络数据要发送到哪台主机去;
  • (3)确保到达:发起通讯的主机要确保接受的主机是否已开机,是否处于正常工作状态;
  • (4)确保接收文件:发起通讯的主机要确保对方主机是否已经做好接受和处理文件的准备工作;
  • (5)确认文件格式:确保接收的主机是否已经做好可以处理这种文件格式的准备,如果没有的话,两个主机之间必须有一台可以处理文件转换;
  • (6)确保数据正常到达:传送过程中如果遇到异常请求,比如丢包,网络异常,传送错误,重复等情况,要保证数据可以正常传送到接收的主机,就必须得要有各种各样的容错处理;

这里只列举了大概几种情况,还有其他很多场景要处理,我这里就不一一列出来了;

从这里可以看出,就只是一个文件的传递,都需要考虑这么多场景,可想而知,计算机网络是何其的复杂;

那么既然有这些问题存在,让我们试想着如何去解决这种问题;

既然场景这么多,那么我们能不能将这些场景进行归类,然后进行分层,每一类场景交由某个类别去进行处理呢?

答案是:可以的;

上面这个文件的传送,我们可以归类为三层:文件传输,通讯服务,网络接入这几层;

看一下大概的流程图:

当然上面只是我们的假想,实际上的模块划分远比这个复杂的多;

比如最开始的OSI模型,足足分了七层;

到这里你是否有疑问了,这样进行分层的好处有哪些呢?请看下面;

分层的好处:

  • (1)每一层是相对独立的,只需要处理自己负责的模块就行,通过将相对复杂的问题,分解为几部分小的问题,交由对于的模块去处理,以此来解决复杂的问题;
  • (2)灵活性好,如果某一层逻辑发生变动,只要其对外开放的接口没有发生改变,就不会影响到其他层面的工作,模块之间相对比较灵活;
  • (3)结构可以分割开,每一层都可以采用比较适合自己的数据,不必考虑其他模块的影响;
  • (4)易于实现和维护,如果某一层需要修改什么东西,只要其对外开放的接口没有改变,那么其修改就不会影响到其他层的工作,因为模块之间耦合性低;
  • (5)能够提供精准化服务,每一层的职责都很明确,都会提供相应的服务;
1.2、TCP,UDP是什么?

TCP(传输控制协议)和UDP(用户数据报协议)是传输层的协议,基于网络层IP协议来实现的;

TCP协议为两个主机的通讯提供可靠性传输,通过将数据切分为适合传输的大小,使用超时重传,确认到达等一系列操作来保证数据可以准确的进行传输;由于提供了端到端的可靠性传输,应用层的软件就不需要关心传输的相关细节了;

UDP协议提供简单快速的数据传送,但是不保证数据的准确性,稳定性,因此使用这个协议的应用层的软件需要考虑稳定性和准确性的问题;

2、传输层

2.1、传输层是怎么工作的?

应用程序在通讯的时候,需要先建立起连接,而建立连接的基础需要先标志自己的进程id,也就是PID,如果只是本地通讯的话,PID基本上不会有冲突,但是和网络上的其他主机通讯时,PID的冲突几率就会大大增加;

那么要怎么解决这个问题呢?

我们知道IP层的协议的IP地址在网络中是唯一的,不会和其他主机冲突,那么我们可以利用这个来避免端口号冲突,也就是IP地址+端口号的实现;

而这种标识就是socket通讯,最终的格式:Socket=(IP地址:端口号);

2.2、socket是什么?

socket翻译过来的意思就是插座,用于应用程序通过网络进行通讯,也被称为套接字;

套接字可以看出是两个应用程序通过传输层传输数据时,使用的端点,而套接字是一个抽象的概念,具体实现是由计算机来实现的;

比如当前主机要和另外一台主机进行数据的传输,会先将数据写入到socket中,然后通过传输层传输到另外一台主机的socket中,然后应用程序再从socket中读取数据;

2.3、socketAPI函数

socket是"打开—读/写—关闭"的实现模式,具体实现如下:

  • socket():根据地址类型,socket类型,协议类型创建socket;
  • bind():一般用于服务端,给socket绑定IP地址和端口号;
  • listen():用于服务端,监听该端口的请求;
  • accept():用于服务端,接收来自客户端的连接请求;
  • connect():用于客户端,连接指定计算机的端口;
  • send()/recv():从socket中读取和发送数据;
  • close():关闭socket;
  • gethostbyname()和gethostbyaddr():用于解析主机名和地址。
  • select():用于修整有如下情况的套接字列表: 准备读,准备写或者是有错误。
  • poll():用于检查套接字的状态。 套接字可以被测试,看是否可以写入、读取或是有错误。
  • getsockopt() 用于查询指定的套接字一个特定的套接字选项的当前值。
  • setsockopt() 用于为指定的套接字设定一个特定的套接字选项。
2.4、socket在TCP的基本流程

2.5、socket在UDP的基本流程

3、TCP连接的特点有哪些?

3.1、面向连接

TCP是面向连接的协议,也就是在进行通信之前,必须先建立起连接,通信完之后,必须要释放连接;

3.2、点对点

每条TCP连接只能有两个端点,不能一对多,或者多对一,只能一一对应,不支持多播(multicast)和广播(broadcast)的传输方式;

3.3、可靠交付

TCP提供可靠交付,保证数据不出错,不重复,不丢失并且有序的传达给对方;

3.4、全双工通信

TCP通信的双方都可以同时向对方发送数据和接收对方发送的数据,并且在本地设置有发送缓存和接收缓存,用于应用程序发送数据和取数据,这样应用程序就不用关心数据的发送和接收了,只需要将数据交给传输层即可;

3.5、面向字节流

流:指的是流入到进程或者从进程流出的字节序列;

应用程序将要传输的数据写入到TCP的发送缓存中,然后TCP再根据实际传输,从发送缓存取出一定的字节序列来进行传输,而另一端接收到数据后,会将数据放入到接收缓存中,应用程序再从接收缓存中读取字节数据;

这其中,应用程序写入发送缓存的字节序列并不代表就是TCP要发送的字节序列,TCP会将发送缓存中的字节序列拆分成适合传输的序列来进行传输;

这个图只讲了大概的流程,实际上数据还得通过IP层和数据链路层的封装才能进行传输;

4、TCP包头格式是啥?

4.1、先来看一下TCP数据的结构:

4.2、看一下TCP首部报文段的结构图:

  • (1)源端口号和目标端口号:每个TCP段都包含源端口号和目标端口号,用于确认TCP数据的来源和要发送的端口号,而计算机上的程序是通过监听端口号来获取TCP数据包的;

  • (2)序号:用于标记这个TCP包的顺序,因为TCP在传送过程中,会先将数据切割为无数个小的数据包,标记了顺序之后,在送达到终端时,重新拼接的时候就不会出错,用于解决数据包错乱的问题;
    既然每个传输的字节都被计数,确认序号包含发送确认的一端所期望收到的下一个序号。因此,确认序号应当是上次已成功收到数据字节序号加1。只有ACK标志(下面介绍)为1时确认序号字段才有效。

  • (3)确认序号:用于确认接收终端需要接收的下一个包的序号,通过为当前包的序号+1,用于保证包传送的顺序;

  • (4)头部长段:由于TCP首部包含一个可变的部分,因此需要一个值来表示当前TCP数据的长度,也就是头部长段;

  • (5)保留位:留待以后使用;

  • (6)标志位:指数据包的属性,用于控制TCP的状态机;
    URG: 表示报文段是否包含紧急数据,当URG=1时才表示有紧急数据;
    ACK: 表示报文段确认序号是否有效,当ACK=1才表示有效,而TCP建立连接之后,发送报文的标志位必须是ACK=1;
    PSH: 用于告知接收方是否需要立即将数据传递给上层,当PSH=1生效,否则就缓存起来;
    RST: 当RST=1,表示当前链接出现了不可知的错误,需要重新建立连接以确保正常的通讯;
    SYN: 在建立连接时使用,当SYN=1时表示用于请求建立连接或者相应建立连接,用于TCP握手使用;
    FIN: 用于标记数据是否发送完毕,当FIN=1时生效;

  • (7)窗口:大小有字节表示,申明接收端所期望接收的字节大小,用于流量控制,不至于传的数据包过大导致接收端接收不了数据,或者传的数据包过小,导致浪费网络资源;

  • (8)校验和:用于TCP数据的校验,是TCP传输可靠性的保障之一,由发送端填充,接收端进行校验;

  • (9)选项:用于额外的功能实现;

5、TCP状态机

5.1、TCP状态机是什么?

TCP状态机是指网络通信过程中,状态变化的情况;

在TCP通信的三次握手和四次挥手的过程中,就是TCP状态变化的过程,由于TCP是面向连接的,具有可靠性的连接,每一个要通信的主机都得和对方建立起连接,而这过程会经历连接,传输,关闭这几个步骤,在双方进行通信的过程,TCP的状态变化是不一样的;

5.2、TCP状态有哪些?
  • 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接受到连接中断请求的确认;
  • CLOSED:没有任何连接状态;

来看一下《计算机网络》关于状态机的流程图:

至于状态机的变化,我们下面的三次握手和四次挥手会讲到;

6、三次握手

6.1、什么是握手?

对于人类来说,握手表示双方友好交互的意思;而对于计算机来说,握手的意义就大不相同了;

来看一下wiki对于计算机握手的定义:

意思就是:在信息传输之前,握手用于确认参数和其他协议特性达成一致;

而对于tcp的握手来说,是两台主机之间互相交换一次tcp数据,用于确认参数和序列;每一次握手,主机都得向对方发送一次tcp数据;

6.2、三次握手是什么?

1,客户端发送信息告诉服务端我要进行连接;

这时候发起连接的主机会先发送一个TCP数据包,数据包含标志位SYN=1,序列号 seq=x,然后发起连接的主机进入SYN-SENT(同步已发送阶段);

第一次握手完成;

2,服务端告诉客户端我收到请求,并且准备好接收数据了;

收到发起连接请求的主机会先解析数据,然后发送一个TCP数据包做为回应,数据包包含 ACK=1(表示当前确认号有效),SYN=1(表示请求连接有效),确认号ack=x+1(表示期望下一次收到TCP数据的序列号), seq=y(表示为自己初始化一个序列号),然后接收连接的主机会进入SYN-RCVD(同步接收状态);

第二次握手完成;

3,客户端发送消息表示我要开始发送数据了,服务端收到后表示连接建立成功;

发起请求的主机接收到另一台主机发送的确认数据后,会再向要连接的主机再发送一个确认数据,数据包包含ACK=1(表示当前确认号有效),确认号ack=x+1(表示期望下一次收到TCP数据的序列号),seq=x+1(自己的初始化序列号+1),此时TCP建立连接成功,发起连接的主机进入ESTABLISHED状态(表示建立连接成功);

第三次握手完成;

TCP连接成功建立;

看一下流程图:

6.3、为什么是三次握手?

让我引用《计算机网络》一书上的说法:

为了防止已经失效的连接请求报文段突然又传到服务端,因而产生错误;

对于这句话的解读:

对于从客户端发送到服务端的请求连接的报文,因为网络上的延迟,或者阻塞等原因,导致连接已经释放后再被服务端接收到,此时服务端再给客户端发送报文表示连接建立成功;

这时候发送给服务端的报文有可能是已经失效的报文了,而服务器在接收到报文后,就建立起连接,这样会导致很多无效连接的建立,大大的浪费了资源;

而客户端接收到服务端发送的确认报文时,客户端需要再发送报文确认是否要建立连接,而不是发送一次报文就建立起连接了;

那么事实上真的是这样吗?

上面这种说法其实是偏向于抽象的说法,而实际上还得从TCP的可靠性传输说起:

为了实现可靠性传输,TCP通信的双方都必须要先维护一个初始化的序列号,标识当前发送TCP数据的序号,用于接收到TCP数据的主机判断哪些数据是收到的,哪些是没收到的;

而三次握手的过程是为了告诉对方自己数据的起始序列号,和期望下次收到数据的确认序列号,而一次握手,两次握手都是不能实现这种操作的,如果是四次握手呢? 可以,但是没有必要,因为会浪费资源;

三次握手的目的就是为了同步双方的序列号,用于可靠性传输的保障,这个下面会讲到;

具体如下所示:

7、四次挥手

7.1、什么是挥手?

挥手的意思,对于人类来说就是告别的含义,挥挥手不带走一片云彩。

而对于TCP连接来说,
TCP连接断开时,要断开的两台主机会互相发送报文给对方,每发送一次报文就叫一次挥手;

7.2、四次挥手是什么?

假设有两台已经建立起连接的主机,分别为主机A和主机B,主机A主动请求断开连接;

第一次挥手:主机A会发送一个报文段(序列号seq=p,标志位FIN=1),然后进入FIN-WAIT-1状态,发送了标志位FIN=1之后,表示我要关闭这个连接,后续将不会再有数据传输过来,但是这时候主机A还是可以收到主机B发送过来的数据;

第二次挥手:主机B接受到主机A发送的FIN报文段,会发送一个确认报文段,标志位ACK=1,确认序列号ack=p+1,然后就进入CLOSE-WAIT状态,这个过程表示接受到关闭方发送的关闭请求了。

这时候主机B就处于半关闭状态,虽然主机A不会再发数据过来,但是主机B还可能会发数据给主机A;

主机A接收到确认报文后,会进入FIN-WAIT-2状态,然后等待主机B发送连接释放报文;

第三次挥手:当主机B的数据都发送完毕后,就会想主机A发送释放连接的报文,主机B发送FIN报文段,标志位FIN=1,序列号seq=q,确认序列号p+1,标志位ACK=1,然后就进入LAST-ACK状态;

第四次挥手:主机A接收到关闭报文段后,会发出确认报文段,标志位ACK=1,确认序列号ack=q+1,序列号seq=u+1,然后进入TIME-WAIT状态,此时TCP连接还没有释放,会等待2∗MSL(最长报文段寿命)的时间后,才会进入CLOSED状态;

主机B接收到确认报文之后,才会进入关闭状态,至此,TCP连接结束,在此看来,接收关闭的主机TCP连接的关闭时间会更快一些。

为什么要等待2∗MSL之后,才会进入关闭状态呢?

MSL的全称为:Maximum Segment Lifetime,翻译过来的意思是:最长报文端寿命,表示报文在网络上存在的最长时间,超过这个时间报文将被丢弃;

这个值是由当前主机设置,TCP允许设置为不同的值;

主要的作用有两个:

(1)保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失

总结,虽然进行了4次挥手,但是做的操作不止四次;

挥手只是一个告知的消息,发送方和接收方都得做出响应操作;

请看流程图:

7.3、为什么是四次挥手?

上面我们分析了TCP的四次挥手,那么是否有这样的疑问,为什么TCP连接断开要进行四次挥手?

在分析为什么要四次挥手之前,我们先来了解一下TCP连接的特性,TCP连接是全双工通信的;

全双工通信的意思是TCP连接的双方都可以同时进行发送和接收消息,这个上面有讲过;

因为TCP连接的双方都能同时进行发送和接收消息,那么要关闭连接的话,连接的双方都要关闭,不能只关闭一方;

假设主机A和主机B正在进行TCP连接通信,这时候主机A想断开连接,那么建立起连接的双方都要关闭发送和接收通道,这仅仅发送一次报文的无法实现的,主机A关闭自己的发送通道和接收通道,得进行两个报文的发送,那么主机B同理,也是得发送两次报文来关闭自己的发送和接收通道;

那么到这里你是否就会有疑问?

我发送一次报文,就关闭自己的发送通道和接收通道不就行了,这样TCP断开连接只需要两次挥手就可以了,不是吗?

当然理论上是可以这样做的,只要发送一次报文,就关闭自己的发送和接收通道,但是实际情况为什么不是这样做的呢,且听我细细道来;

假如要断开连接的主机A,发送一次TCP报文,告诉主机B我要断开连接,然后就关闭自己的读写通道;

那么这里会导致什么问题呢?

(1)关闭了写的通道,表示我没有数据要发送给主机B了,如果在网络正常不丢包的情况下,是可以直接关闭写的通道的,但是如果发送的这个数据丢包了,主机B没有接收到这个报文,那么主机B就认为当前的连接有效,就继续从主机A读取数据,这样会导致传输出现问题;

(2)关闭了读的通道,表示我不读取主机B发送过来的数据了,但是这时候并不确定主机B是否还有没有数据要发送过来,如果有数据要发送过来,而主机A却关闭了读的通道了,那么也会导致传输出现问题;

既然没法发送一次TCP报文来关闭读写通道,那么要怎么做才能比较合理,比较符合TCP可靠传输的特点呢?

那么接下来就要来讲讲前人智慧的结晶了;

假如让你来设计一个关闭TCP连接的方案,你会怎么设计呢?

首先我们先来看一下TCP连接关闭的要求:

(1)TCP连接的主机双方都要关闭读写通道;
(2)TCP连接的主机双方都要保证数据的正常传输;
(3)要符合TCP可靠传输的特点;

那么我们知道了上面的要求,接下来来看看怎么设计比较好呢?

既然不能一次性关闭读和写的通道,那么我们来分开进行关闭;

假设有两台正在进行TCP连接的主机,分别为主机A和主机B,主机A要求要断开连接;

(1)首先主机A先发送一次报文,告诉主机B,我要关闭写的通道了,表示我没有数据要发送给主机B了;

(2)这时候主机B有两种情况,一种情况是正常收到了主机A发送的要关闭写的通道的报文,另一种情况是没有收到主机A发送的要关闭写的通道的报文;

第一种情况:当主机B收到报文时,知道了主机A不发送数据过来了,那么主机B就关闭读的通道,那么这时候需要通知一下主机A,表示我已经收到你的报文了,不然的话主机A并不知道主机B到底收没收到;

第二种情况:主机B没有收到报文,这时候主机A怎么确认主机B是否收到了报文了,当然得要主机B告诉主机A才行,但是主机B没有收到报文,就不会告诉主机A是否已经收到报文了,那么主机A在发送了报文一段时间后,如果没有收到主机B的回应,那么主机A就得再发送一次报文给主机B;

综合上面两种情况,我们知道,主机B收到报文后,必须要告诉主机A,我已经收到报文了,可以不用再发送报文给我了;

那么到这里主机A可以正常关闭写的通道,主机B可以正常关闭读的通道;

主机A关闭了写的通道后,还没有关闭读的通道,而主机B关闭了读的通道,还没有关闭写的通道;

接下来我们继续分析:

(1)主机B在发送完所有数据后,发送报文告诉主机A,表示我这边数据已经传输完毕,要关闭写的通道了;

(2)这时候主机A也面临着上面的这种情况,一种是收到,一种是没收到,我这里就不再多说了;

最后的情况也是主机B发送报文给主机A,表示要关闭写的通道,而主机A接收到报文后,必须要发送报文给主机B,表示我已经收到了,这个和上面的情况一样,这里就不再多说;

至此,TCP连接的双方要正常的关闭,就必须要发送四次报文,也就是经典的四次挥手;

我这里只是讲了大概的原理,实际情况远比这要复杂多,这其中还涉及到状态机的变化,这个可以看看上面四次挥手的分析👆;

8、TCP连接是怎么保证可靠性的?

TCP是基于IP层的协议来进行传输数据的,而IP层的协议是不靠谱的传输协议,那么TCP协议是怎么基于不靠谱的IP层协议来做到可靠传输的呢?

接下来让我们来看看他的真面目吧!

8.1、校验和

什么是校验和?

TCP校验和(Checksum)是一个端到端的校验和,由发送端计算出来,然后接收端进行验证,用于校验数据在发送前和发送后时候一致,如果接受方检测到不一致了,则会丢弃该数据;

8.2、确认应答与序号

每一个数据包都有序号,用于数据有序的传输,TCP在接收到数据后,会根据序号进行排序,然后再把数据交给应用层处理;

而主机接受到TCP数据后,必须要发送一个确认应答的TCP数据,用于表示我已经收到传过来的数据了,序号是多少,并且告诉发送的主机下一次发送时,起始序号是多少;

如图所示:

8.3、超时重传

在进行TCP传输时,每次接受到数据后,都会给发送端返回一个确认应答,用于确认接收端已经接收到数据了;

但是实际上网络是很不稳定的,数据发送了,接收端不一定就会接收到数据,或者接收端接收到了数据,但是确认报文发送端不一定能接收到;

主要有两种情况:

(1)发送端发送了数据,但是由于延迟的问题,导致接收端迟迟没有接收到数据;
(2)接收端接收到了数据,但是也是由于网络问题,导致发送端没有接收到确认报文;

那么这上面两种情况,TCP是怎么来解决的呢?

TCP引入了一种叫超时重传的机制,可以理解为,当数据发送了之后,经过一段时间,没有收到响应,那么就再重新发送一次数据给对方;

这里的超时重传机制是根据是否收到确认报文为准的,也就是说上面两种情况都可以归类为一种情况,就是发送端是否可以接收到确认报文;

而报文的超时时间是动态计算的,因为网络的复杂性,如果超时时间设置为固定的,那么有可能会造成超时时间过长或者过短的问题,而超时时间的计算是由一个严谨的算法来计算的,这里就不过多深入了;

感兴趣的请参考:超时重传的时间计算

8.4、流量控制

(1)窗口

从传输的角度来看,我们肯定希望传输速度越快越好,那么我是否可以把所有数据都一次性发给对方呢?

很明显是不可以的,如果数据小的话可能不会有问题,但是如果数据量很大的情况下呢,自己的发送缓存和对方的接收缓存是否可以放得下这么多数据呢,这种做法是不严谨的,并且很容易出错,这并不符合TCP可靠性传输的特性;

那么我们怎么来判断要传的数据的大小呢?

原理很简单,就是由对方来告诉你,接收的一方来告诉发送方我可以接收多大的数据而不会出错;

那么TCP是怎么实现的呢?

通过TCP的窗口,这个在上面的TCP报文那里有讲过👆;

(2)传输效率

试想有一种情况,当接收方的缓存已经满了,而应用程序每次都只从TCP缓存中取出一个字节的数据,然后接收方就告诉发送方,我现在可以接收一个字节的数据了,这样会不会有问题呢?

一次传输一个字节的数据,虽然不会有什么问题,但是对于传输的效率来说,效率太低了,一次传输只传输了一个字节的数据,会造成资源的浪费;

那么要怎么解决这种问题呢?

通过让接收方多等待一端时间,等到接收缓存中的剩余空间较多时,才发送确认报文,并带上窗口信息来告诉发送方,可以发送对应窗口大小的数据了,这样就可以避免传输效率的问题;

8.5、拥塞控制

网络的传输也是需要进行控制的,试想如果在网络能接收传输的程度下,我们不管不顾的往网络中传输超过其能接受的程度,那么就会造成网络的阻塞;

比如一条高速公路,其能容纳的车流量也是有限的,如果是正常的情况下,高速公路是可以正常运行,不会堵车的,但是如果在节假日放假的情况下,一大堆车会涌向高速公路,而这时候超过了高速公路所能容纳的流量,就会造成堵车,而网络的情况也是如此;

拥塞控制和流量控制不一样,拥塞控制是作用于网络,用于控制网络中传输的效率,而流量控制是作用于接收方;

常见的拥塞控制有:
(1)慢开始,拥塞避免(2)快重传,快恢复

拥塞控制的算法有哪些呢?

(1)慢开始算法

发送方维持一个叫做拥塞窗口cwnd(congestion window)的状态变量,而这个窗口的大小是根据网络的拥塞程度来动态变化的;

拥塞窗口的大小取决于网络的拥塞程度,并且会根据拥塞程度来进行动态的变化,发送方的发送窗口等于拥塞窗口的大小,而还要考虑到接收方的流量控制窗口,那么可能会出现发送窗口小于拥塞窗口的情况;

而慢开始算法的实现原理就是,一开始向网络发送一个很小的数据包,用于探测网络的拥塞程度,而后再动态的加倍增加,直到达到网络所能接受的程度而不造成网络拥塞;

(2)拥塞避免算法

拥塞避免算法的原理就是,每次在发送数据包的时候,只增加很小的窗口容量,比如cwnd加1,而不是加倍的增加;

而上面的慢开始的算法如果每次都进行加倍的增加,就会很容易出现网络拥塞的情况,这样就无法计算的很精准的拥塞窗口容量;

那么我们可以把慢开始算法和拥塞避免算法结合,如果慢开始算法加倍后,出现了网络拥塞,那么就把之前加倍的去掉,然后再采用拥塞避免算法,逐渐的递增,来试探网络的拥塞窗口大小;

拥塞的程度判断,一般都是通过有没有接收到确认报文,当然可能其他情况也会导致没有接受到确认报文,但是其他情况都是统一当成拥塞的情况处理了;

拥塞避免算法并不是可以百分百的避免网络拥塞,而是会降低出现网络拥塞出现的概率;

(3)快重传算法

快重传算法的实现原理是,要求接收方在接收到一个失序的数据后,就立刻发出重复确认报文,而不是要等到发送数据的时候捎带确认报文,这样可以使发送方及早的知道有哪些报文没有接收到;

(4)快恢复算法

快恢复算法的实现原理的是,当进行了快重传算法时,发送方接收到了重复的确认报文,而这时候就认为网络还没有处于拥塞的情况,那么就将当前传输速度减半后,不走慢开始算法;

因为网络没有处于拥塞的情况,那么就执行拥塞避免算法,使cwnd缓慢增大,让网络传输速度快速恢复;

综上所诉,总结为如下流程图:

ssthresh:慢开始门限

cwnd:拥塞窗口

8.6 连接管理

至于连接管理,那就是著名的三次握手四次挥手机制,这个上面已经讲的很详细了👆,这里就不重复讲了;

9、UDP包头格式是啥?

UDP包头格式没有TCP那么复杂,因为UDP传输是不可靠的,就没有TCP那么多机制;

和TCP相比,UDP的包头就很简单了;

  • (1)源端口号和目标端口号:每个TCP段都包含源端口号和目标端口号,用于确认TCP数据的来源和要发送的端口号,而计算机上的程序是通过监听端口号来获取TCP数据包的;
  • (2)校验和:用于TCP数据的校验,是TCP传输可靠性的保障之一,由发送端填充,接收端进行校验;

10、UDP连接的特点有哪些?

10.1、面向无连接

发送数据前不需要建立连接,那么发送结束之后也不需要释放连接,速度较快;

10.2、尽量交付

UDP传输尽最大的努力传输,但是并不保证可靠性传输,不需要维持连接的状态;

10.3、面向报文

发送方的UDP将应用程序交付下来的报文,直接添加首部后,就交给IP层了;

对于应用程序交付下来的报文,不进行拆分,也不合并,应用程序传啥,那么UDP就传啥;

10.4、没有拥塞控制

即使网络出现拥塞了,也不进行拥塞控制,对于一些需要实时性的应用程序,并不需要考虑网络的拥塞;

比如直播,或者游戏等软件,可以允许网络丢包,但是对于传输的实时性要求较高;

10.5、灵活性高

支持一对一,一对多,多对一,多对多等通信;

10.6、首部开销小

首部只有8个字节,比TCP的20个字节的首部要短;

11、UDP连接的使用场景有哪些?

(1)网页或者APP的访问

访问网页和手机 APP 都是基于 HTTP 协议的。HTTP 协议是基于 TCP 的,建立连接都需要多次交互,对于不稳定的网络环境来讲,建立一次连接需要的时间会比较长,然而既然是移动中,TCP 可能还会断了重连,也是很耗时的。

而QUIC(全称 Quick UDP Internet Connections,快速 UDP 互联网连接)是Google基于UDP改进的一种通信协议,旨在减少数据传输及创建连线时的延迟时间,双向控制带宽,以避免网络拥塞;

(2)流媒体的协议

多用于直播方面的视频传输协议,比如上面提到的QUIC协议;

(3)实时游戏

对于游戏来说,实时性的操作非常重要,而对于UDP来说,实时性正是其特点;

(4)物联网

TCP对于物联网来说,维护的成本较高,而UDP对于物联网的实时性来说,是非常适合的;
比如CoAP协议,就是基于 UDP 协议的;

参考&感谢

TCP报文段首部格式详解
传输控制协议
socket是什么?套接字是什么
TCP流量控制、拥塞控制
快速UDP网络连接
简单理解Socket
tcp udp流程图
两张动图-彻底明白TCP的三次握手与四次挥手
TCP 为什么三次握手而不是两次握手(正解版)
《计算机网络》
极客时间-趣谈网络协议

其他

Android 你不得不学的HTTP相关知识

关于我

兄dei,如果我的文章对你有帮助的话,请帮我点个赞吧️,也可以关注一下我的Github博客;

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux网络编程TCPUDP是两种常见的传输协议。 TCP(Transmission Control Protocol)是一种基于连接的可靠传输协议。它提供了面向连接、可靠的数据传输服务。在TCP通信,数据被分割成小的数据块,通过TCP连接按序传输,并且保证数据的可靠性,即使在网络拥塞或数据丢失的情况下也能重新传输丢失的数据。TCP适用于对可靠性要求较高的应用程序,如文件传输、电子邮件和网页浏览。 UDP(User Datagram Protocol)是一种无连接的不可靠传输协议。它提供了一种无序、不可靠的数据传输服务。在UDP通信,数据以数据包(也称为数据报)的形式发送,不进行连接建立和断开,也不保证数据的可靠性和按序传输。UDP适用于对实时性要求较高、对数据可靠性要求较低的应用程序,如音视频流媒体、在线游戏等。 在Linux进行TCPUDP网络编程可以使用Socket API。该API提供了一组函数和数据结构,用于创建套接字(socket)、绑定(bind)套接字到特定的IP地址和端口、监听(listen)连接请求、接受(accept)连接、建立连接(connect)、发送(send)和接收(receive)数据等操作。 你可以使用C语言或其他支持Socket API的编程语言来进行Linux网络编程,通过调用Socket API提供的函数来实现TCPUDP通信。在编程过程,你需要了解TCPUDP的特点、使用套接字创建相应的连接类型、发送和接收数据的方式等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值