无论你是浏览网页、登录系统,还是打游戏、看视频,底层都绕不开两个关键的传输协议 —— TCP 和 UDP,以及两个应用层的明星 —— HTTP 和 HTTPS。本篇博客将带你系统梳理这四种常见协议的原理与差异,从底层传输到高层通信,从性能对比到安全机制,帮助你全面理解它们在实际开发与网络架构中的角色与选择依据。
1️⃣ TCP:稳定可靠的“快递包裹”
TCP(Transmission Control Protocol,传输控制协议)是一种在计算机网络中使用的面向连接的、可靠的传输层通信协议,在互联网中起着至关重要的作用,为应用层提供了稳定的数据传输服务。
面向连接:在开始传输数据之前,通信双方需要通过 “三次握手” 建立起一条可靠的连接通道。这就好比打电话,双方要先拨通电话,确认对方在线且可以正常交流后,才开始正式交谈。建立连接后,双方会维护连接状态,直到数据传输结束并通过 “四次挥手” 关闭连接。
可靠传输:TCP 使用了多种机制来确保数据能够准确无误地到达接收端。例如,通过序列号对每个数据段进行编号,接收端可以根据序列号来判断数据是否缺失或重复;采用确认应答机制,接收端收到数据后会向发送端发送确认信息,发送端只有在收到确认后才会认为数据已成功传输,否则会进行重传。
流量控制:为了防止发送方发送数据过快,导致接收方来不及处理而丢失数据,TCP 实现了流量控制功能。接收方会在确认报文中告知发送方自己当前的接收窗口大小,发送方根据这个窗口大小来调整发送数据的速率。
拥塞控制:当网络出现拥塞时,TCP 能够自动调整发送数据的速率,以缓解网络拥塞。常见的拥塞控制算法有慢开始、拥塞避免、快重传和快恢复等。
字节流服务:TCP 将应用层的数据看作是无结构的字节流,它不关心应用层数据的具体含义和结构,只是按照顺序将数据可靠地传输到对方。
1.2 代码示例
Tcp服务器(Server):
// tcp_server.cpp
#include
#include
#include
#ifdef _WIN32
#include
#pragma comment(lib,"ws2_32.lib")
#else
#include
#include
#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
#include
#include
#ifdef _WIN32
#include
#pragma comment(lib,"ws2_32.lib")
#else
#include
#include
#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步):
创建套接字:socket()
绑定地址和端口:bind()
监听连接请求:listen()
接受客户端连接:accept()
读写数据通信:recv() / send()
可选第6步:关闭连接 close()(或 closesocket() on Windows)
(Client)
💡 核心流程(3步):
创建套接字:socket()
连接服务器:connect()
读写数据通信: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
对于跨平台开发,建议封装 socket 操作类,统一接口
日志调试非常重要,推荐用 spdlog 或 log4cpp 等库
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
#include
#include
#include
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
#include
#include
#include
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)的核心协议。
无状态:
服务器不保存客户端的历史请求信息,每次请求都是独立的。这一特性简化了服务器设计,但也导致需要通过 Cookie、Session 等机制维护状态(如登录状态)。
请求 - 响应模式:
由客户端(如浏览器、APP)主动发起请求,服务器接收后返回响应,是典型的 “一问一答” 模式。
可扩展:
通过自定义请求头(Header)和响应头,支持功能扩展(如缓存控制、跨域资源共享 CORS)。
媒体无关:
可传输任意类型的数据(文本、图片、音频等),通过Content-Type头指定数据格式(如text/html、image/jpeg)。
3.2 代码示例:
以浏览器访问网页为例,HTTP 的完整交互流程如下:
建立 TCP 连接:客户端通过 TCP 与服务器的 80 端口(默认 HTTP 端口)建立连接(三次握手)。
发送 HTTP 请求:客户端向服务器发送请求报文(如获取某个网页)。
服务器处理请求:服务器解析请求,处理业务逻辑(如查询数据库)。
返回 HTTP 响应:服务器将处理结果封装为响应报文,返回给客户端。
关闭 TCP 连接:若为短连接,数据传输完成后断开连接(四次挥手);若为长连接,连接可复用。
Http服务器(Server):
#include
#include
#include
#include
#include
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"
""
""
"
"
Hello from C++ HTTP Server!
""";
// 发送响应
send(client_fd, response.c_str(), response.size(), 0);
// 关闭客户端连接
close(client_fd);
}
// 关闭服务器 socket
close(server_fd);
return 0;
}
Http客户端(Client):
#include
#include
#include
#include
#include
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"
""
""
"
"
Hello from C++ HTTP Server!
""";
// 发送响应
send(client_fd, response.c_str(), response.size(), 0);
// 关闭客户端连接
close(client_fd);
}
// 关闭服务器 socket
close(server_fd);
return 0;
}
注意事项
单线程处理:此示例为单线程,实际生产环境建议使用多线程或事件驱动(如 epoll)处理并发。跨平台:Windows 下需包含
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 握手流程
客户端 Hello:发送支持的 TLS 版本、加密算法等。
服务器 Hello:选择 TLS 版本和加密算法,发送证书。
证书验证:客户端验证证书有效性。
密钥交换:客户端生成会话密钥,用服务器公钥加密后发送。
握手完成:双方使用会话密钥开始加密通信。
4.2 代码示例
Https 服务器(Server):
#include
#include
#include
#include
#include
#include
#include
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"
""
""
"
"
Hello from C++ HTTPS Server!
""";
// 发送响应
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
#include
#include
#include
#include
#include
#include
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.crt 和 server.key 需与 https_server.cpp 放在同一目录。
注意事项
自签名证书:生产环境应使用受信任的 CA 证书(如 Let’s Encrypt)。
端口权限:若使用 443 端口,需 root 权限或配置端口转发。
跨平台:Windows 需包含
单线程:当前为单线程处理,生产环境建议多线程或事件驱动(如 epoll)。
证书验证:客户端需配置信任自签名证书,或禁用验证(不安全,仅测试用)。
📌 五 总结
5.1 TCP与UDP
TCP(传输控制协议)和 UDP(用户数据报协议)是计算机网络传输层的两大核心协议,在 计算机 开发中应用广泛。
特性TCPUDP是否连接面向连接(有握手)无连接(无需握手)可靠性可靠(保证顺序、不丢包)不可靠(可能丢包、乱序)传输速度相对较慢(开销大)较快(开销小)应用场景网页、文件传输、邮件、数据库等视频/语音通话、直播、游戏等是否流式传输是(按字节流)否(按数据报)
错误处理:
TCP:需处理连接断开(如ECONNRESET)、超时等异常。
UDP:需处理丢包(如应用层确认机制)、缓冲区溢出。
并发模型:
TCP:推荐使用异步 I/O(如Boost.Asio)或多线程处理连接。
UDP:单线程即可处理大量并发请求(无连接状态)。
跨平台兼容性:
Linux:可使用epoll(高性能)。
Windows:可使用IOCP(异步 I/O 完成端口)。
协议扩展:
TCP:可基于 TCP 实现自定义可靠协议(如 RPC 框架)。
UDP:可实现应用层可靠 UDP(如 QUIC 协议,已被 HTTP/3 采用)。
5.2 Http和Https
HTTP(超文本传输协议)和 HTTPS(超文本安全传输协议)是 Web 应用的基础协议,二者的核心差异在于安全性与性能。
特性HTTPHTTPS协议端口80443是否加密❌ 明文传输✅ 加密传输(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,它们都像高速公路上的不同车道,各司其职,共同构建起现代网络通信的基础框架。选择哪种协议,并没有“最优”,只有“最适合”。希望通过本篇文章,你能更有信心在不同业务场景下,合理选型、精准判断,写出更加稳定、安全、快速的网络应用。网络虽无形,协议有乾坤。理解它们,就是迈向更高技术水平的重要一步。