超详细讲解:TCP / UDP / HTTP / HTTPS 四种常见协议

无论你是浏览网页、登录系统,还是打游戏、看视频,底层都绕不开两个关键的传输协议 ------ TCPUDP ,以及两个应用层的明星 ------ HTTPHTTPS。本篇博客将带你系统梳理这四种常见协议的原理与差异,从底层传输到高层通信,从性能对比到安全机制,帮助你全面理解它们在实际开发与网络架构中的角色与选择依据。

1️⃣ TCP:稳定可靠的"快递包裹"

TCP(Transmission Control Protocol,传输控制协议)是一种在计算机网络中使用的面向连接的、可靠的传输层通信协议,在互联网中起着至关重要的作用,为应用层提供了稳定的数据传输服务。

  • 面向连接:在开始传输数据之前,通信双方需要通过 "三次握手" 建立起一条可靠的连接通道。这就好比打电话,双方要先拨通电话,确认对方在线且可以正常交流后,才开始正式交谈。建立连接后,双方会维护连接状态,直到数据传输结束并通过 "四次挥手" 关闭连接。

  • 可靠传输:TCP 使用了多种机制来确保数据能够准确无误地到达接收端。例如,通过序列号对每个数据段进行编号,接收端可以根据序列号来判断数据是否缺失或重复;采用确认应答机制,接收端收到数据后会向发送端发送确认信息,发送端只有在收到确认后才会认为数据已成功传输,否则会进行重传。

  • 流量控制:为了防止发送方发送数据过快,导致接收方来不及处理而丢失数据,TCP 实现了流量控制功能。接收方会在确认报文中告知发送方自己当前的接收窗口大小,发送方根据这个窗口大小来调整发送数据的速率。

  • 拥塞控制:当网络出现拥塞时,TCP 能够自动调整发送数据的速率,以缓解网络拥塞。常见的拥塞控制算法有慢开始、拥塞避免、快重传和快恢复等。

  • 字节流服务:TCP 将应用层的数据看作是无结构的字节流,它不关心应用层数据的具体含义和结构,只是按照顺序将数据可靠地传输到对方。

1.2 代码示例

Tcp服务器(Server):

复制代码
// tcp_server.cpp
#include <iostream>
#include <string>
#include <cstring>
#ifdef _WIN32
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#else
#include <unistd.h>
#include <arpa/inet.h>
#endif

int main() {
#ifdef _WIN32
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2,2), &wsaData);
#endif

    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd == -1) {
        std::cerr << "Socket creation failed\n";
        return -1;
    }

    sockaddr_in server_addr{};
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8888);  // 监听端口
    server_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(server_fd, (sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        std::cerr << "Bind failed\n";
        return -1;
    }

    listen(server_fd, 5);
    std::cout << "Server listening on port 8888...\n";

    sockaddr_in client_addr{};
    socklen_t client_len = sizeof(client_addr);
    int client_fd = accept(server_fd, (sockaddr*)&client_addr, &client_len);

    if (client_fd >= 0) {
        char buffer[1024] = {0};
        int len = recv(client_fd, buffer, sizeof(buffer), 0);
        std::cout << "Received from client: " << std::string(buffer, len) << "\n";

        std::string reply = "Hello from server";
        send(client_fd, reply.c_str(), reply.size(), 0);
    }

#ifdef _WIN32
    closesocket(client_fd);
    WSACleanup();
#else
    close(client_fd);
#endif
    return 0;
}

Tcp客户端(Client):

复制代码
// tcp_client.cpp
#include <iostream>
#include <string>
#include <cstring>
#ifdef _WIN32
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#else
#include <unistd.h>
#include <arpa/inet.h>
#endif

