首页 > 系统相关 >学懂C++(四十):网络编程——深入详解 HTTP、HTTPS 及基于 Windows 系统的 C++ 实现

学懂C++(四十):网络编程——深入详解 HTTP、HTTPS 及基于 Windows 系统的 C++ 实现

时间:2024-08-20 12:51:51浏览次数:13  
标签:HTTP 请求 C++ SSL 学懂 HTTPS 服务器 客户端

目录

一、引言

二、HTTP 协议

1. HTTP 概述

2. HTTP 工作原理

3. HTTP 请求和响应格式

HTTP 请求格式

4. HTTP 状态码

三、HTTPS 协议

1. HTTPS 概述

2. HTTPS 工作原理

四、基于 Windows 系统的 C++ 实现

1. 准备工作

2. HTTP 客户端实现

示例代码

3. HTTPS 客户端实现

示例代码

4. HTTPS 服务器实现

示例代码

五、总结


一、引言

        在现代互联网中,HTTP 和 HTTPS 是两种最常见的应用层协议,用于在客户端(如浏览器)和服务器之间传输数据。理解这两种协议的工作原理、区别及其在 C++ 中的实现,对于网络编程开发者来说至关重要。本文将深入解析 HTTP 和 HTTPS 的基础概念、工作原理及其在 Windows 环境下使用 C++ 进行实现的详细过程。

二、HTTP 协议
1. HTTP 概述

HTTP (Hypertext Transfer Protocol) 是一种应用层协议,用于分布式、协作式和超媒体信息系统。HTTP 是万维网的数据通信基础,主要特点包括:

  • 无状态:每个 HTTP 请求都是独立的,服务器不保留请求间的状态。
  • 简单灵活:易于实现和扩展,支持多种数据类型。
  • 面向请求/响应模型:客户端发送请求,服务器返回响应。
2. HTTP 工作原理

HTTP 使用 TCP 作为传输层协议,遵循以下典型的请求/响应模型:

  1. 客户端与服务器建立 TCP 连接
  2. 客户端发送 HTTP 请求,包含请求行、请求头和可选的请求体。
  3. 服务器处理请求并返回 HTTP 响应,包含状态行、响应头和可选的响应体。
  4. 关闭连接或保持连接,根据连接的设置(如 Connection: keep-alive)。
3. HTTP 请求和响应格式
HTTP 请求格式
请求行:方法 URL 版本
请求头:头部字段名: 值
空行
请求体(可选)

示例:

GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0

HTTP 响应格式

状态行:版本 状态码 状态消息
响应头:头部字段名: 值
空行
响应体(可选)

示例:

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 123

<html>...</html>
4. HTTP 状态码

HTTP 状态码表示服务器对请求的处理结果,常见的状态码包括:

  • 1xx (信息):请求已接收,继续处理。
  • 2xx (成功):请求成功,常见如 200 OK
  • 3xx (重定向):需要进一步操作以完成请求,常见如 301 Moved Permanently
  • 4xx (客户端错误):请求包含语法错误或无法完成,常见如 404 Not Found
  • 5xx (服务器错误):服务器在处理请求时发生错误,常见如 500 Internal Server Error
三、HTTPS 协议
1. HTTPS 概述

HTTPS (Hypertext Transfer Protocol Secure) 是一种通过 SSL/TLS 协议进行加密的 HTTP 协议,用于在客户端和服务器之间建立安全的通信通道。HTTPS 增强了 HTTP 的安全性,主要特点包括:

  • 加密:保护数据在传输过程中不被窃听。
  • 身份验证:验证服务器身份,防止中间人攻击。
  • 数据完整性:确保数据在传输过程中不被篡改。
2. HTTPS 工作原理

HTTPS 使用 SSL/TLS 协议对数据进行加密,典型的工作流程如下:

  1. 客户端发起 HTTPS 连接请求
  2. 服务器返回 SSL/TLS 证书,包含服务器的公钥。
  3. 客户端验证证书的合法性,生成对称密钥,并使用服务器公钥加密该密钥。
  4. 服务器使用私钥解密密钥,建立加密通信通道。
  5. 客户端与服务器进行加密通信
四、基于 Windows 系统的 C++ 实现
1. 准备工作

在 Windows 系统上使用 C++ 进行 HTTP 和 HTTPS 通信,可以使用 Windows API 或开源库(如 libcurl)。本文将使用 Windows API 进行示例演示。

2. HTTP 客户端实现
示例代码
#include <windows.h> // 包含 Windows API 函数和常量
#include <wininet.h> // 包含 Windows Internet 函数
#include <iostream>  // 包含输入输出流
#pragma comment(lib, "wininet.lib") // 链接 wininet 库

