学懂C++(三十九):网络编程——深入详解 TCP 和 UDP 的区别和应用场景

本文主要是介绍学懂C++(三十九):网络编程——深入详解 TCP 和 UDP 的区别和应用场景,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、TCP 的特点及应用场景

1. 可靠性

2. 流控制和拥塞控制

3. 有序传输

4. 应用场景

二、UDP 的特点及应用场景

1. 无连接

2. 不可靠性

3. 轻量级

4. 支持广播和多播

5. 应用场景

三、TCP 和 UDP 的区别

四、TCP 和 UDP 的工作原理

1. TCP 的工作原理

三次握手

数据传输

四次挥手

2. UDP 的工作原理

数据传输

广播和多播

五、实例比较

TCP 示例

TCP 服务器

TCP 客户端

UDP 示例

UDP 服务器

UDP 客户端

六、总结


        网络通信中,传输控制协议 (TCP) 和用户数据报协议 (UDP) 是两种主要的传输层协议。它们在数据传输方式、可靠性、连接管理等方面有着显著的区别。本文将深入解析 TCP 和 UDP 的工作原理、主要特性及其应用场景,帮助读者更好地理解和选择合适的传输协议。

一、TCP 的特点及应用场景

传输控制协议 (TCP, Transmission Control Protocol) 是一种面向连接的、可靠的传输协议,适用于对数据传输可靠性要求较高的应用场景。以下是 TCP 的主要特点和应用场景:

1. 可靠性

TCP 提供可靠的数据传输,通过以下机制保证数据的完整性和顺序性:

  • 三次握手:在传输数据前,双方需要进行三次握手以建立连接,确保双方都准备好发送和接收数据。
  • 序列号和确认应答:每个数据包都有一个序列号,接收方会发送确认应答 (ACK) 来确认收到的数据。若发送方未收到 ACK,则会重传数据包。
  • 校验和:每个数据包附带校验和,以检测传输过程中是否出现数据错误。
  • 重传机制:如在规定时间内未收到 ACK,发送方会自动重传数据包,直到确认数据包成功到达。
2. 流控制和拥塞控制

TCP 实现了流控制和拥塞控制机制:

  • 滑动窗口:TCP 使用滑动窗口机制控制数据流量,防止发送方发送过多数据,导致接收方缓冲区溢出。
  • 拥塞控制:TCP 采用慢启动、拥塞避免、快速重传和快速恢复等算法,动态调整数据发送速率,避免网络拥塞。
3. 有序传输

TCP 保证数据包按序到达,即使在网络中数据包乱序传输,接收方也能按照正确顺序重组数据。这对于应用程序处理数据的顺序性要求至关重要。

4. 应用场景

由于 TCP 提供可靠的数据传输和流控制,适用于以下应用场景:

  • 网页浏览 (HTTP/HTTPS):浏览器与服务器之间的数据传输需要高可靠性,以确保网页内容的完整性。
  • 文件传输 (FTP):文件传输协议需要保证数据的完整性和顺序性,避免文件损坏。
  • 电子邮件 (SMTP/IMAP/POP3):邮件传输需要确保邮件内容的完整性和可靠性。
  • 远程登录 (SSH/Telnet):远程登录需要可靠的数据传输,以确保命令和响应的正确性。
二、UDP 的特点及应用场景

用户数据报协议 (UDP, User Datagram Protocol) 是一种无连接的、不可靠的传输协议,适用于对实时性要求高但容错性强的应用场景。以下是 UDP 的主要特点和应用场景:

1. 无连接

UDP 是无连接协议,数据包直接发送到目标地址,无需建立连接。这使得 UDP 的传输过程更简单、更快速。

2. 不可靠性

UDP 不保证数据包的可靠传输,没有序列号和确认应答机制,数据包可能丢失、重复或乱序到达。应用程序需要自行处理数据包的可靠性。

3. 轻量级

UDP 头部开销小(仅 8 字节),数据包传输更加高效,适用于对实时性要求高的应用。

4. 支持广播和多播

UDP 支持广播和多播,允许数据包同时发送到多个接收方,适用于某些特殊应用场景。

5. 应用场景

由于 UDP 的不可靠性和高效性,适用于以下应用场景:

  • 实时音视频传输 (VoIP、视频会议):实时应用对时延敏感,允许少量数据丢失,但要求数据快速传输。
  • 在线游戏:在线游戏需要快速响应,允许少量数据丢失,以确保游戏体验。
  • 简单的请求-响应服务 (DNS):DNS 查询对时延要求高,允许少量查询重试。
  • 广播和多播:如 IPTV、实时股票行情等应用需要将数据包发送到多个接收方。