int main() {
#ifdef _WIN32
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2,2), &wsaData);
#endif

    int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (sock_fd < 0) {
        std::cerr << "Socket creation failed\n";
        return -1;
    }

    sockaddr_in server_addr{};
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8888);
    inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);

    if (connect(sock_fd, (sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        std::cerr << "Connection failed\n";
        return -1;
    }

    std::string message = "Hello from client";
    send(sock_fd, message.c_str(), message.size(), 0);

    char buffer[1024] = {0};
    int len = recv(sock_fd, buffer, sizeof(buffer), 0);
    std::cout << "Received from server: " << std::string(buffer, len) << "\n";

#ifdef _WIN32
    closesocket(sock_fd);
    WSACleanup();
#else
    close(sock_fd);
#endif
    return 0;
}

(Server)

💡 核心流程(5步):

  1. 创建套接字socket()

  2. 绑定地址和端口bind()

  3. 监听连接请求listen()

  4. 接受客户端连接accept()

  5. 读写数据通信recv() / send()

可选第6步:关闭连接 close()(或 closesocket() on Windows)

(Client)

💡 核心流程(3步):

  1. 创建套接字socket()

  2. 连接服务器connect()

  3. 读写数据通信send() / recv()

1.3进阶技巧(开发实战建议)

✅ 服务端建议:

  • 多线程/线程池:每个客户端一个线程,或使用线程池处理任务(如 Boost.Asio)。

  • select()/epoll():高并发场景推荐使用,避免线程爆炸。

  • 设置 SO_REUSEADDR:防止重启服务器时 "地址已被占用"。

    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

  • 封装连接类 :将 read() / write() 封装成类函数,提升可读性与可维护性。

✅ 客户端建议:

  • 重连机制:连接失败时加入重试机制。

  • 超时控制 :使用 setsockopt() 设置连接和读写超时。

  • 自动心跳包:长连接建议定时发送心跳,保持活跃。

✅ 通用建议:

  • 使用 std::string / std::vector<char> 管理数据缓冲区

  • 对于跨平台开发,建议封装 socket 操作类,统一接口

  • 日志调试非常重要,推荐用 spdloglog4cpp 等库

2️⃣ UDP:快速轻量的"明信片"

UDP(User Datagram Protocol,用户数据报协议)是一种在传输层工作的通信协议,与 TCP(传输控制协议) 同属传输层,在网络通信中发挥着重要作用。

发送端将应用层要发送的数据加上 UDP 首部(包含源端口号、目的端口号、长度、校验和等字段),封装成 UDP 报文,然后通过网络层将报文发送出去。接收端收到 UDP 报文后,根据首部中的目的端口号将数据交付给相应的应用程序。如果校验和检查发现报文在传输过程中出现错误,UDP 会直接丢弃该报文,不会采取重传等补救措施。

  • 无连接:UDP 在发送数据之前不需要像 TCP 那样通过三次握手建立连接。发送端直接将数据封装成 UDP 报文并发送出去,接收端收到报文后也无需进行专门的连接确认。这使得 UDP 的通信过程更加简单、快捷,就像发送明信片,不需要提前和对方打招呼确认能否接收,直接寄出即可。

  • 不可靠传输:UDP 没有确认应答、重传等机制来保证数据一定能到达接收端,也不能保证数据的顺序和完整性。如果在传输过程中数据丢失或出错,UDP 不会自动处理,需要应用层自行解决。不过,在一些对实时性要求高、允许少量数据丢失的场景中,这种特性反而不会因为等待确认和重传而增加延迟。

  • 面向数据报:UDP 以数据报为单位进行数据传输,每个 UDP 报文都包含完整的源端口、目的端口、数据等信息,发送端发送的每个数据报都是相互独立的,接收端接收数据报的顺序可能与发送端发送的顺序不一致。

  • 高效率:由于没有复杂的连接建立、流量控制、拥塞控制等机制,UDP 的传输效率相对较高,开销较小,适合对传输效率要求高,能容忍一定数据丢失的应用场景。

  • 支持广播和多播:UDP 允许发送端将数据报发送给网络中的所有主机(广播),或者发送给一组特定的主机(多播),而 TCP 只能进行一对一的通信 。

2.2 代码示例

Udp服务器(Server):

复制代码
// udp_server.cpp
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
#include <unistd.h>

int main() {
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        std::cerr << "socket creation failed\n";
        return -1;
    }

    sockaddr_in servaddr{};
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = INADDR_ANY;
    servaddr.sin_port = htons(8888);

    if (bind(sockfd, (const sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
        std::cerr << "bind failed\n";
        return -1;
    }

    char buffer[1024];
    sockaddr_in cliaddr{};
    socklen_t len = sizeof(cliaddr);

    std::cout << "UDP Server listening on port 8888...\n";
    while (true) {
        int n = recvfrom(sockfd, buffer, sizeof(buffer)-1, 0, (sockaddr*)&cliaddr, &len);
        if (n > 0) {
            buffer[n] = '\0';
            std::cout << "Received from client: " << buffer << "\n";

            std::string reply = "Hello from UDP Server";
            sendto(sockfd, reply.c_str(), reply.size(), 0, (sockaddr*)&cliaddr, len);
        }
    }

    close(sockfd);
    return 0;
}

Udp客户端(Client):

复制代码
// udp_client.cpp
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
#include <unistd.h>

int main() {
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        std::cerr << "socket creation failed\n";
        return -1;
    }

    sockaddr_in servaddr{};
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(8888);
    inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);

    std::string msg = "Hello from UDP Client";
    sendto(sockfd, msg.c_str(), msg.size(), 0, (const sockaddr*)&servaddr, sizeof(servaddr));
    std::cout << "Sent to server: " << msg << "\n";

    char buffer[1024];
    socklen_t len = sizeof(servaddr);
    int n = recvfrom(sockfd, buffer, sizeof(buffer)-1, 0, (sockaddr*)&servaddr, &len);
    if (n > 0) {
        buffer[n] = '\0';
        std::cout << "Received from server: " << buffer << "\n";
    }

    close(sockfd);
    return 0;
}