int main() {
    // 初始化 Internet 句柄
    HINTERNET hInternet = InternetOpen(L"MyHTTPClient", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
    if (!hInternet) {
        std::cerr << "InternetOpen failed: " << GetLastError() << std::endl;
        return -1;
    }

    // 连接到指定的服务器
    HINTERNET hConnect = InternetConnect(hInternet, L"www.example.com", INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
    if (!hConnect) {
        std::cerr << "InternetConnect failed: " << GetLastError() << std::endl;
        InternetCloseHandle(hInternet);
        return -1;
    }

    // 创建一个 HTTP 请求句柄
    HINTERNET hRequest = HttpOpenRequest(hConnect, L"GET", L"/index.html", NULL, NULL, NULL, 0, 0);
    if (!hRequest) {
        std::cerr << "HttpOpenRequest failed: " << GetLastError() << std::endl;
        InternetCloseHandle(hConnect);
        InternetCloseHandle(hInternet);
        return -1;
    }

    // 发送 HTTP 请求
    if (!HttpSendRequest(hRequest, NULL, 0, NULL, 0)) {
        std::cerr << "HttpSendRequest failed: " << GetLastError() << std::endl;
        InternetCloseHandle(hRequest);
        InternetCloseHandle(hConnect);
        InternetCloseHandle(hInternet);
        return -1;
    }

    // 读取服务器响应的数据
    char buffer[4096];
    DWORD bytesRead;
    while (InternetReadFile(hRequest, buffer, sizeof(buffer) - 1, &bytesRead) && bytesRead) {
        buffer[bytesRead] = '\0'; // 添加字符串终止符
        std::cout << buffer; // 输出响应内容
    }

    // 关闭句柄
    InternetCloseHandle(hRequest);
    InternetCloseHandle(hConnect);
    InternetCloseHandle(hInternet);
    return 0;
}

解析

  1. InternetOpen:初始化使用 wininet 的应用程序。
  2. InternetConnect:连接到指定的服务器。
  3. HttpOpenRequest:创建一个 HTTP 请求句柄。
  4. HttpSendRequest:发送 HTTP 请求。
  5. InternetReadFile:读取服务器响应的数据。
3. HTTPS 客户端实现
示例代码
#include <windows.h>  // 包含 Windows API 函数和常量
#include <wininet.h>  // 包含 Windows Internet 函数
#include <iostream>   // 包含输入输出流
#pragma comment(lib, "wininet.lib")  // 链接 wininet 库

int main() {
    // 初始化 Internet 句柄
    HINTERNET hInternet = InternetOpen(L"MyHTTPSClient", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
    if (!hInternet) {
        std::cerr << "InternetOpen failed: " << GetLastError() << std::endl;
        return -1;
    }

    // 连接到指定的 HTTPS 服务器
    HINTERNET hConnect = InternetConnect(hInternet, L"www.example.com", INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
    if (!hConnect) {
        std::cerr << "InternetConnect failed: " << GetLastError() << std::endl;
        InternetCloseHandle(hInternet);
        return -1;
    }

    // 创建一个 HTTPS 请求句柄
    HINTERNET hRequest = HttpOpenRequest(hConnect, L"GET", L"/index.html", NULL, NULL, NULL, INTERNET_FLAG_SECURE, 0);
    if (!hRequest) {
        std::cerr << "HttpOpenRequest failed: " << GetLastError() << std::endl;
        InternetCloseHandle(hConnect);
        InternetCloseHandle(hInternet);
        return -1;
    }

    // 发送 HTTPS 请求
    if (!HttpSendRequest(hRequest, NULL, 0, NULL, 0)) {
        std::cerr << "HttpSendRequest failed: " << GetLastError() << std::endl;
        InternetCloseHandle(hRequest);
        InternetCloseHandle(hConnect);
        InternetCloseHandle(hInternet);
        return -1;
    }

    // 读取服务器响应的数据
    char buffer[4096];
    DWORD bytesRead;
    while (InternetReadFile(hRequest, buffer, sizeof(buffer) - 1, &bytesRead) && bytesRead) {
        buffer[bytesRead] = '\0'; // 添加字符串终止符
        std::cout << buffer; // 输出响应内容
    }

    // 关闭句柄
    InternetCloseHandle(hRequest);
    InternetCloseHandle(hConnect);
    InternetCloseHandle(hInternet);
    return 0;
}

解析: 与 HTTP 客户端的实现类似,主要区别在于:

  • InternetConnect 中使用 INTERNET_DEFAULT_HTTPS_PORT
  • HttpOpenRequest 中使用 INTERNET_FLAG_SECURE 标志。
4. HTTPS 服务器实现

基于 Windows 的 C++ 实现 HTTPS 服务器相对复杂,通常建议使用开源库(如 OpenSSL)来处理 SSL/TLS 加密。以下是一个简单的 HTTPS 服务器示例,使用 OpenSSL 进行 SSL/TLS 加密。

示例代码
#include <iostream>     // 包含输入输出流
#include <openssl/ssl.h> // 包含 OpenSSL 函数和常量
#include <openssl/err.h> // 包含 OpenSSL 错误处理函数
#include <unistd.h>     // 包含 POSIX 操作系统 API
#include <netinet/in.h> // 包含网络库
#include <arpa/inet.h>  // 包含网络库

#define PORT 8080
#define CERT_FILE "server.crt" // 证书文件路径
#define KEY_FILE "server.key"  // 密钥文件路径

// 初始化 OpenSSL 库
void init_openssl() {
    SSL_load_error_strings(); // 加载错误字符串
    OpenSSL_add_ssl_algorithms(); // 加载加密算法
}

// 清理 OpenSSL 库
void cleanup_openssl() {
    EVP_cleanup(); // 清理加密算法
}

// 创建 SSL 上下文
SSL_CTX* create_context() {
    const SSL_METHOD *method;
    SSL_CTX *ctx;

    method = SSLv23_server_method(); // 创建服务器方法

    ctx = SSL_CTX_new(method); // 创建新 SSL 上下文
    if (!ctx) {
        perror("Unable to create SSL context");
        ERR_print_errors_fp(stderr);
        exit(EXIT_FAILURE);
    }

    return ctx;
}

// 配置 SSL 上下文,加载证书和私钥
void configure_context(SSL_CTX *ctx) {
    SSL_CTX_use_certificate_file(ctx, CERT_FILE, SSL_FILETYPE_PEM); // 加载证书文件
    SSL_CTX_use_PrivateKey_file(ctx, KEY_FILE, SSL_FILETYPE_PEM); // 加载私钥文件
}

int main() {
    int sockfd;
    struct sockaddr_in addr;

    init_openssl(); // 初始化 OpenSSL
    SSL_CTX *ctx = create_context(); // 创建 SSL 上下文
    configure_context(ctx); // 配置 SSL 上下文

    sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建套接字
    if (sockfd < 0) {
        perror("Unable to create socket");
        exit(EXIT_FAILURE);
    }

    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { // 绑定套接字
        perror("Unable to bind");
        exit(EXIT_FAILURE);
    }

    if (listen(sockfd, 1) < 0) { // 监听连接
        perror("Unable to listen");
        exit(EXIT_FAILURE);
    }

    while (1) {
        struct sockaddr_in addr;
        uint len = sizeof(addr);
        SSL *ssl;

        int client = accept(sockfd, (struct sockaddr*)&addr, &len); // 接受客户端连接
        if (client < 0) {
            perror("Unable to accept");
            exit(EXIT_FAILURE);
        }

        ssl = SSL_new(ctx); // 创建新 SSL 结构
        SSL_set_fd(ssl, client); // 关联 SSL 结构和客户端套接字

        if (SSL_accept(ssl) <= 0) { // SSL 握手
            ERR_print_errors_fp(stderr);
        } else {
            const char reply[] = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nHello, HTTPS!";
            SSL_write(ssl, reply, strlen(reply)); // 发送响应
        }

        SSL_shutdown(ssl); // 关闭 SSL 连接
        SSL_free(ssl); // 释放 SSL 结构
        close(client); // 关闭客户端套接字
    }

    close(sockfd); // 关闭服务器套接字
    SSL_CTX_free(ctx); // 释放 SSL 上下文
    cleanup_openssl(); // 清理 OpenSSL
    return 0;
}

解析

  1. init_openssl:初始化 OpenSSL 库。
  2. create_context:创建 SSL 上下文。
  3. configure_context:配置 SSL 上下文,加载证书和私钥。
  4. socketbindlisten:创建、绑定和监听套接字。
  5. accept:接受客户端连接,使用 SSL 处理加密通信。
五、总结

        本文深入解析了 HTTP 和 HTTPS 的基础概念、工作原理及其在 Windows 系统下使用 C++ 进行实现的方法。通过详细的示例代码和解析,展示了如何使用 Windows API 和 OpenSSL 库实现 HTTP 和 HTTPS 客户端及服务器。希望本文能帮助读者更好地理解和掌握 HTTP、HTTPS 及其在 C++ 中的实现,提高网络编程技能。

标签:HTTP,请求,C++,SSL,学懂,HTTPS,服务器,客户端
From: https://blog.csdn.net/martian665/article/details/141355512

相关文章

  • 一款可以自动续签证书的神器——HTTPSOK,告别SSL证书续展的烦恼。
    嗨咯,大家好,我叫无敌大香肠叔叔,为啥去这个名字呢,因为那会戳代码的时候,最爱吃粉肠,所以就取了这个名字了,好了,不废话了,我们回归正题。​问题一:什么是HTTPSOK?​官方的解释是:一款便捷的HTTPS证书自动续签、监控工具,专为NGINX设计。​问题二:与传统部署方式对比​我的回答是......
  • 2024年全国青少年信息素养大赛国赛PYTHON组(C++做法)
    目录前提第一题第二题第三题第四题第五题:第六题前提鄙人是C++学生,所以将PYTHON题做为C++题,还请各位多多海涵!!!部分芝士来自度娘和其它网站温馨提示:题目顺序可能不同,请各位仔细浏览! 第一题题目描述蓝蓝最近学到了一些单词,比如orange(橘子),apple(苹果),pear(梨)。......
  • 【C++篇】迈入新世界的大门——初识C++(下篇)
    文章目录前言引用引用的概念和定义引用的特性引用的使用const引用指针和引用的关系inline#define定义宏inlinenullptr前言接上篇:【C++篇】迈入新世界的大门——初识C++(上篇)引用引用的概念和定义引⽤不是新定义⼀个变量,⽽是给已存在变量取了⼀个别名,编译器不会......
  • C++类型推导
    C++11引入了auto和decltype关键字实现类型推导,通过这两个关键字不仅能方便地获取复杂的类型,还能简化书写,提高编码效率。1.auto类型推导1.1auto关键字的新意义在go语言中,在方法范围中声明的变量可以具有类型推导,例如:vari=10;//在go中自动类型推导变量i为int型而C++11......
  • C++第十一弹 -- STL之List的剖析与使用
    文章索引前言1.list的介绍2list的使用2.1list的构造函数2.2iterator的使用2.3listcapacity2.4listelementaccess2.5listmodifiers3.list的迭代器失效4.list与vector的对比总结前言本篇我们旨在探讨对于STL中list的使用,下一篇我们将会对list进行底层......
  • Oracle RAC 集群启动顺序 转发:https://www.modb.pro/db/1824295923545612288?utm_s
    前言前几天使用脚本在RockyLinux9.4安装Oracle11GR2RAC,安装完之后发现集群无法正常启动,后经过分析发现原来是因为RHEL9版本默认安装移除了 initscripts 软件包,需要人为手动安装,在RHEL8之前是默认安装的。在分析问题的过程中,顺便对OracleRAC集群启动顺序进行了更......
  • Node.js 的 http模块
    http模块http模块是一个内置模块,它提供了HTTP服务器的功能。使用这个模块,你可以创建一个HTTP服务器来响应客户端的请求,http模块是Node.js中进行HTTP通信的基础,无论是作为服务器还是客户端。常用的服务器软件还有:Apache,IIS,Nginx,Tomcathttp模块的一些基本用法创建服......
  • postgresql 定时收集表和索引统计信息 转发:https://blog.csdn.net/weixin_33711641/a
    --由于pg中表和索引的信息收集都是基于时间点的,对于以往的信息无法与现在的信息进行对比,故写下此工具进行统计信息收集--创建数据信息的schemacreateschemadb_stat;--创建收集信息的基础表createtabledb_stat.snapshot_pg_stat_all_indexes(relidint,indexrelidint,scheman......
  • C++面试基础系列-volatile
    系列文章目录文章目录系列文章目录C++面试基础系列-volatile1.volatile核心规则2.C与C++中volatile区别2.1.C语言中的volatile2.2.C++中的volatile2.3.原子性和顺序2.4.易失性2.5.优化2.6.使用场景2.7.C++特有的特性2.8.C++20引入的变化(如果有)3.volatile常见面试问题4......
  • ArchLinux配置OpenCV C++环境
    本文将简单介绍在ArchLinux中安装OpenCVC++库并运行一个简单的OpenCV程序的过程。参考:https://github.com/donaldssh/Install-OpenCV我的环境最新的ArchLinuxKDEPlasma6桌面环境OpenCV4.10.0clang18.1.8gcc14.2.1安装安装以下包:sudopacman-Shdf5vtk......