三、TCP 和 UDP 的区别

以下是 TCP 和 UDP 在不同方面的详细区别:

特性TCPUDP
连接类型面向连接无连接
可靠性可靠不可靠
数据传输顺序保证按序传输不保证按序传输
流控制具有流控制机制无流控制机制
拥塞控制具有拥塞控制机制无拥塞控制机制
头部开销较大(20-60 字节)较小(8 字节)
传输效率较低(由于可靠性机制)较高
适用场景需要高可靠性的数据传输需要高实时性的数据传输
应用示例HTTP、FTP、SMTP、SSHVoIP、视频会议、DNS、在线游戏
四、TCP 和 UDP 的工作原理
1. TCP 的工作原理
三次握手

三次握手用于建立 TCP 连接,确保双方都准备好进行数据传输:

  1. SYN:客户端发送 SYN 报文,表示请求建立连接。
  2. SYN-ACK:服务器收到 SYN 报文后,回应 SYN-ACK 报文,表示同意建立连接。
  3. ACK:客户端收到 SYN-ACK 报文后,发送 ACK 报文,表示确认建立连接。
数据传输

TCP 通过序列号和确认应答机制,确保数据包按序到达和完整性:

  • 序列号:每个数据包都有一个序列号,表示数据包在整个数据流中的位置。
  • 确认应答 (ACK):接收方发送 ACK 报文,确认收到的数据包。
四次挥手

四次挥手用于关闭 TCP 连接,确保双方都同意结束数据传输:

  1. FIN:客户端发送 FIN 报文,表示请求关闭连接。
  2. ACK:服务器收到 FIN 报文后,回应 ACK 报文,表示确认关闭请求。
  3. FIN:服务器发送 FIN 报文,表示同意关闭连接。
  4. ACK:客户端收到 FIN 报文后,发送 ACK 报文,表示确认关闭。
2. UDP 的工作原理
数据传输

UDP 直接将数据包发送到目标地址,无需建立连接和确认应答:

  • 数据报:每个数据包称为数据报,包含源地址、目标地址、数据等信息。
  • 无序传输:数据报可能乱序到达,应用程序需要自行处理数据的顺序性。
广播和多播

UDP 支持广播和多播,允许数据包同时发送到多个接收方:

  • 广播:将数据包发送到同一网络中的所有主机。
  • 多播:将数据包发送到特定的一组主机。
五、实例比较

以下是 TCP 和 UDP 的简单应用示例,展示它们的实际使用情况。

TCP 示例
TCP 服务器
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>#define PORT 8080
#define BUFFER_SIZE 1024int main() {int server_fd, new_socket;struct sockaddr_in address;int opt = 1;int addrlen = sizeof(address);char buffer[BUFFER_SIZE] = {0};const char* message = "Hello from server";// 创建套接字if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {perror("socket failed");exit(EXIT_FAILURE);}// 绑定套接字if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {perror("setsockopt");exit(EXIT_FAILURE);}address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(PORT);if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {perror("bind failed");exit(EXIT_FAILURE);}// 监听连接if (listen(server_fd, 3) < 0) {perror("listen");exit(EXIT_FAILURE);}// 接受连接if ((new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0) {perror("accept");exit(EXIT_FAILURE);}read(new_socket, buffer, BUFFER_SIZE);std::cout << "Message received: " << buffer << std::endl;send(new_socket, message, strlen(message), 0);std::cout << "Hello message sent" << std::endl;close(new_socket);close(server_fd);return 0;
}
TCP 客户端
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>#define PORT 8080
#define BUFFER_SIZE 1024int main() {int sock = 0;struct sockaddr_in serv_addr;char buffer[BUFFER_SIZE] = {0};const char* message = "Hello from client";// 创建套接字if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {std::cerr << "Socket creation error" << std::endl;return -1;}serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(PORT);// 将服务器地址转为二进制形式if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {std::cerr << "Invalid address/ Address not supported" << std::endl;return -1;}// 连接到服务器if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {std::cerr << "Connection Failed" << std::endl;return -1;}send(sock, message, strlen(message), 0);std::cout << "Hello message sent" << std::endl;read(sock, buffer, BUFFER_SIZE);std::cout << "Message received: " << buffer << std::endl;close(sock);return 0;
}

运行结果