⚠️ 注意事项

UDP 是无连接的

  • 不需要 listen()accept(),直接 sendto / recvfrom 就可以。

数据包大小限制

  • 一般单个 UDP 报文 ≤ 64KB,建议小于 MTU(通常 1500 字节)。

无可靠性保证

  • 丢包、乱序需要上层自己处理。

适合广播/多播

  • 可通过 setsockopt() 配置广播、多播。

2.3 进阶技巧 (开发实战建议)

1️⃣ 使用 非阻塞套接字 + select()/epoll() 提高并发性能

UDP 不需要连接,但 recvfrom() 默认是阻塞的,建议:

复制代码
fcntl(sockfd, F_SETFL, O_NONBLOCK);  // 设置非阻塞

结合 select()/poll()/epoll() 实现事件驱动,提升响应效率。

2️⃣ 自定义协议实现"伪可靠"机制(如 ACK/重发)

因为 UDP 本身不可靠,在重要数据传输中可自建机制:

  • 每个包加序号 seq_id

  • 接收端回 ACK

  • 超时未收到 ACK,重发

适用于低延迟、高可靠需求,如在线对战游戏、控制信号。

3️⃣ 使用 多线程 / 线程池 提高吞吐量

虽然 UDP 不涉及连接,但高并发数据接收与处理仍推荐多线程:

  • 主线程接收数据

  • 投递给工作线程池处理(解析 / 响应 / 落库等)

4️⃣ 支持 广播 / 多播,实现局域网自动发现

复制代码
int yes = 1;
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));

配合 255.255.255.255 或多播地址如 224.0.0.1,实现设备发现、状态同步。

5️⃣ 控制缓冲区大小,防止数据丢失

默认 UDP 缓冲区较小,推荐调大:

复制代码
int size = 4 * 1024 * 1024;
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));

