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

        上一节,我们学习了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 ) :

三次握手发生在客户端执行 connect()的时候,该方法返回成功,则说明三次握手已经建 立。三次握手示例图如下:

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

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

当一端要进行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 是假想的起始点,并不是一个实际的状态。这种状态变化就好比我们打电话通话处于不同的状态,但是只要双方拨通了电话,那么就一直是通话中。只有在拨打电话和挂断电话时,状态会发生变化。

        上图中,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会持续大概两分钟的时间。

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

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

面试题:

为什么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()链接失败原因:没有运行服务器,客户端连接就会失败。网断了也链接不上。

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

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

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

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

1. 使用定长消息

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

2. 使用特殊分隔符

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

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

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

1.4 应答确认与超时重传

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

面试题: 

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

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

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

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

1.5 滑动窗口

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

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

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

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

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

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

1. #include <stdio.h>
2. #include <stdlib.h>
3. #include <unistd.h>
4. #include <string.h>
5. #include <assert.h>
6. #include <sys/socket.h>
7. #include <netinet/in.h>
8. #include <arpa/inet.h>
9. #include <pthread.h>
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) <= 0 )
19. {
20. break;
21. }
22.
23. printf("recv(%d)=%s\n",c,buff);
24. send(c,"ok",2,0);
25. }
26.
27. printf("one client over(%d)\n",c);
28. close(c);
29. }
30.
31. int main()
32. {
33. int sockfd = socket(AF_INET,SOCK_STREAM,0);
34. assert( sockfd != -1 );
35.
36. struct sockaddr_in saddr,caddr;
37. memset(&saddr,0,sizeof(saddr));
38. saddr.sin_family = AF_INET;
39. saddr.sin_port = htons(6000);
40. saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
41.
42. int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
43. assert( res != -1 );
44.
45. listen(sockfd, 5);
46.
47. while( 1 )
48. {
49. int len = sizeof(caddr);
50. int c = accept(sockfd,(struct sockaddr*)&caddr,&len);
51. if ( c < 0 )
52. {
53. continue;
54. }
55.
56. printf("accept c = %d\n",c);
57.
58. pthread_t id;
59. pthread_create(&id,NULL,fun,(void*)c);//创建子线程
60.
61. }
62.
63. close(sockfd);
64. exit(0);
65. }

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

1. #include <stdio.h>
2. #include <stdlib.h>
3. #include <assert.h>
4. #include <string.h>
5. #include <unistd.h>
6. #include <sys/types.h>
7. #include <sys/socket.h>
8. #include <arpa/inet.h>
9. #include <netinet/in.h>
10. #include <signal.h>
11.
12. void DealClientLink(int c, struct sockaddr_in caddr)
13. {
14. while(1)
15. {
16. char buff[128] = {0};
17. int n = recv(c, buff, 127, 0);
18. if(n <= 0)
19. {
20. break;
21. }
22.
23. printf("%s:%d %s\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), buff);
24.
25. send(c, "OK", 2, 0);
26. }
27.
28. printf("one client unlink\n");
29. close(c);
30. }
31.
32. void sigfun(int sign)
33. {
34. wait(NULL);
35. }
36.
37. int main()
38. {
39. signal(SIGCHLD, sigfun); //处理僵死进程
40. int sockfd = socket(AF_INET, SOCK_STREAM, 0);
41. assert(-1 != sockfd);
42.
43. struct sockaddr_in saddr;
44. memset(&saddr, 0, sizeof(saddr));
45. saddr.sin_family = AF_INET;
46. saddr.sin_port = htons(6000);
47. saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
48.
49. int res = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr));
50. assert(-1 != res);
51.
52. listen(sockfd, 5);
53.
54. while(1)
55. {
56. struct sockaddr_in caddr;
57. int len = sizeof(caddr);
58.
59. int c = accept(sockfd, (struct sockaddr*)&caddr, &len);
60. assert(-1 != c);
61.
62. printf("%s:%d Link Success\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));
63.
64. pid_t pid = fork();    //产生子进程
65. assert(-1 != pid);
66. if(0 == pid)
67. {
68. DealClientLink(c, caddr);  //子进程
69. exit(0); // 必须结束子进程,否则会有多个进程调 accept
70. }
71. else
72. {
73. close(c); // 父子进程都需要关闭 c
74. }
75. }
76.
77. close(sockfd);
78. exit(0);
79. }

