Skip to content

C++ Web 编程

C++ 可以用于 Web 编程,包括创建 Web 服务器、客户端和 CGI 程序。虽然 C++ 不是 Web 开发的首选语言,但它可以用于高性能的 Web 应用。

1. HTTP 协议

HTTP(HyperText Transfer Protocol)是 Web 应用使用的协议。

1.1 HTTP 请求

HTTP 请求由请求行、请求头和请求体组成。

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

1.2 HTTP 响应

HTTP 响应由状态行、响应头和响应体组成。

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

<html>
<body>
Hello, World!
</body>
</html>

2. 创建简单的 HTTP 服务器

2.1 使用 socket 创建服务器

cpp
#include <iostream>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

int main() {
    int server_fd, client_fd;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[1024] = {0};
    
    // 创建 socket
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        std::cerr << "socket 创建失败" << std::endl;
        return 1;
    }
    
    // 设置 socket 选项
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        std::cerr << "setsockopt 失败" << std::endl;
        return 1;
    }
    
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(8080);
    
    // 绑定 socket
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        std::cerr << "bind 失败" << std::endl;
        return 1;
    }
    
    // 监听连接
    if (listen(server_fd, 3) < 0) {
        std::cerr << "listen 失败" << std::endl;
        return 1;
    }
    
    std::cout << "服务器运行在 http://localhost:8080" << std::endl;
    
    // 接受连接
    if ((client_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
        std::cerr << "accept 失败" << std::endl;
        return 1;
    }
    
    // 读取请求
    read(client_fd, buffer, 1024);
    std::cout << "收到请求:" << std::endl;
    std::cout << buffer << std::endl;
    
    // 发送响应
    const char* response = "HTTP/1.1 200 OK\nContent-Type: text/html\n\n<html><body>Hello, World!</body></html>";
    write(client_fd, response, strlen(response));
    
    // 关闭连接
    close(client_fd);
    close(server_fd);
    
    return 0;
}

3. 创建 HTTP 客户端

3.1 使用 socket 创建客户端

cpp
#include <iostream>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>

int main() {
    int sock = 0;
    struct sockaddr_in serv_addr;
    char buffer[1024] = {0};
    
    // 创建 socket
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        std::cerr << "socket 创建失败" << std::endl;
        return 1;
    }
    
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(80);
    
    // 解析主机名
    struct hostent* host = gethostbyname("www.example.com");
    if (host == nullptr) {
        std::cerr << "无法解析主机名" << std::endl;
        return 1;
    }
    
    memcpy(&serv_addr.sin_addr, host->h_addr, host->h_length);
    
    // 连接服务器
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        std::cerr << "连接失败" << std::endl;
        return 1;
    }
    
    // 发送请求
    const char* request = "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n";
    send(sock, request, strlen(request), 0);
    
    // 读取响应
    read(sock, buffer, 1024);
    std::cout << "收到响应:" << std::endl;
    std::cout << buffer << std::endl;
    
    // 关闭连接
    close(sock);
    
    return 0;
}

4. CGI 编程

CGI(Common Gateway Interface)是一种标准,用于 Web 服务器和外部程序之间的通信。

4.1 简单的 CGI 程序

cpp
#include <iostream>

int main() {
    std::cout << "Content-type: text/html\n\n";
    std::cout << "<html><body>";
    std::cout << "<h1>Hello, CGI!</h1>";
    std::cout << "</body></html>";
    
    return 0;
}

4.2 处理表单数据

cpp
#include <iostream>
#include <cstdlib>
#include <string>

int main() {
    std::cout << "Content-type: text/html\n\n";
    std::cout << "<html><body>";
    
    const char* query_string = getenv("QUERY_STRING");
    if (query_string != nullptr) {
        std::cout << "<h1>查询字符串: " << query_string << "</h1>";
    } else {
        std::cout << "<h1>没有查询字符串</h1>";
    }
    
    std::cout << "</body></html>";
    
    return 0;
}

5. 使用第三方库

5.1 使用 libcurl

libcurl 是一个流行的 C/C++ 库,用于处理 HTTP 请求。

cpp
#include <iostream>
#include <curl/curl.h>

size_t write_callback(void* contents, size_t size, size_t nmemb, void* userp) {
    ((std::string*)userp)->append((char*)contents, size * nmemb);
    return size * nmemb;
}

int main() {
    CURL* curl;
    CURLcode res;
    std::string read_buffer;
    
    curl = curl_easy_init();
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "http://www.example.com");
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &read_buffer);
        
        res = curl_easy_perform(curl);
        
        if (res != CURLE_OK) {
            std::cerr << "curl_easy_perform 失败: " << curl_easy_strerror(res) << std::endl;
        } else {
            std::cout << "收到响应:" << std::endl;
            std::cout << read_buffer << std::endl;
        }
        
        curl_easy_cleanup(curl);
    }
    
    return 0;
}

5.2 使用 Boost.Asio

Boost.Asio 是一个跨平台的 C++ 库,用于网络编程。

cpp
#include <iostream>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