否则在高并发场景下可能出现 recvfrom() 丢包。

6️⃣ 使用 connect() 优化性能(非必须)

虽然 UDP 是无连接的,也可以调用 connect() 绑定默认目标地址:

复制代码
connect(sockfd, (sockaddr*)&dest, sizeof(dest));
send(sockfd, buf, len, 0);  // 替代 sendto()

这样内核可以省略每次地址验证,提升性能

7️⃣ 使用 MSG_PEEK 预读数据头部或快速丢包

复制代码
recvfrom(sockfd, buf, sizeof(buf), MSG_PEEK, ...);

可用于查看包长度、来源 IP,做过滤处理后再正式读取。

8️⃣ 用 CRC32 / HMAC 校验数据完整性

UDP 没有校验机制,容易被篡改或传输错误,建议自行加上:

  • CRC32 校验(轻量)

  • HMAC-SHA256(安全通信)

3️⃣ HTTP:网页通信的标准语言

HTTP(Hypertext Transfer Protocol,超文本传输协议)是互联网上应用最广泛的客户端 - 服务器协议,用于传输超文本(如 HTML、图片、视频等)。它基于 TCP/IP 协议栈,是万维网(WWW)的核心协议。

  1. 无状态

    服务器不保存客户端的历史请求信息,每次请求都是独立的。这一特性简化了服务器设计,但也导致需要通过 Cookie、Session 等机制维护状态(如登录状态)。

  2. 请求 - 响应模式

    由客户端(如浏览器、APP)主动发起请求,服务器接收后返回响应,是典型的 "一问一答" 模式。

  3. 可扩展

    通过自定义请求头(Header)和响应头,支持功能扩展(如缓存控制、跨域资源共享 CORS)。

  4. 媒体无关

    可传输任意类型的数据(文本、图片、音频等),通过Content-Type头指定数据格式(如text/htmlimage/jpeg)。

3.2 代码示例:

以浏览器访问网页为例,HTTP 的完整交互流程如下:

  1. 建立 TCP 连接:客户端通过 TCP 与服务器的 80 端口(默认 HTTP 端口)建立连接(三次握手)。

  2. 发送 HTTP 请求:客户端向服务器发送请求报文(如获取某个网页)。

  3. 服务器处理请求:服务器解析请求,处理业务逻辑(如查询数据库)。

  4. 返回 HTTP 响应:服务器将处理结果封装为响应报文,返回给客户端。

  5. 关闭 TCP 连接:若为短连接,数据传输完成后断开连接(四次挥手);若为长连接,连接可复用。

Http服务器(Server):

复制代码
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <netinet/in.h>