客户端代码 TcpClient.c 如下:

1. #include <stdio.h>
2. #include <stdlib.h>
3. #include <unistd.h>
4. #include <string.h>
5. #include <assert.h>
6. #include <sys/socket.h>
7. #include <netinet/in.h>
8. #include <arpa/inet.h>
9.
10. int main()
11. {
12. int sockfd = socket(AF_INET,SOCK_STREAM,0);
13. assert( sockfd != -1 );
14.
15. struct sockaddr_in saddr;
16. memset(&saddr,0,sizeof(saddr));
17. saddr.sin_family = AF_INET;
18. saddr.sin_port = htons(6000);
19. saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
20.
21. int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
22. assert(res != -1 );
23.
24. while( 1 )
25. {
26. char buff[128]= {0};
27. printf("input:\n");
28.
29. fgets(buff,128,stdin);
30. if ( strncmp(buff,"end",3) == 0 )
31. {
32. break;
33. }
34.
35. send(sockfd,buff,strlen(buff),0);
36.
37. memset(buff,0,128);
38. recv(sockfd,buff,127,0);
39. printf("buff=%s\n",buff);
40. }
41.
42. close(sockfd);
43. }


三、UDP协议

3.1 UDP协议编程流程

     UDP 提供的是无连接、不可靠的、数据报服务。可以通俗的将TCP理解成打电话,UDP理解成发短信。

        socket()用来创建套接字,使用 udp 协议时,选择数据报服务 SOCK_DGRAM。sendto() 用来发送数据,由于 UDP 是无连接的,每次发送数据都需要指定对端的地址(IP 和端 口)。recvfrom()接收数据,每次都需要传给该方法一个地址结构来存放发送端的地址。 recvfrom()可以接收所有客户端发送给当前应用程序的数据,并不是只能接收某一个客户端的数据。

UDP 服务端编程示例代码:

1. #include <stdio.h>
2. #include <stdlib.h>
3. #include <unistd.h>
4. #include <string.h>
5. #include <assert.h>
6. #include <sys/socket.h>
7. #include <netinet/in.h>
8. #include <arpa/inet.h>
9.
10. int main()
11. {
12.     int sockfd = socket(AF_INET,SOCK_DGRAM,0);
13.     assert( sockfd != -1 );
14.
15.     struct sockaddr_in saddr,caddr;
16.     memset(&saddr,0,sizeof(saddr));
17.     saddr.sin_family = AF_INET;
18.     saddr.sin_port = htons(6000);
19.     saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
20.
21.     int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
22.     assert( res != -1 );
23. 
24.     while( 1 )
25.     {
26.          int len = sizeof(caddr);
27.          char buff[128] = {0};
28.          recvfrom(sockfd,buff,127,0,(struct sockaddr*)&caddr,&len);
29.          printf("ip:%s,port:%d,buff=%s\n",inet_ntoa(caddr.sin_addr), ntohs(caddr.si
n_port),buff );
30.
31.         sendto(sockfd,"ok",2,0,(struct sockaddr*)&caddr,sizeof(caddr));
32.     }
33.
34.     close(sockfd);
35. }

UDP 客户端编程示例代码:

