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

目录

[一、TCP 的特点及应用场景](#一、TCP 的特点及应用场景)

[1. 可靠性](#1. 可靠性)

[2. 流控制和拥塞控制](#2. 流控制和拥塞控制)

[3. 有序传输](#3. 有序传输)

[4. 应用场景](#4. 应用场景)

[二、UDP 的特点及应用场景](#二、UDP 的特点及应用场景)

[1. 无连接](#1. 无连接)

[2. 不可靠性](#2. 不可靠性)

[3. 轻量级](#3. 轻量级)

[4. 支持广播和多播](#4. 支持广播和多播)

[5. 应用场景](#5. 应用场景)

[三、TCP 和 UDP 的区别](#三、TCP 和 UDP 的区别)

[四、TCP 和 UDP 的工作原理](#四、TCP 和 UDP 的工作原理)

[1. TCP 的工作原理](#1. TCP 的工作原理)

三次握手

数据传输

四次挥手

[2. UDP 的工作原理](#2. UDP 的工作原理)

数据传输

广播和多播

五、实例比较

[TCP 示例](#TCP 示例)

[TCP 服务器](#TCP 服务器)

[TCP 客户端](#TCP 客户端)

[UDP 示例](#UDP 示例)

[UDP 服务器](#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 在不同方面的详细区别:

特性 TCP UDP
连接类型 面向连接 无连接
可靠性 可靠 不可靠
数据传输顺序 保证按序传输 不保证按序传输
流控制 具有流控制机制 无流控制机制
拥塞控制 具有拥塞控制机制 无拥塞控制机制
头部开销 较大(20-60 字节) 较小(8 字节)
传输效率 较低(由于可靠性机制) 较高
适用场景 需要高可靠性的数据传输 需要高实时性的数据传输
应用示例 HTTP、FTP、SMTP、SSH VoIP、视频会议、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 服务器
cpp 复制代码
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8080
#define BUFFER_SIZE 1024

int 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 客户端
cpp 复制代码
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8080
#define BUFFER_SIZE 1024

int 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;
}

运行结果

cpp 复制代码
// 服务器端输出
Message received: Hello from client
Hello message sent

// 客户端输出
Hello message sent
Message received: Hello from server
UDP 示例
UDP 服务器
cpp 复制代码
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8080
#define BUFFER_SIZE 1024

int 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 客户端
cpp 复制代码
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8080
#define BUFFER_SIZE 1024

int 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;
}

运行结果

cpp 复制代码
// 服务器端输出
Message received: Hello from client
Hello message sent

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

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

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

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

相关推荐
彭于晏6891 分钟前
Android广播
android·java·开发语言
前端张三16 分钟前
Mac 电脑pink 后端ip地址进行本地联调
服务器·tcp/ip·macos
弱冠少年27 分钟前
websockets库使用(基于Python)
开发语言·python·numpy
长天一色27 分钟前
C语言日志类库 zlog 使用指南(第五章 配置文件)
c语言·开发语言
仍有未知等待探索32 分钟前
Linux 传输层UDP
linux·运维·udp
MinBadGuy35 分钟前
【GeekBand】C++设计模式笔记5_Observer_观察者模式
c++·设计模式
一般清意味……39 分钟前
快速上手C语言【上】(非常详细!!!)
c语言·开发语言
卑微求AC40 分钟前
(C语言贪吃蛇)16.贪吃蛇食物位置随机(完结撒花)
linux·c语言·开发语言·嵌入式·c语言贪吃蛇
技术无疆1 小时前
【Python】Streamlit:为数据科学与机器学习打造的简易应用框架
开发语言·人工智能·python·深度学习·神经网络·机器学习·数据挖掘
金灰1 小时前
HTML5--裸体回顾
java·开发语言·前端·javascript·html·html5