int main() {
    // 创建 socket
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) {
        std::cerr << "Socket creation failed\n";
        return -1;
    }

    // 设置地址可重用
    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    // 设置服务器地址
    sockaddr_in server_addr{};
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(8080);

    // 绑定地址
    if (bind(server_fd, (sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        std::cerr << "Bind failed\n";
        return -1;
    }

    // 监听连接
    if (listen(server_fd, 5) < 0) {
        std::cerr << "Listen failed\n";
        return -1;
    }

    std::cout << "HTTP server running on http://localhost:8080\n";

    while (true) {
        // 接受客户端连接
        sockaddr_in client_addr{};
        socklen_t client_len = sizeof(client_addr);
        int client_fd = accept(server_fd, (sockaddr*)&client_addr, &client_len);
        if (client_fd < 0) {
            std::cerr << "Accept failed\n";
            continue;
        }

        // 接收客户端请求
        char buffer[2048] = {0};
        recv(client_fd, buffer, sizeof(buffer) - 1, 0);
        std::cout << "Received request:\n" << buffer << "\n";

        // 构造 HTTP 响应
        std::string response =
            "HTTP/1.1 200 OK\r\n"
            "Content-Type: text/html\r\n"
            "Connection: close\r\n"
            "\r\n"
            "<!DOCTYPE html>"
            "<html>"
            "<head><title>C++ HTTP Server</title></head>"
            "<body><h1>Hello from C++ HTTP Server!</h1></body>"
            "</html>";

        // 发送响应
        send(client_fd, response.c_str(), response.size(), 0);

        // 关闭客户端连接
        close(client_fd);
    }

    // 关闭服务器 socket
    close(server_fd);
    return 0;
}

Http客户端(Client):

复制代码
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <netinet/in.h>

int main() {
    // 创建 socket
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) {
        std::cerr << "Socket creation failed\n";
        return -1;
    }

    // 设置地址可重用
    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    // 设置服务器地址
    sockaddr_in server_addr{};
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(8080);

    // 绑定地址
    if (bind(server_fd, (sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        std::cerr << "Bind failed\n";
        return -1;
    }

    // 监听连接
    if (listen(server_fd, 5) < 0) {
        std::cerr << "Listen failed\n";
        return -1;
    }

    std::cout << "HTTP server running on http://localhost:8080\n";

    while (true) {
        // 接受客户端连接
        sockaddr_in client_addr{};
        socklen_t client_len = sizeof(client_addr);
        int client_fd = accept(server_fd, (sockaddr*)&client_addr, &client_len);
        if (client_fd < 0) {
            std::cerr << "Accept failed\n";
            continue;
        }

        // 接收客户端请求
        char buffer[2048] = {0};
        recv(client_fd, buffer, sizeof(buffer) - 1, 0);
        std::cout << "Received request:\n" << buffer << "\n";

        // 构造 HTTP 响应
        std::string response =
            "HTTP/1.1 200 OK\r\n"
            "Content-Type: text/html\r\n"
            "Connection: close\r\n"
            "\r\n"
            "<!DOCTYPE html>"
            "<html>"
            "<head><title>C++ HTTP Server</title></head>"
            "<body><h1>Hello from C++ HTTP Server!</h1></body>"
            "</html>";

        // 发送响应
        send(client_fd, response.c_str(), response.size(), 0);

        // 关闭客户端连接
        close(client_fd);
    }

    // 关闭服务器 socket
    close(server_fd);
    return 0;
}

注意事项

  • 单线程处理 :此示例为单线程,实际生产环境建议使用多线程或事件驱动(如 epoll)处理并发。
  • 跨平台 :Windows 下需包含 <winsock2.h> 并调用 WSAStartup() 初始化。
  • 请求解析:当前仅处理简单 GET 请求,复杂场景需解析 HTTP 请求头和方法。
  • SO_REUSEADDR:设置地址可重用,避免端口占用问题。

3.3进阶技巧

  • 多线程/异步 :使用线程池或 select/epoll 支持多客户端并发。

  • 支持 POST/JSON:解析请求体,处理复杂数据格式。

  • HTTPS 支持 :集成 OpenSSL 或使用库如 cpp-httplib

  • 推荐库cpp-httplib(轻量单文件)、Boost.Beast(功能强大)。

4️⃣ HTTPS:加密版 HTTP,安全通信之选

HTTPS(HTTP Secure)是 HTTP 协议的安全版本,通过TLS/SSL 加密身份验证解决了 HTTP 明文传输的安全隐患。

1. 加密机制

HTTPS 通过对称加密非对称加密结合实现安全通信:

  • 非对称加密(TLS 握手阶段):

    • 客户端与服务器交换公钥。

    • 用服务器公钥加密生成的会话密钥(对称密钥)。

  • 对称加密(数据传输阶段):

    • 使用会话密钥加密 / 解密 HTTP 数据,效率更高。

2. 身份验证

  • 数字证书:服务器通过 CA(证书颁发机构)签发的证书证明自己的身份。

  • 证书链验证:客户端验证服务器证书的合法性(签名、有效期、域名匹配等)。

3. TLS 握手流程

  1. 客户端 Hello:发送支持的 TLS 版本、加密算法等。

  2. 服务器 Hello:选择 TLS 版本和加密算法,发送证书。

  3. 证书验证:客户端验证证书有效性。

  4. 密钥交换:客户端生成会话密钥,用服务器公钥加密后发送。

  5. 握手完成:双方使用会话密钥开始加密通信。

4.2 代码示例

Https 服务器(Server):

复制代码
#include <iostream>
#include <string>
#include <cstring>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <netinet/in.h>
#include <unistd.h>

void init_openssl() {
    SSL_load_error_strings();
    OpenSSL_add_ssl_algorithms();
}

void cleanup_openssl() {
    EVP_cleanup();
}

int main() {
    // 初始化 OpenSSL
    init_openssl();
    SSL_CTX* ctx = SSL_CTX_new(TLS_server_method());
    if (!ctx) {
        std::cerr << "SSL_CTX_new failed\n";
        return -1;
    }

    // 加载证书和私钥
    if (SSL_CTX_use_certificate_file(ctx, "server.crt", SSL_FILETYPE_PEM) <= 0) {
        std::cerr << "Failed to load certificate: " << ERR_error_string(ERR_get_error(), nullptr) << "\n";
        SSL_CTX_free(ctx);
        return -1;
    }
    if (SSL_CTX_use_PrivateKey_file(ctx, "server.key", SSL_FILETYPE_PEM) <= 0) {
        std::cerr << "Failed to load private key: " << ERR_error_string(ERR_get_error(), nullptr) << "\n";
        SSL_CTX_free(ctx);
        return -1;
    }

    // 创建 socket
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) {
        std::cerr << "Socket creation failed\n";
        SSL_CTX_free(ctx);
        return -1;
    }

    // 设置地址可重用
    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    // 设置服务器地址
    sockaddr_in server_addr{};
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(8443); // HTTPS 默认端口为 443,此处用 8443 避免权限问题

    // 绑定地址
    if (bind(server_fd, (sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        std::cerr << "Bind failed\n";
        close(server_fd);
        SSL_CTX_free(ctx);
        return -1;
    }

    // 监听连接
    if (listen(server_fd, 5) < 0) {
        std::cerr << "Listen failed\n";
        close(server_fd);
        SSL_CTX_free(ctx);
        return -1;
    }

    std::cout << "HTTPS server running on https://localhost:8443\n";

    while (true) {
        // 接受客户端连接
        sockaddr_in client_addr{};
        socklen_t client_len = sizeof(client_addr);
        int client_fd = accept(server_fd, (sockaddr*)&client_addr, &client_len);
        if (client_fd < 0) {
            std::cerr << "Accept failed\n";
            continue;
        }

        // 创建 SSL 连接
        SSL* ssl = SSL_new(ctx);
        SSL_set_fd(ssl, client_fd);
        if (SSL_accept(ssl) <= 0) {
            std::cerr << "SSL_accept failed: " << ERR_error_string(ERR_get_error(), nullptr) << "\n";
            SSL_free(ssl);
            close(client_fd);
            continue;
        }

        // 接收客户端请求
        char buffer[2048] = {0};
        int bytes_received = SSL_read(ssl, buffer, sizeof(buffer) - 1);
        if (bytes_received > 0) {
            buffer[bytes_received] = '\0';
            std::cout << "Received request:\n" << buffer << "\n";
        }

        // 构造 HTTPS 响应
        std::string response =
            "HTTP/1.1 200 OK\r\n"
            "Content-Type: text/html\r\n"
            "Connection: close\r\n"
            "\r\n"
            "<!DOCTYPE html>"
            "<html>"
            "<head><title>C++ HTTPS Server</title></head>"
            "<body><h1>Hello from C++ HTTPS Server!</h1></body>"
            "</html>";

        // 发送响应
        SSL_write(ssl, response.c_str(), response.size());

        // 清理
        SSL_free(ssl);
        close(client_fd);
    }

    // 清理
    close(server_fd);
    SSL_CTX_free(ctx);
    cleanup_openssl();
    return 0;
}

Https 客户端(Client):

复制代码
#include <iostream>
#include <string>
#include <cstring>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <arpa/inet.h>
#include <unistd.h>

void init_openssl() {
    SSL_load_error_strings();
    OpenSSL_add_ssl_algorithms();
}

void cleanup_openssl() {
    EVP_cleanup();
}

int main() {
    // 初始化 OpenSSL
    init_openssl();
    SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());
    if (!ctx) {
        std::cerr << "SSL_CTX_new failed\n";
        return -1;
    }

    // 创建 socket
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        std::cerr << "Socket creation failed\n";
        SSL_CTX_free(ctx);
        return -1;
    }

    // 设置服务器地址
    sockaddr_in servaddr{};
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(443); // HTTPS 默认端口
    inet_pton(AF_INET, "93.184.216.34", &servaddr.sin_addr); // example.com IP

    // 连接服务器
    if (connect(sockfd, (sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
        std::cerr << "Connection failed\n";
        close(sockfd);
        SSL_CTX_free(ctx);
        return -1;
    }

    // 创建 SSL 连接
    SSL* ssl = SSL_new(ctx);
    SSL_set_fd(ssl, sockfd);
    if (SSL_connect(ssl) <= 0) {
        std::cerr << "SSL_connect failed: " << ERR_error_string(ERR_get_error(), nullptr) << "\n";
        SSL_free(ssl);
        close(sockfd);
        SSL_CTX_free(ctx);
        return -1;
    }

    // 设置服务器主机名以进行 SNI(服务器名称指示)
    SSL_set_tlsext_host_name(ssl, "example.com");

    // 构造 HTTPS GET 请求
    std::string request =
        "GET / HTTP/1.1\r\n"
        "Host: example.com\r\n"
        "Connection: close\r\n"
        "\r\n";
    if (SSL_write(ssl, request.c_str(), request.size()) <= 0) {
        std::cerr << "SSL_write failed\n";
    }

    // 接收响应
    char buffer[4096];
    std::string response;
    int bytes_received;
    while ((bytes_received = SSL_read(ssl, buffer, sizeof(buffer) - 1)) > 0) {
        buffer[bytes_received] = '\0';
        response += buffer;
    }

    // 输出响应
    std::cout << "HTTPS Response:\n" << response << "\n";

    // 清理
    SSL_free(ssl);
    close(sockfd);
    SSL_CTX_free(ctx);
    cleanup_openssl();
    return 0;
}

由于 HTTPS 是基于 TLS/SSL 的安全协议,代码需要使用 OpenSSL 处理加密通信。此示例适用于 Linux/macOS,Windows 需额外配置 OpenSSL 环境。

生成自签名证书和私钥

在 Linux/macOS 上生成自签名证书和私钥(需安装 OpenSSL):

复制代码
openssl req -x509 -newkey rsa:2048 -keyout server.key -out server.crt -days 365 -nodes
  • 回答提示时可直接回车,或设置 Common Name 为 localhost

  • 生成的 server.crtserver.key 需与 https_server.cpp 放在同一目录。

注意事项

  • 自签名证书:生产环境应使用受信任的 CA 证书(如 Let's Encrypt)。

  • 端口权限:若使用 443 端口,需 root 权限或配置端口转发。

  • 跨平台 :Windows 需包含 <winsock2.h> 并调用 WSAStartup() 初始化网络。

  • 单线程 :当前为单线程处理,生产环境建议多线程或事件驱动(如 epoll)。

  • 证书验证:客户端需配置信任自签名证书,或禁用验证(不安全,仅测试用)。

📌 五 总结

5.1 TCP与UDP

TCP(传输控制协议)和 UDP(用户数据报协议)是计算机网络传输层的两大核心协议,在 计算机 开发中应用广泛。

特性 TCP UDP
是否连接 面向连接(有握手) 无连接(无需握手)
可靠性 可靠(保证顺序、不丢包) 不可靠(可能丢包、乱序)
传输速度 相对较慢(开销大) 较快(开销小)
应用场景 网页、文件传输、邮件、数据库等 视频/语音通话、直播、游戏等
是否流式传输 是(按字节流) 否(按数据报)
  1. 错误处理

    • TCP :需处理连接断开(如ECONNRESET)、超时等异常。

    • UDP:需处理丢包(如应用层确认机制)、缓冲区溢出。

  2. 并发模型

    • TCP :推荐使用异步 I/O(如Boost.Asio)或多线程处理连接。

    • UDP:单线程即可处理大量并发请求(无连接状态)。

  3. 跨平台兼容性

    • Linux:可使用epoll(高性能)。

    • Windows:可使用IOCP(异步 I/O 完成端口)。

  4. 协议扩展

    • TCP:可基于 TCP 实现自定义可靠协议(如 RPC 框架)。

    • UDP:可实现应用层可靠 UDP(如 QUIC 协议,已被 HTTP/3 采用)。

5.2 Http和Https

HTTP(超文本传输协议)和 HTTPS(超文本安全传输协议)是 Web 应用的基础协议,二者的核心差异在于安全性与性能。

特性 HTTP HTTPS
协议端口 80 443
是否加密 ❌ 明文传输 ✅ 加密传输(SSL/TLS)
安全性 低,易被监听、篡改 高,防窃听、防篡改、防伪造
证书 无需证书 需要 SSL/TLS 数字证书
URL 前缀 http:// https://
性能开销 较小 稍高(但现代硬件/HTTP/2基本忽略不计)
SEO(搜索排名) 不利(被视为不安全) 有利(Google 更推荐 HTTPS)
  • HTTP:不安全,仅适用于无敏感信息、局域网或调试。
  • HTTPS:现代网站/应用的默认选择,保护用户数据、提升信任度和 SEO 排名。
  • ✅ 使用 Let's Encrypt 可免费部署 HTTPS(配合 nginx / apache / C++ 服务框架等)。

无论是稳重可靠的 TCP,还是轻巧灵活的 UDP,亦或是支撑整个 Web 世界的 HTTP 与 HTTPS,它们都像高速公路上的不同车道,各司其职,共同构建起现代网络通信的基础框架。选择哪种协议,并没有"最优",只有"最适合"。希望通过本篇文章,你能更有信心在不同业务场景下,合理选型、精准判断,写出更加稳定、安全、快速的网络应用。网络虽无形,协议有乾坤。理解它们,就是迈向更高技术水平的重要一步。

相关推荐
程序员杨叔34 分钟前
fiddler/charles https配置完毕依然无法抓取APP https请求的解决办法
软件测试·https·fiddler
Bin努力加餐饭3 小时前
【HTTP版本演变】
http
勇闯逆流河5 小时前
【C++】list及其模拟实现
开发语言·c++
小指纹5 小时前
巧用Bitset!优化dp
数据结构·c++·算法·代理模式·dp·bitset
liulilittle6 小时前
游戏加速器核心技术:动态超发
开发语言·网络·c++·网络协议·游戏·加速器·游戏加速
不想写bug呀7 小时前
HTTP协议介绍
网络·网络协议·http
oioihoii9 小时前
C++11迭代器改进:深入理解std::begin、std::end、std::next与std::prev
java·开发语言·c++
赖亦无9 小时前
【水动力学】04 二维洪水淹没模型Pypims安装
c++·python·gpu·水动力·洪水
球求了9 小时前
C++:现代 C++ 编程基石,C++11核心特性解析与实践
开发语言·c++·学习·visual studio