Linux基础 (十五):TCP 协议特点和UDP协议

2024-06-10 1010阅读

        上一节,我们学习了TCP协议的服务器-客户端的编程流程以及对中间的过程进行了详细的讨论,那么,这一节,我们对于TCP协议的特点进行进一步的分析,这也是面试的重点和难点。

目录

一、TCP 协议特点

1.1  连接的建立与断开

1.1.1 面试题

1.2  TCP 状态转移(面试题)

1.3 流式服务特点

1.4 应答确认与超时重传

1.5 滑动窗口

二、多进程、多线程处理并发 

三、UDP协议

3.1 UDP协议编程流程

3.2 UDP 协议特点

3.3 应用场景

四、面试题

4.1 TCP和UDP的区别

4.2 同一个端口可不可以被一个 TCP 和一个 UDP 的应用程序同时使用?

4.3 同一个应用程序可以创建多个套接字吗?


一、TCP 协议特点

       通过前面的学习,我们知道:TCP 协议提供的是:面向连接、可靠的、字节流服务。

1.1  连接的建立与断开

        使用 TCP 协议通信的双发必须先建立连接(三次握手),然后才能开始数据的读写。双方都必须为该连接分配必要的内核资源,以管理连接的状态和连接上数据的传输。TCP 连接是全双工的,双方的数据可以通过一个连接进行读写。完成数据交换之后,通信双方都必须断开连接以释放系统资源(四次挥手)。 使用 tcpdump 抓包命令可以抓包观察 TCP 连接的建立与关闭。该命令需要管理员权限,格式如下(假设两个测试用的主机 IP 地址为 192.168.43.214 和 192.168.43.160 ) :

Linux基础 (十五):TCP 协议特点和UDP协议

三次握手发生在客户端执行 connect()的时候,该方法返回成功,则说明三次握手已经建 立。三次握手示例图如下:Linux基础 (十五):TCP 协议特点和UDP协议

客户端执行connect()会给服务器发送一个tcp报文,此时SYN标志有效,还会发送一个序列号;服务器收到报文,会发送报文回复,此时SYN有效,发送一个序列号,还回复会一个确认号是客服端发送的序列号+1;客服端收到服务器的回复,也会再次回复服务器,此时会发送确认号是刚刚客户端发送的序列号+1  ;

四次挥手发生在客户端或服务端执行 close()关闭连接的时候,示例图如下:

Linux基础 (十五):TCP 协议特点和UDP协议

当一端要进行close(),会给对方发送一个报文,此时FIN标志有效,还有一个序列号,然后对方收到报文,会回复对方已经收到了,发送一个确认号ACK,是刚刚发送的序列号+1;然后另一端也要close()关闭,也会给对方发送报文告诉对方字节要关闭了,FIN 序列号,对方收到报文了,会回复对方已经收到了,也发送一个确认号ACK,确认号是刚刚发送的序列号+1。

1.1.1 面试题

1、四次挥手的过程可以用三次完成吗? 

可以,四次挥手可以演化成三次挥手 。

        当一端close 发送报文过来,此时我也要close了,回复报文,和通知对方关闭的报文一起发送。

  1. 第一次挥手(FIN): 客户端发送一个FIN报文,表示它要关闭到服务器的数据传送。
  2. 第二次挥手(FIN): 服务器收到FIN后,直接发送一个FIN报文,表示它也要关闭到客户端的数据传送。
  3. 第三次挥手(ACK): 客户端收到FIN后,发送一个ACK报文,确认收到关闭请求,连接关闭。

2、挥手时,可能受到什么样的攻击?

