TCP与UDP的特性与区别(详细)

一.引入

计算机网络体系大致分为三种,OSI七层模型、TCP/IP四层模型和五层模型。

传输层(有时也叫运输层)负责向两台主机进程之间的通信提供数据传输服务。传输层的协议主要有传输控制协议TCP和用户数据协议UDP。

用户数据报协议(User Datagram Protocol)简称UDP协议,它是在IP的数据报服务上增加了端口和简单的差错检测来实现进程到进程之间的数据传输。

TCP(Transmission Control Protocol,传输控制协议) 是一种面向连接的、可靠的、基于字节流的传输层通信协议

二.UDP

1.UDP协议段格式

源端口号: 操作系统自动分配的
这个字段占据 UDP 报文头的前 16 位,通常包含发送数据报的应用程序所使用的 UDP 端口。接收端的应用程序利用这个字段的值作为发送响应的目的地址。

目的端口: 服务器提前准备好的端口
接收端计算机上 UDP 软件使用的端口,占据 16 位。所以有效载荷向上交付的问题UDP使用目的端口号来解决

UDP长度: 该字段占据 16 位(2字节),表示 UDP 数据报长度,包含 UDP 报文头和 UDP 数据长度
因为 UDP 报文头长度是 8 个字节,所以这个值最小为 8字节。也因此报头和有效载荷的分离问题

UDP使用报头定长8字节来解决。

UDP校验和: 该字段占据 16 位,可以检验数据在传输过程中是否被损坏。如果校验和出错, 就会直接丢弃;

端口号(Port)标识了一个主机上进行通信的不同的应用程序;

执行下面的命令, 可以看到知名端口号(centos):

cat /etc/services

 2.UDP数据包长度

在协议端格式中我们提到UDP长度该字段占据 16 位(2字节)=2^16次方=65536byte=64KB。

也就是说一个UDP能传输的数据最大长度是64K(包含UDP首部).64K在当今的互联网环境下, 是一个非常小的数字.一个游戏随便都是几个G,那不得下几个小时。同时如果我们需要传输的数据超过64K,那么只能自己在应用层将数据拆开,然后再发送。

3.UDP特性

        有三点:无连接,不可靠,面向数据报。

       UDP是面向数据报,即:应用层交给UDP多长的报文, UDP原样发送, 既不会拆分, 也不会合并。

用UDP传输100个字节的数据: 如果发送端调用一次sendto, 发送100个字节, 那么接收端也必须调用对应的一次recvfrom, 接收100个字节; 而不能循环调用10次recvfrom, 每次接收10个字节;这也就导致UDP不能够灵活的控制读写数据的次数和数量;

操作系统在收到 UDP 报文后,会将其插入到队列里,队列里的每一个元素就是一个 UDP 报文,这样当用户调用 recvfrom() 系统调用读数据的时候,就会从队列里取出一个数据,然后从内核里拷贝给用户缓冲区。读一个 UDP 报文就能读取到完整的用户消息。

这里便存在一个问题我们如何知道一个报文是否是完整的呢?

报文<8字节一定不完整,因为报头都不完整,报文>=8字节时,16位UDP长度-8字节就是有效载荷。

       UDP是不可靠的

           (1)不提供可靠性保证:

                UDP在传输数据时,不提供可靠性保证,不保证数据包的可靠传输。UDP数据包在传输过程中,有可能会出现数据包丢失、重复、乱序等问题。由于UDP不提供可靠性保证,因此应用层需要自行设计协议和算法来处理这些问题。

           (2)不进行流量控制和拥塞控制

                 UDP不进行流量控制和拥塞控制。UDP发送方在发送数据时,不会根据网络的状况进行调整,而是直接发送数据包。如果网络出现拥塞,UDP数据包会丢失或延迟,甚至导致网络更加拥塞。

       UDP是无连接的:因为使用UDP协议的发送者和接受者之间不必存在任何长期的关系。它们没有建立连接过程,知道对端的IP和端口号就直接进行传输,整个消息传输过程简单来说就是“发送即结束。相比于TCP的面向连接的特性,UDP更加轻量和灵活。

4.UDP缓冲区

UDP没有真正意义上的 发送缓冲区. 调用sendto会直接交给内核, 由内核将数据传给网络层协议进行后续的传输动作;

