Appearance
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.01.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 编程包括:
HTTP 协议:
- HTTP 请求:请求行、请求头、请求体
- HTTP 响应:状态行、响应头、响应体
创建简单的 HTTP 服务器:
- 使用 socket 创建服务器
- 监听连接、接受连接、读取请求、发送响应
创建 HTTP 客户端:
- 使用 socket 创建客户端
- 连接服务器、发送请求、读取响应
CGI 编程:
- CGI(Common Gateway Interface)是一种标准
- 用于 Web 服务器和外部程序之间的通信
使用第三方库:
- libcurl:用于处理 HTTP 请求
- Boost.Asio:用于网络编程
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 应用。