// 服务器端输出
Message received: Hello from client
Hello message sent// 客户端输出
Hello message sent
Message received: Hello from server
UDP 示例
UDP 服务器
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>#define PORT 8080
#define BUFFER_SIZE 1024int main() {int sockfd;struct sockaddr_in server_addr, client_addr;char buffer[BUFFER_SIZE];const char* message = "Hello from server";// 创建套接字if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {perror("socket creation failed");exit(EXIT_FAILURE);}memset(&server_addr, 0, sizeof(server_addr));memset(&client_addr, 0, sizeof(client_addr));// 填充服务器信息server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = INADDR_ANY;server_addr.sin_port = htons(PORT);// 绑定套接字if (bind(sockfd, (const struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {perror("bind failed");close(sockfd);exit(EXIT_FAILURE);}socklen_t len = sizeof(client_addr);int n = recvfrom(sockfd, (char *)buffer, BUFFER_SIZE, MSG_WAITALL, (struct sockaddr *)&client_addr, &len);buffer[n] = '\0';std::cout << "Message received: " << buffer << std::endl;sendto(sockfd, message, strlen(message), MSG_CONFIRM, (const struct sockaddr *) &client_addr, len);std::cout << "Hello message sent" << std::endl;close(sockfd);return 0;
}
UDP 客户端
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>#define PORT 8080
#define BUFFER_SIZE 1024int main() {int sockfd;struct sockaddr_in serv_addr;char buffer[BUFFER_SIZE];const char* message = "Hello from client";// 创建套接字if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {perror("socket creation failed");exit(EXIT_FAILURE);}memset(&serv_addr, 0, sizeof(serv_addr));// 填充服务器信息serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(PORT);serv_addr.sin_addr.s_addr = INADDR_ANY;sendto(sockfd, message, strlen(message), MSG_CONFIRM, (const struct sockaddr *) &serv_addr, sizeof(serv_addr));std::cout << "Hello message sent" << std::endl;socklen_t len = sizeof(serv_addr);int n = recvfrom(sockfd, (char *)buffer, BUFFER_SIZE, MSG_WAITALL, (struct sockaddr *) &serv_addr, &len);buffer[n] = '\0';std::cout << "Message received: " << buffer << std::endl;close(sockfd);return 0;
}

运行结果

// 服务器端输出
Message received: Hello from client
Hello message sent// 客户端输出
Hello message sent
Message received: Hello from server
六、总结

通过对 TCP 和 UDP 的深入解析,我们可以得出以下结论:

  • TCP 提供可靠的数据传输,适用于需要高可靠性和顺序保证的应用,如网页浏览、文件传输、电子邮件和远程登录。
  • UDP 提供不可靠但高效的数据传输,适用于对实时性要求高但容错性强的应用,如实时音视频传输、在线游戏、DNS 查询和广播多播应用。

        在实际项目中,选择合适的传输协议至关重要,需要根据具体应用场景的需求来做出决策。希望本文的深入解析能够帮助读者更好地理解 TCP 和 UDP 的区别及其应用场景,提升网络编程能力。

这篇关于学懂C++(三十九):网络编程——深入详解 TCP 和 UDP 的区别和应用场景的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1092191

相关文章

Debezium 与 Apache Kafka 的集成方式步骤详解

《Debezium与ApacheKafka的集成方式步骤详解》本文详细介绍了如何将Debezium与ApacheKafka集成,包括集成概述、步骤、注意事项等,通过KafkaConnect,D... 目录一、集成概述二、集成步骤1. 准备 Kafka 环境2. 配置 Kafka Connect3. 安装 D

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

Spring Cloud LoadBalancer 负载均衡详解

《SpringCloudLoadBalancer负载均衡详解》本文介绍了如何在SpringCloud中使用SpringCloudLoadBalancer实现客户端负载均衡,并详细讲解了轮询策略和... 目录1. 在 idea 上运行多个服务2. 问题引入3. 负载均衡4. Spring Cloud Load

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

在 Spring Boot 中使用 @Autowired和 @Bean注解的示例详解

《在SpringBoot中使用@Autowired和@Bean注解的示例详解》本文通过一个示例演示了如何在SpringBoot中使用@Autowired和@Bean注解进行依赖注入和Bean... 目录在 Spring Boot 中使用 @Autowired 和 @Bean 注解示例背景1. 定义 Stud

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为

SQL 中多表查询的常见连接方式详解

《SQL中多表查询的常见连接方式详解》本文介绍SQL中多表查询的常见连接方式,包括内连接(INNERJOIN)、左连接(LEFTJOIN)、右连接(RIGHTJOIN)、全外连接(FULLOUTER... 目录一、连接类型图表(ASCII 形式)二、前置代码(创建示例表)三、连接方式代码示例1. 内连接(I