UDP具有接收缓冲区. 内核会有一个有限大小的接收缓冲区来临时存储接收到的 UDP 数据报。但这个缓冲区通常较小,并且如果短时间内收到大量的 UDP 数据报,超出缓冲区容量的数据可能会被丢弃。且这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致; 

UDP的socket既能读, 也能写, 这个概念叫做 全双工


5.基于UDP的应用层协议

NFS: 网络文件系统

TFTP: 简单文件传输协议

DHCP: 动态主机配置协议

BOOTP: 启动协议(用于无盘设备启动) DNS: 域名解析协议

三.TCP

TCP全称为 "传输控制协议(Transmission Control Protocol"). 人如其名, 要对数据的传输进行一个详细的控制;


1.TCP协议段格式

16位源端口:发送方主机的应用程序的端口号

16位目的端口:目的主机的应用程序的端口号。应用层程序会绑定端口号,根据端口号有效载荷可以向上交付

32位TCP序列号:表示本报文段所发送数据的第一个字节的编号

32位TCP确认序号:接收方期望收到发送方下一个报文段的第一个字节数据的编号

4位TCP首部长度:数据偏移是指数据段中的“数据”部分起始处距离TCP报文段起始处的字节偏移量。确定TCP报文的报头部分长度,告诉接收端应用程序,数据(有效载荷)从何处开始

6位保留字段:为TCP将来的发展预留空间,目前必须全部为0 

6位标志位:共有6个标志位,每个标志位占1个bit

URG:紧急指针是否有效
ACK:确认号是否有效
PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走
RST:对方要求重新建立连接; 我们把携带RST标识的称为复位报文段
SYN:请求建立连接; 我们把携带SYN标识的称为同步报文段

FIN:

通知对方, 本端要关闭了

 

16位窗口大小:表示发送该TCP报文的接受窗口还可以接受多少字节的数据量。该字段用于TCP的流量控制

16位校验和字段:用于确认传输的数据有无损坏 。发送端基于数据内容校验生成一个数值,接收端根据接受的数据校验生成一个值。两个值相同代表数据有效,反之无效,丢弃该数据包。校验和根据 伪报头 + TCP头 + TCP数据 三部分进行计算

16位紧急指针字段: 仅当标志位字段的URG标志位为1时才有意义。指出有效载荷中为紧急数据的字节数。当所有紧急数据处理完后,TCP就会告诉应用程序恢复到正常操作。即使接收方窗口大小为0,也可以发送紧急数据,因为紧急数据无须缓存

选项字段:长度不定,但长度必须是32bits的整数倍。内容可变,因此必须使用首部长度来区分选项的具体长度

在TCP中协议报头与有效载荷如何分离?

