无论你是浏览网页、登录系统,还是打游戏、看视频,底层都绕不开两个关键的传输协议 ------ TCP 和 UDP ,以及两个应用层的明星 ------ HTTP 和 HTTPS。本篇博客将带你系统梳理这四种常见协议的原理与差异,从底层传输到高层通信,从性能对比到安全机制,帮助你全面理解它们在实际开发与网络架构中的角色与选择依据。
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步):
-
创建套接字 :
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<char>
管理数据缓冲区 -
对于跨平台开发,建议封装 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 <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)的核心协议。
-
无状态 :
服务器不保存客户端的历史请求信息,每次请求都是独立的。这一特性简化了服务器设计,但也导致需要通过 Cookie、Session 等机制维护状态(如登录状态)。
-
请求 - 响应模式 :
由客户端(如浏览器、APP)主动发起请求,服务器接收后返回响应,是典型的 "一问一答" 模式。
-
可扩展 :
通过自定义请求头(Header)和响应头,支持功能扩展(如缓存控制、跨域资源共享 CORS)。
-
媒体无关 :
可传输任意类型的数据(文本、图片、音频等),通过
Content-Type
头指定数据格式(如text/html
、image/jpeg
)。

3.2 代码示例:
以浏览器访问网页为例,HTTP 的完整交互流程如下:
-
建立 TCP 连接:客户端通过 TCP 与服务器的 80 端口(默认 HTTP 端口)建立连接(三次握手)。
-
发送 HTTP 请求:客户端向服务器发送请求报文(如获取某个网页)。
-
服务器处理请求:服务器解析请求,处理业务逻辑(如查询数据库)。
-
返回 HTTP 响应:服务器将处理结果封装为响应报文,返回给客户端。
-
关闭 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 握手流程
-
客户端 Hello:发送支持的 TLS 版本、加密算法等。
-
服务器 Hello:选择 TLS 版本和加密算法,发送证书。
-
证书验证:客户端验证证书有效性。
-
密钥交换:客户端生成会话密钥,用服务器公钥加密后发送。
-
握手完成:双方使用会话密钥开始加密通信。
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.crt
和server.key
需与https_server.cpp
放在同一目录。
注意事项
-
自签名证书:生产环境应使用受信任的 CA 证书(如 Let's Encrypt)。
-
端口权限:若使用 443 端口,需 root 权限或配置端口转发。
-
跨平台 :Windows 需包含
<winsock2.h>
并调用WSAStartup()
初始化网络。 -
单线程 :当前为单线程处理,生产环境建议多线程或事件驱动(如
epoll
)。 -
证书验证:客户端需配置信任自签名证书,或禁用验证(不安全,仅测试用)。
📌 五 总结
5.1 TCP与UDP
TCP(传输控制协议)和 UDP(用户数据报协议)是计算机网络传输层的两大核心协议,在 计算机 开发中应用广泛。
特性 | TCP | UDP |
---|---|---|
是否连接 | 面向连接(有握手) | 无连接(无需握手) |
可靠性 | 可靠(保证顺序、不丢包) | 不可靠(可能丢包、乱序) |
传输速度 | 相对较慢(开销大) | 较快(开销小) |
应用场景 | 网页、文件传输、邮件、数据库等 | 视频/语音通话、直播、游戏等 |
是否流式传输 | 是(按字节流) | 否(按数据报) |
-
错误处理:
-
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 应用的基础协议,二者的核心差异在于安全性与性能。
特性 | 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,它们都像高速公路上的不同车道,各司其职,共同构建起现代网络通信的基础框架。选择哪种协议,并没有"最优",只有"最适合"。希望通过本篇文章,你能更有信心在不同业务场景下,合理选型、精准判断,写出更加稳定、安全、快速的网络应用。网络虽无形,协议有乾坤。理解它们,就是迈向更高技术水平的重要一步。