int main() {
    try {
        boost::asio::io_context io_context;
        
        tcp::resolver resolver(io_context);
        tcp::resolver::results_type endpoints = resolver.resolve("www.example.com", "http");
        
        tcp::socket socket(io_context);
        boost::asio::connect(socket, endpoints);
        
        boost::asio::streambuf request;
        std::ostream request_stream(&request);
        request_stream << "GET / HTTP/1.1\r\n";
        request_stream << "Host: www.example.com\r\n";
        request_stream << "Connection: close\r\n\r\n";
        
        boost::asio::write(socket, request);
        
        boost::asio::streambuf response;
        boost::asio::read_until(socket, response, "\r\n");
        
        std::istream response_stream(&response);
        std::string http_version;
        response_stream >> http_version;
        
        unsigned int status_code;
        response_stream >> status_code;
        
        std::string status_message;
        std::getline(response_stream, status_message);
        
        std::cout << "HTTP 版本: " << http_version << std::endl;
        std::cout << "状态码: " << status_code << std::endl;
        std::cout << "状态消息: " << status_message << std::endl;
        
    } catch (std::exception& e) {
        std::cerr << "异常: " << e.what() << std::endl;
    }
    
    return 0;
}

6. JSON 处理

JSON 是 Web 应用中常用的数据格式。

6.1 使用 nlohmann/json 库

cpp
#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

int main() {
    // 创建 JSON 对象
    json j;
    j["name"] = "张三";
    j["age"] = 20;
    j["city"] = "北京";
    
    // 转换为字符串
    std::string json_str = j.dump();
    std::cout << "JSON 字符串: " << json_str << std::endl;
    
    // 解析 JSON 字符串
    json j2 = json::parse(json_str);
    std::cout << "姓名: " << j2["name"] << std::endl;
    std::cout << "年龄: " << j2["age"] << std::endl;
    std::cout << "城市: " << j2["city"] << std::endl;
    
    return 0;
}

7. 示例:综合运用

现在,让我们看一个综合运用各种 Web 编程特性的例子:

cpp
#include <iostream>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

std::string generate_response(const std::string& path) {
    std::string content;
    
    if (path == "/") {
        content = "<html><body><h1>欢迎来到主页</h1></body></html>";
    } else if (path == "/about") {
        content = "<html><body><h1>关于我们</h1></body></html>";
    } else if (path == "/contact") {
        content = "<html><body><h1>联系我们</h1></body></html>";
    } else {
        content = "<html><body><h1>404 Not Found</h1></body></html>";
    }
    
    std::string response = "HTTP/1.1 200 OK\n";
    response += "Content-Type: text/html\n";
    response += "Content-Length: " + std::to_string(content.length()) + "\n\n";
    response += content;
    
    return response;
}

int main() {
    int server_fd, client_fd;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[1024] = {0};
    
    // 创建 socket
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        std::cerr << "socket 创建失败" << std::endl;
        return 1;
    }
    
    // 设置 socket 选项
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        std::cerr << "setsockopt 失败" << std::endl;
        return 1;
    }
    
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(8080);
    
    // 绑定 socket
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        std::cerr << "bind 失败" << std::endl;
        return 1;
    }
    
    // 监听连接
    if (listen(server_fd, 3) < 0) {
        std::cerr << "listen 失败" << std::endl;
        return 1;
    }
    
    std::cout << "服务器运行在 http://localhost:8080" << std::endl;
    
    while (true) {
        // 接受连接
        if ((client_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
            std::cerr << "accept 失败" << std::endl;
            continue;
        }
        
        // 读取请求
        read(client_fd, buffer, 1024);
        std::cout << "收到请求:" << std::endl;
        std::cout << buffer << std::endl;
        
        // 解析请求路径
        std::string request(buffer);
        size_t pos = request.find(" ");
        if (pos != std::string::npos) {
            size_t end_pos = request.find(" ", pos + 1);
            if (end_pos != std::string::npos) {
                std::string path = request.substr(pos + 1, end_pos - pos - 1);
                std::cout << "请求路径: " << path << std::endl;
                
                // 生成响应
                std::string response = generate_response(path);
                write(client_fd, response.c_str(), response.length());
            }
        }
        
        // 关闭连接
        close(client_fd);
    }
    
    close(server_fd);
    
    return 0;
}

小结

C++ Web 编程包括:

  1. HTTP 协议

    • HTTP 请求:请求行、请求头、请求体
    • HTTP 响应:状态行、响应头、响应体
  2. 创建简单的 HTTP 服务器

    • 使用 socket 创建服务器
    • 监听连接、接受连接、读取请求、发送响应
  3. 创建 HTTP 客户端

    • 使用 socket 创建客户端
    • 连接服务器、发送请求、读取响应
  4. CGI 编程

    • CGI(Common Gateway Interface)是一种标准
    • 用于 Web 服务器和外部程序之间的通信
  5. 使用第三方库

    • libcurl:用于处理 HTTP 请求
    • Boost.Asio:用于网络编程
  6. JSON 处理

    • JSON 是 Web 应用中常用的数据格式
    • 使用 nlohmann/json 库处理 JSON

关键概念:

  • HTTP 协议:Web 应用使用的协议
  • HTTP 请求:请求行、请求头、请求体
  • HTTP 响应:状态行、响应头、响应体
  • socket:用于网络编程的接口
  • CGI:Common Gateway Interface
  • libcurl:用于处理 HTTP 请求的库
  • Boost.Asio:用于网络编程的库
  • JSON:Web 应用中常用的数据格式

掌握 Web 编程是编写网络应用的重要基础,C++ 虽然不是 Web 开发的首选语言,但它可以用于高性能的 Web 应用。