首先可以看到,TCP首部包括20字节的固定长度首部和选项字段,同时报头中有一个首部长度字段(作用:确定TCP报文的报头部分长度,告诉接收端应用程序,数据(有效载荷)从何处开始

通过报头中的首部长度字段(4bits),可以知道首部长度最大可表示15,即TCP首部最长为15 x 4 =60字节。因为固定首部20字节,所以选项字段最长40字节。当没有选项字段时,首部长度字段为5(20 = 5*4)。这样就可以将TCP协议报头和有效载荷分离。

2.TCP特性

        

             面向字节流,面向连接,可靠传输。

        
      (一)面向连接    

        TCP传输有以下三个阶段:

  • 建立TCP连接,也就是通过三报文握手来建立TCP连接。
  • 数据传送,也就是基于已建立的TCP连接进行可靠的数据传输。
  • 释放连接,也就是在数据传输结束后,还要通过四报文挥手来释放TCP连接。
           (1)TCP的三次握手:

                最初:TCP客户端、服务器都处于关闭状态(CLOSED)

                第一步:TCP服务器进程首先创建传输控制块,用来存储TCP连接中的一些重要信息。 例如TCP连接表、指向发送和接收缓存的指针、指向重传队列的指针,当前的发送和接收序号等。之后就准备接受TCP客户进程的连接请求, 此时TCP服务器进程就要进入监听状态等待TCP客户进程的连接请求。

第二步:TCP客户进程也是首先创建传输控制块,然后再打算建立。 TCP服务器进程是被动等待来自TCP客户端进程的连接请求,因此称为被动打开连接。

 第三步(开始第一次握手

 客户端向服务器发送1个连接请求的报文段。在报文段中的报头中的标志位SYN被设置为1,表明这是一个tcp连接请求报文段,其次初始序列号(seq)假设设置为x。请注意TCP规定SYN被设置为1的报文段不能携带数据但要消耗掉一个序号。

 第四步(第二次握手

TCP服务器进程收到TCP连接请求报文段后,如果同意建立连接,则向TCP客户进程发送TCP连接请求确认报文段。则该报文段中:

首部中的同步位SYN和确认位ACK 都设置为1,表明这是一个TCP连接请求;

序列号字段seq被设置了一个初始值y,作为TCP服务器进程所选择的初始序号;

确认序列号字段ack的值被设置成了x+1,这是对TCP客户进程所选择的初始序号seq的确认。

请注意TCP规定SYN被设置为1的报文段不能携带数据但要消耗掉一个序号。

 

 到这一步为止,可以简单的理解为:你说的话(第一次握手的SYN),我能听到(第二次握手的ACK),那么反过来了,我说的话你能听到吗?故需要第三次握手

第五步(第三次握手

客户端收到确认报文段后,向服务器再次发出连接确认报文段。则该报文段中:

  • 该报文段首部中的确认位ACK被设置为1,表明这是一个普通的TCP确认报文段 。
  • 序列号字段seq 被设置为x+1,这是因为TCP客户进程发送的第一个TCP报文段的序号为x,并且不携带数据,因此第二个报文段的序号为x +1。
  • 确认序列号字段ack被设置为y + 1,这是对TCP服务器进程所选择的初始序号的确认。

第三次握手可以防止服务器端因接收了早已失效的连接请求报文(或因网络原因导致pc端触发重传机制),从而一直等待客户端请求,最终导致形成死锁、浪费资源;其次三次握手的主要目的是确认自己和对方的发送和接收都是正常的,从而保证了双方能够进行可靠通信。若采用两次握手,当第二次握手后就建立连接的话,此时客户端知道服务器能够正常接收到自己发送的数据,而服务器并不知道客户端是否能够收到自己发送的数据。

注意TCP规定,普通的TCP确认报文段可以携带数据。但如果不携带数据则不消耗序号,在这种情况下所发送的下一个数据报文段的序号仍是x + 1且第三次握手没有SYN!!!。

 

 最后,就可以进行数据传输。

  1. TCP提供的是全双工通信,故通信双方的应用进程在任何时候都能发送数据
  2. 三次握手期间,任何1次未收到对面的回复,则都会重发

 服务端的TCP资源分配时刻 = 完成第二次握手时;而客户端的TCP资源分配时刻 = 完成第三次握手时

        如果第三次握手失败?

如果第三次握手失败(即服务器发送了SYN+ACK包,但是没有收到客户端的ACK包),服务器会根据/proc/sys/net/ipv4/tcp_synack_retries的设置进行SYN+ACK的重传,默认重传次数是5次。如果超过重传指定次数后,服务器仍然没有收到客户端的ACK应答,服务器会放弃这个连接,并在一段时间后自动关闭它。client 一般是通过 connect() 函数来连接服务器的,而connect()是在 TCP的三次握手的第二次握手完成后就成功返回值。也就是说 client 在接收到 SYN+ACK包,它的TCP连接状态就为 established (已连接),表示该连接已经建立。那么如果 第三次握手中的ACK包丢失的情况下,Client 向 server端发送数据,Server端将以 RST包响应,告诉客户端这个连接已经不存在了。

(2)TCP的四次挥手:

在挥手前,客户端和服务端都是建立好的!!。

 为什么TCP释放连接需四次挥手?

 保证通信双方都能通知到对方要释放和断开连接。比如:前两次挥手,由客户端发起释放连接报文(FIN),此时服务器知道了客户端要释放连接了并做好准备关闭了;服务端发送确认报文(ACK),此时客户端收到服务端的肯定答案后,便关闭了客户端->服务端的连接。注意此时的服务端->客户端的连接并未断开(因为服务端要保证数据完全传给客户端才能够关闭连接),当服务端传输完数据后,便向客户端发起释放连接报文(FIN),客户端收到释放连接后,便发送同意释放连接报文。此时,服务端和客户端之前才算正在互相通知到位。

为什么客户端关闭连接前要等待2MSL时间(TIME - WAIT 状态的作用是什么)?

  • 原因1:为了保证客户端发送的最后1个连接释放确认报文 能到达服务器,从而使得服务器能正常释放连接
  • 原因2:防止上文提到的早已失效的连接请求报文 出现在本连接中 客户端发送了最后1个连接释放请求确认报文后,再经过2MSL时间,则可使本连接持续时间内所产生的所有报文段都从网络中消失。从而保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启, 可能会收到来自上一个进程的迟到的数据, 但是这种数据很可能是错误的)

TCP协议规定,主动关闭连接的一方要处于TIME_ WAIT状态,等待两个MSL(maximum segment lifetime)的时间后才能回到CLOSED状态。在TIME_WAIT期间不能再次监听同样的server/pc端口,因为应用程序终止了,但TCP协议层的连接并没有完全断开。

  1. MSL = 最长报文段寿命(Maximum Segment Lifetime
  2. TIME - WAIT=2MSL

MSL在RFC1122中规定为两分钟,但是各操作系统的实现不同, 在Centos7上默认配置的值是60s;

解决TIME_WAIT状态引起的bind失败的方法

使用setsockopt()设置socket描述符的 选项SO_REUSEADDR为1, 表示允许创建端口号相同但IP地址不同的多个socket描述符

(3)TCP保活机制

TCP Keep-alive应用场景

一般的,TCP的客户端与服务器的连接类型可以分为:

短链接:客户端连接到服务器后,即开始与服务器交互,请求资源,上报数据等,交互完毕后即断开与服务器的连接,如HTTP协议等。

长连接:客户端连接到服务器后,不一定会立即进行数据的传递,而是一直保持连接状态,且双方一般不会主动断开连接,如MQTT协议等

需要注意的是,不管是长连接还是短连接都不是TCP协议本身所规定的,TCP只是给应用层提供了建立与断开连接的方法与资源管理。

可以想到,当客户端与服务器处于长连接状态下,如果服务器突然断电了,服务器也无法通知客户端异常状况,客户端就无法察觉服务器异常。只有当客户端向服务器发送数据时,由于超时机制,客户端才能知道服务器异常。并且数据也自然丢弃了。而且如果异常连接无法释放,也会导致系统资源的消耗与浪费。所以在长连接下,就可以启用TCP 的keep-alive机制,避免一方可能意外断电、死机、崩溃、重启,还是中间路由网络无故断开,从而导致的异常连接。

保活机制工作工程描述:
连接中启动保活功能的一端,在保活时间内连接处于非活动状态,则向对方发送一个保活探测报文,如果收到响应,则重置保活计时器,如果没有收到响应报文,则经过一个保活时间间隔后再次向对方发送一个保活探测报文,如果还没有收到响应报文,则继续,直到发送次数到达保活探测数,此时,对方主机将被确认为不可到达,连接被中断。

(二)可靠传输

        (1)可靠
                确认应答机制

                TCP将每个字节的数据都进行了编号. 即为序列号。每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你从哪里开始发.就这样,通过序列号和确认应答号,TCP可以实现可靠传输

如果有确认应答,说明数据已经成功到达,如果没有,那么数据有可能丢失了,如下:

主机B会收到很多重复数据. 那么TCP协议需要能够识别出那些包是重复的包, 并且把重复的丢弃掉.去重的效果. 这时候我们可以利用前面提到的序列号。
 

若数据未发出,则情况如下:

 超时重传机制

在前面的确认应答机制中,我们提到两种情况。

1.数据由主机A发出但因为网络等原因导致丢失,从而无法到达主机B。如果主机A在一个特定时
间间隔内都未收到主机B发来的确认应答,将会对此数据进行重发。那么这个特定时间间隔是多少呢?

2.由主机B返回的确认应答,因网络拥堵等原因在传送的途中丢失,没有到达主机A。主机A会等待一段时间,若在特定的时间间隔内始终未能收到这个确认应答,主机A会对此数据进行重发。那么这个等待一段时间又是多少呢?

最理想的情况下, 找到一个最小的时间, 保证 "确认应答一定能在这个时间内返回".但是这个时间的长短, 随着网络环境的不同, 是有差异的.

如果超时时间设的太长, 会影响整体的重传效率;

如果超时时间设的太短, 有可能会频繁发送重复的包;

TCP为了保证无论在任何环境下都能比较高性能的通信, 因此会动态计算这个最大超时时间.

Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控制, 每次判定超时重发的超时时间都是500ms的整数倍.如果重发一次之后, 仍然得不到应答, 等待 2*500ms 后再进行重传.
如果仍然得不到应答, 等待 4*500ms 进行重传. 依次类推, 以指数形式递增.

累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接

校验和(检验和)

        TCP校验和是一个端到端的校验和,由发送端计算,然后由接收端验证。其目的是为了发现TCP首部和数据在发送端到接收端之间发生的任何改动。如果接收方检测到校验和有差错,则TCP段会被直接丢弃。 TCP在计算检验和时,要加上一个12字节的伪首部。注意TCP的校验和是必需的,而UDP的校验和是可选的。TCP和UDP计算校验和时,都要加上一个12字节的伪首部。伪首部共有12字节,包含IP首部的一些字段,有如下信息:32位源IP地址、32位目的IP地址、8位保留字节(置0)、8位传输层协议号(TCP是6,UDP是17)、16位TCP报文长度(TCP首部+数据)。

         伪首部是为了增加TCP校验和的检错能力:通过伪首部的目的IP地址来检查TCP报文是否收错了、通过伪首部的传输层协议号来检查传输层协议是否选对了。

具体校验过程这里不再阐述。


(2)性能
       防止丢包--滑动窗口机制

        为什么要引入滑动窗口机制呢?不是有了确认应答机制和超时重传机制可以可靠传输数据了吗?

举个例子:

在确认应答机制中我们提到,它是通过发+等待确认实现的。也就是说我发你消息后,你要是不给我发确认你收到消息了,我便不给你发消息了,一直等到你说我收到了你可以继续往下说了。这样就显得速度很慢。为了解决这个问题,TCP 引入了窗口,它是操作系统开辟的一个缓存空间。窗口大小值表示无需等待确认应答,而可以继续发送数据的最大值。其中窗口大小为16位,我们可以在TCP协议段格式中可以看到。

滑动窗口组成:

发送方维护一个发送窗口swnd,接收方则会维护一个接收窗口rwnd,它们是一个连续的字节序列。操作系统内核为了维护这个两个窗口, 需要开辟 发送/接收缓冲区 来记录当前还有哪些数据没有应答/发送; 对于发送缓冲区只有确认应答过的数据, 才能从缓冲区删掉;接收缓冲区可以存储这些暂时未处理的数据,避免数据丢失窗口越大, 则网络的吞吐率就越高。

假设发送窗口大小的值可以运行发四个1000的数据,则发送如下:

发送前四个段的时候, 不需要等待任何ACK, 直接发送;
 

 收到第一个ACK后, 滑动窗口向后移动, 继续发送第五个段的数据; 依次类推;

那么如果出现了丢包, 如何进行重传? 这里分两种情况讨论.

情况一: 数据包已经抵达, ACK被丢了

 这种情况下,这需要等待超时后重传ACK(前面提到的超时重传机制),然后确认就行。

情况二: 数据包就直接丢了.

当某一段报文段丢失之后, 发送端会一直收到 1001 这样的ACK, 就像是在提醒发送端 "我想要的是 1001"一样;如果发送端主机连续三次收到了同样一个 "1001" 这样的应答, 就会将对应的数据 1001 - 2000 重新发送;这个时候接收端收到了 1001 之后, 再次返回的ACK就是7001了(因为2001 - 7000)接收端其实之前就已经收到了, 被放到了接收端操作系统内核的接收缓冲区中;这种机制被称为 "高速重发控制"(也叫 "快重传")。

 流量控制

        这里要给大家强调一下,上面所讲到的传输数据的时候,不是简单的客户端发一个“hello”,服务器端给你回一个“I‘m fine,thank you!”。客户端发送完数据的时候,服务器端收到数据,是要对数据经行分析和处理的,当数据处理完成后在发给客户端,所以在处理分析数据是有时间消耗的。没准服务器上一条数据还没处理完,客户端又发来一条。如果发送端发的太快, 导致接收端的缓冲区被打满, 这个时候如果发送端继续发送,就会造成丢包, 继而引起丢包重传等等一系列连锁反应。最后导致不必要的损耗。

        因此TCP支持根据接收端的处理能力, 来决定发送端的发送速度. 这个机制就叫做流量控制(Flow Control);

接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 "窗口大小" 字段, 通过ACK端通知发送端;

窗口大小字段越大, 说明网络的吞吐量越高;
接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端;
发送端接受到这个窗口之后, 就会减慢自己的发送速度;

如果接收端缓冲区满了, 就会将窗口置为0; 这时发送方不再发送数据, 但是需要定期发送一个窗口探测数据段, 使接收端把窗口大小告诉发送端

接收端如何把窗口大小告诉发送端呢? 回忆我们的TCP首部中, 有一个16位窗口字段, 就是存放了窗口大小信息;那么问题来了, 16位数字最大表示65535, 那么TCP窗口最大就是65535字节么?实际上, TCP首部40字节选项中还包含了一个窗口扩大因子M, 实际窗口大小是 窗口字段的值左移 M 位;

拥塞控制

        看到这里,没准大家会认为有了这么多机制总该万无一失了吧。实则不然虽然TCP有了滑动窗口这个大杀器, 能够高效可靠的发送大量的数据. 但是如果在刚开始阶段就发送大量的数据, 仍然可能引发问题.比如:因为网络上有很多的计算机, 可能当前的网络状态就已经比较拥堵. 在不清楚当前网络状态下, 贸然发送大量的数据,是很有可能引起雪上加霜的。因此,TCP引入 慢启动 机制, 先发少量的数据, 探探路, 摸清当前的网络拥堵状态, 再决定按照多大的速度传输数据;

首先我们要先知道什么是拥塞窗口,什么是慢启动?

拥塞窗口

  • 发送方维持一个状态变量:拥塞窗口(cwnd, congestion window )

慢启动,即:当主机开始发送数据时,由小到大逐渐增大 拥塞窗口数值(即 发送窗口数值),从而 由小到大逐渐增大发送报文段。

发送开始的时候, 定义拥塞窗口大小为1;

每次收到一个ACK应答, 拥塞窗口加1;

  像上面这样的拥塞窗口增长速度, 是指数级别的. "慢启动" 只是指初使时慢, 但是增长速度非常快

 为了不增长的那么快,防止拥塞窗口(cwnd)增长过大而引起网络拥塞, 因此不能使拥塞窗口单纯的加倍.此处引入一个叫做慢启动的阈值。当拥塞窗口超过这个阈值的时候, 不再按照指数方式增长, 而是按照线性方式增长。

 当TCP开始启动的时候, 慢启动阈值等于窗口最大值;

在每次超时重发的时候, 慢启动阈值会变成原来的一半, 同时拥塞窗口置回1

 少量的丢包, 我们仅仅是触发超时重传; 大量的丢包, 我们就认为网络拥塞;当TCP通信开始后, 网络吞吐量会逐渐上升; 随着网络发生拥堵, 吞吐量会立刻下降;拥塞控制, 归根结底是TCP协议想尽可能快的把数据传输给对方, 但是又要避免给网络造成太大压力的折中方案。

补充:窗口的大小就是由流量控制,拥塞控制等因素决定的。

提高性能-捎带应答和延迟应答

延迟应答:如果接收数据的主机立刻返回ACK应答, 这时候返回的窗口可能比较小。

如下面这种情况

假设接收端缓冲区为1M. 一次收到了500K的数据; 如果立刻应答, 返回的窗口就是500K;

但实际上可能处理端处理的速度很快, 10ms之内就把500K数据从缓冲区消费掉了;
在这种情况下, 接收端处理还远没有达到自己的极限, 即使窗口再放大一些, 也能处理过来;

如果接收端稍微等一会再应答, 比如等待200ms再应答, 那么这个时候返回的窗口大小就是1M

 一定要记得, 窗口越大, 网络吞吐量就越大, 传输效率就越高. 我们的目标是在保证网络不拥塞的情况下尽量提高传输效率。

那么所有的包都可以延迟应答么? 肯定也不是;

数量限制: 每隔N个包就应答一次;

时间限制: 超过最大延迟时间就应答一次;

具体的数量和超时时间, 依操作系统不同也有差异; 一般N取2, 超时时间取200ms;

 

 捎带应答:

捎带应答是在延迟应答的基础上的。 让ACK的传输时机略有延迟,大概是200ms左右,这足以让应用程序完成响应计算,之后Resp在写回的时候发现刚才的ACK还没有发,就在这个Resp的数据报的基础上带上ACK,顺便传输一下。

 这样就减少了包的传输个数,降低了通信成本,从而提高了效率。

(三)面向字节流

        在讲UDP的时候我们提到,UDP是面向数据报的。通过 UDP 协议传输时,操作系统不会对消息进行拆分所以发出去的 UDP 报文中的数据部分就是完整的用户消息。而TCP不同,用户消息通过 TCP 协议传输时,消息可能会被操作系统分组成多个的 TCP 报文,也就是一个完整的用户消息被拆分成多个 TCP 报文进行传输。因为在传输过程中,会创建一个TCP的socket, 同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区;调用write时, 数据会先写入发送缓冲区中;如果发送的字节数太长, 会被拆分成多个TCP的数据包发出;如果发送的字节数太短, 就会先在缓冲区里等待, 等到缓冲区长度差不多了, 或者其他合适的时机发送出去(这里会引出另一个问题--粘包问题);

        接收数据的时候, 数据也是从网卡驱动程序到达内核的接收缓冲区;然后应用程序可以调用read从接收缓冲区拿数据;另一方面, TCP的一个连接, 既有发送缓冲区, 也有接收缓冲区, 那么对于这一个连接, 既可以读数据, 也可以写数据. 这个概念叫做 全双工

由于缓冲区的存在, TCP程序的读和写不需要一一匹配, 例如:

写100个字节数据时, 可以调用一次write写100个字节, 也可以调用100次write, 每次写一个字节;

读100个字节数据时, 也完全不需要考虑写的时候是怎么写的, 既可以一次read 100个字节, 也可以一次read一个字节, 重复100次;

简单来说因为可以一个字节一个字节的写和读,向河流一样一个接一个,所以是面向字节流的。

补充:

1.像write,sendto,send等函数,实质上就是将数据从应用程序的内存空间拷贝到操作系统内核的缓冲区。相反,read,recv,recvfrom将数据从内核空间拷贝到应用程序空间。

2.缓冲区分为用户级缓冲区和内核级缓冲区

粘包问题

首先要明确, 粘包问题中的 "包" , 是指的应用层的数据包。

产生的原因:TCP是面向字节流的,在传输层对数据边界不敏感。虽然被分组成多个的 TCP 报文是一个一个过来的,但是在发送/接收缓冲区中这么一连串的字节数据, 就不知道从哪个部分开始到哪个部分, 是一个完整的应用层数据包。从而导致粘包问题。

那么如何避免粘包问题呢? 归根结底就是一句话, 明确两个包之间的边界

解决方案:

1.在一个完整的TCP报文(未被拆分前)的数据部分的结尾加一个特殊字符,以此来区分报文的边界。

2.模仿UDP那样。UDP有 "报文长度(16位)" 这样的字段直接明确知道这个报文的长度。因此我们可以在TCP报文的数据部分的头部明确一个包总长度的字段, 从而就知道了包的结束位置。
 

四.TCP/UDP对比

注意:从直观上来看,UDP似乎远远比不上TCP,谁好谁坏一目了然。但是在网络通信发展的早期,对数据传输的需求相对简单和直接,UDP 这种无连接、低开销的协议首先被开发和使用。只不过随着技术的发展,需求变得更高,UDP无法满足复杂和严格的通信需求。TCP 协议才被设计和发展出来。

五.参考文档

深入浅出TCP三次握手 (多图详解)[通俗易懂]-腾讯云开发者社区-腾讯云

浅谈面向数据报的协议-UDP协议-腾讯云开发者社区-腾讯云

计算机网络:这是一份非常全面&详细的TCP/IP协议学习指南-腾讯云开发者社区-腾讯云 (tencent.com)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值