1. #include <stdio.h>
2. #include <stdlib.h>
3. #include <unistd.h>
4. #include <string.h>
5. #include <assert.h>
6. #include <sys/socket.h>
7. #include <netinet/in.h>
8. #include <arpa/inet.h>
9.
10. int main()
11. {
12. int sockfd = socket(AF_INET,SOCK_DGRAM,0);
13. assert( sockfd != -1 );
14.
15. struct sockaddr_in saddr;
16. memset(&saddr,0,sizeof(saddr));
17. saddr.sin_family = AF_INET;
18. saddr.sin_port = htons(6000);
19. saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
20.
21. while( 1 )
22. {
23. char buff[128] = {0};
24. printf("input:\n");
25.
26. fgets(buff,128,stdin);
27.
28. if ( strncmp(buff,"end",3) == 0 )
29. {
30. break;
31. }
32.
33. sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&saddr,sizeof(saddr));
34. memset(buff,0,128);
35.
36. int len = sizeof(saddr);
37. recvfrom(sockfd,buff,127,0,(struct sockaddr*)&saddr,&len);
38.
39. printf("buff=%s\n",buff);
40. }
41.
42. close(sockfd);
43. }

启动服务端和客户端,再关掉服务端,还能再发送数据嘛?可以 因为udp是无连接的,只要服务端启动,有人发数据就接受。关掉服务端对客户端来说,丝毫没有影响  

3.2 UDP 协议特点

      UDP 数据报服务特点:发送端应用程序每执行一次写操作,UDP 模块就将其封装成一 个 UDP 数据报发送。接收端必须及时针对每一个 UDP 数据报执行读操作,否则就会丢包,因此它不会出现粘包现象。 并且,如果用户没有指定足够的应用程序缓冲区来读取 UDP 数据,则 UDP 数据将被截断。

3.3 应用场景

     tcp和udp应用分场景,例如下载一个文件,肯定是要完整的下载下来,数据不能丢失。如果实时通话视频时,那就用udp,因为只是要看当下的你,如果视频过程中网不好,数据没发出去,再重新发,这样慢慢的就会变成录屏,因为tcp有接收缓冲区,重新发的数据都会被对方,接收到接受缓冲区,对方要全部读完,所以这一帧数据没发送成功就不要了。

四、面试题

4.1 TCP和UDP的区别

tcp是面向连接的可靠的流式服务,udp是无连接不可靠的数据报服务 。

  1. tcp建立连接要进行三次握手,而udp不需要建立连接直接指定地址发数据就行
  2. tcp在发送数据时有应答确认,超时重传机制,而udp发送数据成功就成功,失败了也不会重发。
  3. tcp会出现粘包,udp不会出现粘包。

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

       是的,可以,同一个端口可以同时被一个 TCP 应用程序和一个 UDP 应用程序使用。TCP 和 UDP 是两个不同的传输层协议,它们的连接和数据传输方式不同,因此它们可以在相同的端口号上共存。操作系统和网络栈通过区分传输层协议(TCP 或 UDP)来将数据包正确地交付给对应的应用程序。例如,假设你有一个 TCP 服务在端口 8080 上运行,同时你也可以在相同的端口 8080 上运行一个 UDP 服务。这两个服务不会互相干扰,因为操作系统能够根据协议类型将到达端口 8080 的 TCP 数据包和 UDP 数据包区分开来并分别处理。

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

同一个应用程序可以创建多个套接字。套接字是网络通信的基础,它允许程序发送和接收数据。应用程序创建多个套接字的原因有很多,包括但不限于以下几个方面:

  1. 多协议支持:一个应用程序可能需要同时支持多种协议,例如同时使用 TCP 和 UDP,这时它需要分别为 TCP 和 UDP 创建不同的套接字。

  2. 多端口监听:一个服务器应用程序可能需要监听多个端口,以便提供不同的服务或支持不同的协议版本。例如,一个应用程序可以同时监听 80 端口(HTTP)和 443 端口(HTTPS)。

  3. 客户端连接管理:对于一个 TCP 服务器,每当一个客户端连接到服务器时,服务器通常会为每个客户端连接创建一个新的套接字。这允许服务器同时处理多个客户端连接。

  4. 多线程或多进程通信:应用程序可能使用多个套接字来实现多线程或多进程间的通信。例如,一个线程或进程负责监听网络连接,另一个线程或进程负责处理数据。