FIN Flood 攻击:

  • 原理:攻击者发送大量的FIN包到目标服务器。这些包会让服务器尝试关闭大量的连接,耗费资源处理这些无效的连接终止请求。
  • 影响:服务器资源被耗尽,可能导致拒绝服务(DoS攻击)。

    3、为什么是三次握手,可不可以是两次为什么?

          握手只能是三次:例如客户端连接服务器后然后关闭了,服务器收到了并回复客户端,此时服务器就认为和客户端建立了链接,这个链接就一直保持着,但是客户端已经没了,所以还需要客户端第三次进行确认回复,来确保双方都保持链接。

    4、三次握手时可能出现什么攻击?

    • SYN Flood 攻击:

      • 原理:攻击者发送大量的SYN请求包到目标服务器,但不完成后续的握手步骤(即不发送ACK包)。目标服务器会为每个SYN请求分配资源并等待ACK回应,这样会导致服务器资源耗尽,无法处理合法用户的请求。(当有一个链接进来就会先放到未完成三次握手队列中,如果在短时间内有人连续发送链接就会把未完成三次握手队列塞满,使真正要进行链接的客户端连接不上。)
      • 影响:造成服务器拒绝服务(DoS攻击)。
    • SYN ACK Flood 攻击:

      • 原理:攻击者在没有发送初始SYN包的情况下,发送大量的SYN-ACK包到目标服务器。服务器会浪费资源去回复ACK,等待建立连接,导致资源耗尽。
      • 影响:和SYN Flood类似,可能导致拒绝服务。

      1.2  TCP 状态转移(面试题)

            下图是 TCP 连接从建立到关闭整个过程中通信两端状态的变化。tcp状态的改变是在建立连接和断开连接的基础上的 ,其中 CLOSED 是假想的起始点,并不是一个实际的状态。这种状态变化就好比我们打电话通话处于不同的状态,但是只要双方拨通了电话,那么就一直是通话中。只有在拨打电话和挂断电话时,状态会发生变化。

      Linux基础 (十五):TCP 协议特点和UDP协议

              上图中,TIME_WAIT 状态一般情况下是主动关闭的一端才会出现的状态。该状态出现后,会维持一段长为 2MSL(Maximum Segment Life)的时间,才能完全关闭。MSL 是 TCP 报文 段在网络中的最大生存时间,标准文档 RFC1122 的建议值是 2min。 在 Linux 系统上,一个 TCP 端口不能被同时打开多次(两次及以上)。当一个 TCP 连接 处于 TIME_WAIT 状态时,我们将无法立即使用该连接占用着的端口来建立一个新连接,必须要等待这两分钟过去,才能继续使用这个端口。

      双方同时关闭:

           双方都执行close(),都像对方发送FIN,双发都变成FIN_WAIT_1状态,等到双方都收到各自都收到对方发出的FIN,并发出ACK之后就会变成CLOSING状态,在等到双方都收到对方的ACK之后就会变成TIME_WAIT状态。

      四次挥手演化成三次挥手:

              主动关闭端执行close(),发FIN,被动关闭端收到FIN,但此时被动关闭端也要关闭了,就把ACK和FIN一起发送给主动关闭端

      connect()三次握手:

            客户端执行connect()后进行第一次进行握手发出SYN状态就变成了SYN_SENT状态,这个状态非常短暂,会观察不到,瞬间就没了。

            服务器收到SYN后,又给客户端发出SYN,ACK后变成,SYN_RCVD状态。

      服务器和客户端都完成三次握手状态就会变成,ESTABLISHED。

      close()四次挥手:

              无论哪一方主动执行close()端,先发送FIN,然后主动关闭端就会变成FIN_WAIT_1状态,然后对方收到FIN,再发ACK就会变成CLOSE_WAIT状态,主动关闭端收到对方的回复,就变成了FIN_WAIT_2状态。此时两次挥手结束。被动关闭端执行close(),会给主动关闭端发送FIN,会变成LAST_ACK状态,主动关闭端收到FIN并发送ACK,主动关闭端状态就变成了TIME_WAIT,然后被动关闭段收到ACK,然后就消失了。

             TIME_WAIT会持续大概两分钟的时间。

      Linux基础 (十五):TCP 协议特点和UDP协议

      Linux基础 (十五):TCP 协议特点和UDP协议

      如上图所示:服务器会跟很多客户端有连接,每个连接都有自己的状态。每一个连接都会有自己的接收缓冲区和发送缓冲区。

      使用命令:netstat -natp可以查看连接的状态

      Linux基础 (十五):TCP 协议特点和UDP协议

      Linux基础 (十五):TCP 协议特点和UDP协议

      面试题:

      为什么TIME_WAIT状态要持续一段时间?

      1.可靠地终止TCP的连接。

      2.保证让迟来的TCP报文段有足够的时间被识别并丢弃。

      1. 被动关闭端关闭发FIN,主动关闭端收到FIN,发ACK,变成TIME_WAIT,有可能被动关闭端没收到这个ACK,这个ACK在路上丢失了,过一会被动关闭端没收到主动关闭端的ACK就会再次发FIN,如果TIME_WAIT状态不持续直接关闭,那最后假如ACK丢失,被动关闭端在发送FIN就没人管它了。
      2. 在通讯的过程中,有一些数据正在发送,但还没发送到,数据正在从A端到B端但还没到,此时断开接收端和发送端的连接,之后这个延迟的数据包到达了,但此时连接已经断开了,就会出现一些问题尤其是服务器。如果没有TIME_WAIT状态,我们就可以立刻重新启动服务端,这样延迟的数据包就会陆陆续续发到我们这个新启动的服务器里,虽然我们新启动的服务器用的是这个ip这个端口,延迟的数据包用的也是这个ip和端口,但是这些数据包是发给上个已经结束的进程的,不是发给我们这个新进程的。因此就会让TIME_WAIT状态等待大概2分钟,这俩分钟是一个报文生存期最长时间的俩倍,这样就会把我们网络中延迟的数据包耗死,我们把这些延迟的数据一收延后丢掉,俩分钟后网络中就干净了。

      题目:

              一个局域网内,有一个客户端一个服务器,他们都已完成三次握手状态,没有发送数据,此时拔掉网线,服务器再close(),重新运行服务器,运行之后在插上网线,问此时客户端跟服务器的状态。

             网线拔掉之后,不进行收发送数据,双方是不知道的,由于拔掉网线,关闭服务器,服务器会发送FIN,但是客户端收不到,也不会回复,服务器就等了俩分钟后就关闭了,再重新启动服务器,此时服务器就是LISTEN状态等待连接,客户端还是完成三次握手状态。

      1.3 流式服务特点

          TCP 字节流的特点,发送端执行的写操作次数和接收端执行的读操作次数之间没有任何数量关系,应用程序对数据的发送和接收是没有边界限制的。多次发送的数据会被对方一次接受,或者一次发送的数据被对方,分多次接受。

      netstat -natp命令 可查看端口是否被占用 也能查看接收缓冲区和发送缓冲区有多少数据

      1. bind()会失败的原因 :端口被占用或者把这个程序运行了,又运行了一个,端口已经分给第一个运行的程序。
      2. recv()返回值为0是唯一判断对方客户端关闭链接的条件。
      3. connect()链接失败原因:没有运行服务器,客户端连接就会失败。网断了也链接不上。

      Linux基础 (十五):TCP 协议特点和UDP协议

      修改循环收发的服务器端的代码如下:
      char buff[128]={0};
      recv(sockfd,buff,1,0);
      /*一次只接收一个字符*/
      

             客户端发个hello,服务器将接收字符个数改成1,出现的结果是:循环5次把hello打印完,直到把buff里的数据打印完。客服端那里会一次收到5个ok。

      Linux基础 (十五):TCP 协议特点和UDP协议

              这是因为服务器和客户端都有一个接收缓冲区和发送缓冲区,一端send()发送数据,先把数据写到发送缓冲区里,再通过底层协议把发送缓冲区的数据挪到对方的接受缓冲区中,然后对方再通过recv()把接收缓冲区中的数据读出来。recv()发送成功只能说明成功将数据发达发送缓冲区,对方并没有收到。有可能会多次从接收缓冲区一次读取,也有可能分多次读取,就像我们购物从菜鸟驿站取快递,我们取出的快递件也可能一次取完,也有可能还没到菜鸟驿站,我们就需要分多次取。这就是TCP 粘包:连续多次send()发送的数据,被对方recv()一次性收到。发送数据的次数跟接收数据的次数是不对应的。所以会出现粘包。如何解决呢?(面试题)

      Linux基础 (十五):TCP 协议特点和UDP协议

      解决粘包问题的常见方法有以下几种:(面试题)

      1. 使用定长消息

               通过规定每条消息的长度,接收方可以按照固定长度读取数据。例如,如果消息长度固定为100字节,接收方每次读取100字节的数据,就可以避免粘包问题。

      2. 使用特殊分隔符

              在每条消息的末尾添加特定的分隔符(如换行符、特殊字符等),接收方可以通过检测分隔符来区分消息边界。

      3. 使用消息头(长度前缀)

             在每条消息前添加一个消息头,用于存储消息的长度,接收方先读取消息头中的长度信息,再根据长度读取具体的消息内容。

      1.4 应答确认与超时重传

             TCP 发送的报文段是交给 IP 层传送的。但 IP 层只能提供尽最大努力的服务,也就是说,TCP 下面的网络所提供的是不可靠的传输。因此,TCP 必须采用适当的措施才能使两个运输层之间的通信变得可靠。TCP 的可靠传输是通过使用应答确认和超时重传来完成。下图是通过 netstat 命令抓包看到的信息:

      Linux基础 (十五):TCP 协议特点和UDP协议

      面试题: 

      tcp的可靠性体现在:应答确认、超时重传、去重、乱序重排、进行流量控制滑动窗口

      1. 应答确认:给对方send()发送一个数据,对方收到了,在底层会回复发送方表明收到数据了,A端给B端发送数据,表面只能看到俩次交互,实际有四次,另外两次我们看不到,但可以用tcpdump抓包命令看到。
      2. 超时重传:给对方发送数据收,等了一段事件后没有收到对方的回复,就认为这个数据丢失了,就会再重新发送一份数据给对方。
      3. 去重:给对方发送数据,对方收到了,回复确认收到信息,但回复这个信息丢失了,发送段没收到,就会认为发送的数据在路上丢失了,就会重新发,然后接收端就会有俩个一样的数据,重复了就会去重。
      4. 乱序重排:后发送的数据比先发送的数据先到达,这样顺序就会乱,但在接收到数据后,会对数据的顺序进行检查。
      5. 滑动窗口: 给对方发送数据,一个字节一个字节发效率不高,就会有一个窗口,窗口左边是已发送对方回复确认的数据,窗口内是有已发送未收到确认的和未发送的数据,窗口右边是超过窗口范围内外就不能发送的,窗口内比如能够发送100字节,我们20字节一个包,这样发送,发送20字节,没收到对方回复,我们还能继续发送,直到把这滑动窗口内的100字节数据全部发送完了,还没收到对方回复收到的信号,就不能再发送了,如果前面20字节,对方回复收到了,这个窗口就向后移动,确保窗口内数据有100个字节,然后新到窗口内的数据就能发送了。因为如果你光发送数据,也不知道对方收没收就到一直发;或者就是对方一次性只能接受多少数据,发太多也没用。

      下图是无差错时,数据交互的流程:发送端发送数据 m1 给接收端,接收端收到数据后会给发送端一个确认信息,以表明数据已经被成功收到。在发送方未收到确认信息前,M1 应继续被保留,直到确认信息到达才能丢弃。

      Linux基础 (十五):TCP 协议特点和UDP协议

      下图是出现差错时,数据交互的流程:

      Linux基础 (十五):TCP 协议特点和UDP协议

      1.5 滑动窗口

             TCP 协议是利用滑动窗口实现流量控制的。一般来说,我们总是希望数据传输得更快一些,不会一次只发一个字节。但是如果发送方把数据发得过快,接受方就可能来不及接收, 这就会造成数据的丢失。所谓流量控制就是让发送方的发送速率不要太快,要让接收方来的及接收。在 TCP 的报头中有一个字段叫做接收通告窗口,这个字段由接收端填充,是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。所以发送端就会有一个发送窗口,这个发送窗口的大小是由接收端填充的接收通告窗口的大小决定的,并且窗口的位置会随着发送端数据的发送和接收到接收端对数据的确认而不断的向右滑动,将之称为滑动窗口。发送方的滑动窗口示意图如下:

      Linux基础 (十五):TCP 协议特点和UDP协议

      当收到 36 的 ack,并发出 46-51 的字节后,窗口滑动的示意图如下:

      Linux基础 (十五):TCP 协议特点和UDP协议

      二、多进程、多线程处理并发 

            如下图所示, 当一个客户端与服务器建立连接以后,服务器端 accept()返回,进而准备循环接收客户端发过来的数据。如果客户端暂时没发数据,服务端会在第 40 行的 recv()阻 塞。此时,其他客户端向服务器发起连接后,由于服务器阻塞了,无法执行 accept()接受连 接,也就是其他客户端发送的数据,服务器无法读取。服务器也就无法并发同时处理多个客户端。 

      Linux基础 (十五):TCP 协议特点和UDP协议

      Linux基础 (十五):TCP 协议特点和UDP协议

            这个问题可以通过引入多线程和多进程来解决。服务端接受一个客户端的连接后,创建 一个线程或者进程,然后在新创建的线程或进程中循环处理数据。主线程(父进程)只负责监听客户端的连接,并使用 accept()接受连接,不进行数据的处理。如下图所示:

      Linux基础 (十五):TCP 协议特点和UDP协议

      多线程处理并发的服务器端示例代码 MultiThread.c 如下:

      1. #include 
      2. #include 
      3. #include 
      4. #include 
      5. #include 
      6. #include 
      7. #include 
      8. #include 
      9. #include 
      10.
      11. void* fun(void * arg) //线程函数用来处理单个客户端收发数据
      12. {
      13.    int c = (int)arg;
      14.
      15. while( 1 )
      16. {
      17. char buff[128] = {0};
      18. if ( recv(c,buff,127,0) 
VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]