至此,已经讲解完毕!篇幅较长,慢慢消化,以上就是全部内容!请务必掌握,创作不易,欢迎大家点赞加关注评论,您的支持是我前进最大的动力!下期再见!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/694793.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

关系数据库标准查询语言-SQL-SQL语言概述

一、SQL(Structured Query Language)语言 1、是高度非过程化的语言 2、关系数据库管理系统(RDBMS)都支持SQL标准 3、具有定义、查询、更新、控制四大功能 4、数据库对象由数据库&#xff08;Database&#xff09;、基本表&#xff08;Table&#xff09;、视图&#xff08;V…

简单的基于threejs和BVH第一人称视角和第三人称视角控制器

渲染框架是基于THREE,碰撞检测是基于BVH。本来用的是three自带的octree结构做碰撞发现性能不太好 核心代码&#xff1a; import * as THREE from three import { RoundedBoxGeometry } from three/examples/jsm/geometries/RoundedBoxGeometry.js; import { MeshBVH, MeshBVHHe…

【iOS】JSONModel源码阅读笔记

文章目录 前言一、JSONModel使用二、JSONModel其他方法转换属性名称 三、源码分析- (instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError **)err[self init]__setup____inspectProperties - (BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMa…

小白都可以通过U盘重装系统,再也不用花50块钱去安装系统啦

下载Ventoy 软件 1、今天带着大家通过Ventoy 安装Windows 11 系统。 2、首先我们通过官网如下地址&#xff1a;https://www.ventoy.net/cn/&#xff0c;找到我们对应系统的Ventoy 软件安装包。 3、通过官网可以找到软件包的地址地址&#xff0c;如下图所示。 4、如下就是我下…

面试题 17.15. 最长单词

. - 力扣&#xff08;LeetCode&#xff09; class Solution { public: struct Trie {Trie() {end false;next.resize(26, nullptr);}bool end;std::vector<Trie*> next; };void insert_trie(Trie* root, std::string& word) {Trie* cur root;for (int i 0; i <…

《MySQL是怎样运行的》读书笔记(三) B+树索引

前言 从前面数据存储结构中我们已经知道了页和记录的关系示意图: 其中页a、页b、页c ... 页n 这些页可以不在物理结构上相连&#xff0c;只要通过双向链表相关联即可。 在正式介绍索引之前&#xff0c;我们需要了解一下没有索引的时候是怎么查找记录的。下边先只讨论搜索条件…

SpringBoot 参数验证的几种方式

文章目录 SpringBoot 参数验证1、为什么要进行参数验证2、验证方式2.1 if 语句判断2.2 Assert2.3 Validator2.3.1 引入依赖2.3.2 定义参数实体类2.3.4 定义特定异常全局拦截方法2.3.5 定义校验类进行测试2.3.6 测试 2.4 自定义验证注解2.4.1 定义自定义注解2.4.2 定义自定义验证…

C#操作MySQL从入门到精通(20)——更新数据

前言: 谈到数据库,大家最容易脱口而出的就是增删改查,本文所说的更新数据就是增删改查的改,改变数据的意思。 本文测试使用的数据库如下: 1、更新一列 所谓更新一列的意思就是只更改一列数据,并且通常要使用where条件,因为不加这个条件的话会导致将所有行的数据进行…

Java | Leetcode Java题解之第137题只出现一次的数字II

题目&#xff1a; 题解&#xff1a; class Solution {public int singleNumber(int[] nums) {int a 0, b 0;for (int num : nums) {b ~a & (b ^ num);a ~b & (a ^ num);}return b;} }

十大人工智能企业

​​​​​​链接&#xff1a;​​​​​​人工智能品牌排行-人工智能十大公司-人工智能十大品牌-Maigoo品牌榜

Linux--进程间通信(system V共享内存)

目录 1.原理部分 2.系统调用接口 参数说明 返回值 1. 函数原型 2. 参数说明 3. 返回值 4. 原理 5. 注意事项 3.使用一下shmget&#xff08;一段代码&#xff09; 4.一个案例&#xff08;一段代码) 1.简单封装一下 2.使用共享内存 2.1挂接&#xff08;shmat&#x…

2024 年适用于 Linux 的 5 个微软 Word 替代品

对于那些最近由于隐私问题或其他原因而转向 Linux 的用户来说&#xff0c;可能很难替换他们最喜欢的、不在 Linux 操作系统上运行的应用程序。 寻找流行程序的合适替代品可能会成为一项挑战&#xff0c;而且并不是每个人都准备好花费大量时间来尝试弄清楚什么可以与他们在 Win…

新买的移动硬盘无法识别

文章目录 背景解决方案 背景 同事新买的移动硬盘&#xff0c;插在电脑上识别不出来盘符&#xff0c;检查了一下&#xff0c;硬盘没问题应该&#xff0c;是ssk的硬盘盒M.2的SSD&#xff0c;硬盘驱动也是正常的&#xff0c;插拔了几次&#xff0c;都不识别&#xff0c;换了太电脑…

fl studio怎么设置中文及 2024年最新fl studio选购指南

FL Studio让你的计算机就像是全功能的录音室&#xff0c;漂亮的大混音盘&#xff0c;先进的创作工具&#xff0c;让你的音乐突破想象力的限制。zol提供FL Studio中文版下载。 FL Studio中文版下载软件简介 FL Studio 让你的计算机就像是全功能的录音室&#xff0c;漂亮的大混…

基于实验的电动汽车动力电池SOC

前言 本文为笔者在学习《基于MATLAB的新能源汽车仿真》过程中学习笔记&#xff0c;所涉及的表格数据和公式均为书籍里的。仿真数据是网上找的恒电流放电数据。本文仅作为笔者的入门学习记录。 一、分析动力电池SOC估算方法 SOC是指动力电池按照规定放电条件可以释放的容量占…

java版知识付费saas租户平台:剖析现代知识付费平台的功能架构与运营逻辑

在数字化学习的时代背景下&#xff0c;知识付费平台已经成为教育行业的一颗璀璨明星&#xff0c;以其用户需求为中心&#xff0c;提供便捷高效的学习途径。这些平台汇聚了众多专业知识&#xff0c;覆盖职业技能、生活兴趣和人文社科等多个领域&#xff0c;满足不同用户的学习需…

安全专业的硬件远控方案 设备无网也能远程运维

在很多行业中&#xff0c;企业的运维工作不仅仅局限在可以联网的IT设备&#xff0c;不能连接外网的特种设备也需要专业的远程运维手段。 这种特种设备在能源、医疗等行业尤其常见&#xff0c;那么我们究竟如何通过远程控制&#xff0c;对这些无网设备实施远程运维&#xff0c;…

[Algorithm][动态规划][01背包问题][目标和][最后一块石头的重量Ⅱ]详细讲解

目录 1.目标和1.题目链接2.算法原理详解3.代码实现 2.最后一块石头的重量 II1.题目链接2.算法原理详解3.代码实现 1.目标和 1.题目链接 目标和 2.算法原理详解 问题转化&#xff1a;在数组中选择一些数&#xff0c;让这些数的和等于a&#xff0c;一共有多少种选法&#xff1f…

推荐4个好用有趣的软件

MyComic——漫画聚合软件 MyComic是一款界面简洁、分类详尽的漫画阅读软件&#xff0c;专为动漫爱好者设计。它提供了丰富的高清漫画资源&#xff0c;支持在线免费阅读&#xff0c;并且可以一键下载到书架&#xff0c;方便随时离线观看&#xff0c;节省流量。用户可以轻松找到喜…

CSAPP Lab01——Data Lab完成思路

陪你把想念的酸拥抱成温暖 陪你把彷徨写出情节来 未来多漫长再漫长还有期待 陪伴你 一直到 故事给说完 ——陪你度过漫长岁月 完整代码见&#xff1a;CSAPP/datalab-handout at main SnowLegend-star/CSAPP (github.com) 01 bitXor 这道题是用~和&计算x^y。 异或是两个…