|
|
#include "httpserver.hpp"
|
|
|
#include <iostream>
|
|
|
#include <sstream>
|
|
|
#include <cstring>
|
|
|
#include <algorithm>
|
|
|
#include <memory>
|
|
|
#include <sys/socket.h>
|
|
|
#include <netinet/in.h>
|
|
|
#include <unistd.h>
|
|
|
#include <arpa/inet.h>
|
|
|
#include <signal.h>
|
|
|
#include <fcntl.h>
|
|
|
#include <ctime>
|
|
|
|
|
|
// ==================== 工具函数实现 ====================
|
|
|
|
|
|
std::string HTTPServer::urlDecode(const std::string &encoded)
|
|
|
{
|
|
|
std::string decoded;
|
|
|
for (size_t i = 0; i < encoded.length(); ++i)
|
|
|
{
|
|
|
if (encoded[i] == '%' && i + 2 < encoded.length())
|
|
|
{
|
|
|
int hex;
|
|
|
std::istringstream hexStream(encoded.substr(i + 1, 2));
|
|
|
if (hexStream >> std::hex >> hex)
|
|
|
{
|
|
|
decoded += static_cast<char>(hex);
|
|
|
i += 2;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
decoded += encoded[i];
|
|
|
}
|
|
|
}
|
|
|
else if (encoded[i] == '+')
|
|
|
{
|
|
|
decoded += ' ';
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
decoded += encoded[i];
|
|
|
}
|
|
|
}
|
|
|
return decoded;
|
|
|
}
|
|
|
|
|
|
std::map<std::string, std::string> HTTPServer::parseQueryString(const std::string &query)
|
|
|
{
|
|
|
std::map<std::string, std::string> params;
|
|
|
std::istringstream stream(query);
|
|
|
std::string pair;
|
|
|
|
|
|
while (std::getline(stream, pair, '&'))
|
|
|
{
|
|
|
size_t equalsPos = pair.find('=');
|
|
|
if (equalsPos != std::string::npos)
|
|
|
{
|
|
|
std::string key = urlDecode(pair.substr(0, equalsPos));
|
|
|
std::string value = urlDecode(pair.substr(equalsPos + 1));
|
|
|
params[key] = value;
|
|
|
}
|
|
|
else if (!pair.empty())
|
|
|
{
|
|
|
params[urlDecode(pair)] = "";
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return params;
|
|
|
}
|
|
|
|
|
|
std::map<std::string, std::string> HTTPServer::parseHeaders(const std::string &headers)
|
|
|
{
|
|
|
std::map<std::string, std::string> header_map;
|
|
|
std::istringstream stream(headers);
|
|
|
std::string line;
|
|
|
|
|
|
while (std::getline(stream, line))
|
|
|
{
|
|
|
if (line.empty() || line == "\r")
|
|
|
continue;
|
|
|
|
|
|
// 移除可能的回车符
|
|
|
if (!line.empty() && line.back() == '\r')
|
|
|
{
|
|
|
line.pop_back();
|
|
|
}
|
|
|
|
|
|
size_t colonPos = line.find(':');
|
|
|
if (colonPos != std::string::npos)
|
|
|
{
|
|
|
std::string key = line.substr(0, colonPos);
|
|
|
// 跳过冒号和空格
|
|
|
size_t value_start = colonPos + 1;
|
|
|
while (value_start < line.length() && line[value_start] == ' ')
|
|
|
{
|
|
|
value_start++;
|
|
|
}
|
|
|
std::string value = line.substr(value_start);
|
|
|
header_map[key] = value;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return header_map;
|
|
|
}
|
|
|
|
|
|
std::string HTTPServer::getContentType(const std::map<std::string, std::string> &headers)
|
|
|
{
|
|
|
auto it = headers.find("Content-Type");
|
|
|
if (it != headers.end())
|
|
|
{
|
|
|
return it->second;
|
|
|
}
|
|
|
return "";
|
|
|
}
|
|
|
|
|
|
int HTTPServer::getContentLength(const std::map<std::string, std::string> &headers)
|
|
|
{
|
|
|
auto it = headers.find("Content-Length");
|
|
|
if (it != headers.end())
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
return std::stoi(it->second);
|
|
|
}
|
|
|
catch (const std::exception &e)
|
|
|
{
|
|
|
std::cerr << "解析Content-Length错误: " << e.what() << std::endl;
|
|
|
return 0;
|
|
|
}
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
void HTTPServer::sendResponse(int client_socket, const std::string &response,
|
|
|
const std::string &content_type)
|
|
|
{
|
|
|
std::string httpResponse =
|
|
|
"HTTP/1.1 200 OK\r\n"
|
|
|
"Content-Type: " +
|
|
|
content_type + "\r\n"
|
|
|
"Content-Length: " +
|
|
|
std::to_string(response.length()) + "\r\n"
|
|
|
"Connection: close\r\n"
|
|
|
"\r\n" +
|
|
|
response;
|
|
|
|
|
|
send(client_socket, httpResponse.c_str(), httpResponse.length(), 0);
|
|
|
}
|
|
|
|
|
|
void HTTPServer::sendError(int client_socket, int code, const std::string &message)
|
|
|
{
|
|
|
std::string response = "<html><body><h1>" + std::to_string(code) + " " + message + "</h1></body></html>";
|
|
|
std::string httpResponse =
|
|
|
"HTTP/1.1 " + std::to_string(code) + " " + message + "\r\n"
|
|
|
"Content-Type: text/html; charset=utf-8\r\n"
|
|
|
"Content-Length: " +
|
|
|
std::to_string(response.length()) + "\r\n"
|
|
|
"Connection: close\r\n"
|
|
|
"\r\n" +
|
|
|
response;
|
|
|
|
|
|
send(client_socket, httpResponse.c_str(), httpResponse.length(), 0);
|
|
|
}
|
|
|
|
|
|
void HTTPServer::sendJSONResponse(int client_socket, const std::string &json)
|
|
|
{
|
|
|
sendResponse(client_socket, json, "application/json");
|
|
|
}
|
|
|
|
|
|
// ==================== 私有方法实现 ====================
|
|
|
|
|
|
std::string HTTPServer::readFullRequest(int client_socket, int timeout_ms)
|
|
|
{
|
|
|
std::string request;
|
|
|
char buffer[4096];
|
|
|
fd_set read_fds;
|
|
|
struct timeval timeout;
|
|
|
|
|
|
// 设置非阻塞模式
|
|
|
int flags = fcntl(client_socket, F_GETFL, 0);
|
|
|
fcntl(client_socket, F_SETFL, flags | O_NONBLOCK);
|
|
|
|
|
|
// 设置超时
|
|
|
timeout.tv_sec = timeout_ms / 1000;
|
|
|
timeout.tv_usec = (timeout_ms % 1000) * 1000;
|
|
|
|
|
|
while (true) {
|
|
|
FD_ZERO(&read_fds);
|
|
|
FD_SET(client_socket, &read_fds);
|
|
|
|
|
|
int activity = select(client_socket + 1, &read_fds, nullptr, nullptr, &timeout);
|
|
|
|
|
|
if (activity < 0) {
|
|
|
std::cerr << "select error" << std::endl;
|
|
|
break;
|
|
|
} else if (activity == 0) {
|
|
|
// 超时,检查是否已经有数据
|
|
|
if (!request.empty()) {
|
|
|
break;
|
|
|
}
|
|
|
std::cerr << "read timeout" << std::endl;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
if (FD_ISSET(client_socket, &read_fds)) {
|
|
|
ssize_t bytes_read = recv(client_socket, buffer, sizeof(buffer) - 1, 0);
|
|
|
|
|
|
if (bytes_read > 0) {
|
|
|
buffer[bytes_read] = '\0';
|
|
|
request.append(buffer, bytes_read);
|
|
|
|
|
|
// 检查是否已经收到完整的请求
|
|
|
if (isRequestComplete(request)) {
|
|
|
break;
|
|
|
}
|
|
|
} else if (bytes_read == 0) {
|
|
|
// 连接关闭
|
|
|
break;
|
|
|
} else {
|
|
|
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
|
|
std::cerr << "recv error: " << strerror(errno) << std::endl;
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 恢复阻塞模式
|
|
|
fcntl(client_socket, F_SETFL, flags);
|
|
|
|
|
|
return request;
|
|
|
}
|
|
|
|
|
|
bool HTTPServer::isRequestComplete(const std::string& request)
|
|
|
{
|
|
|
// 查找请求头和请求体的分隔符
|
|
|
size_t header_end = request.find("\r\n\r\n");
|
|
|
if (header_end == std::string::npos) {
|
|
|
return false; // 没有找到完整头部
|
|
|
}
|
|
|
|
|
|
// 提取头部部分
|
|
|
std::string headers_part = request.substr(0, header_end);
|
|
|
|
|
|
// 解析Content-Length
|
|
|
int content_length = getContentLength(parseHeaders(headers_part));
|
|
|
|
|
|
// 如果有Content-Length,检查是否收到完整body
|
|
|
if (content_length > 0) {
|
|
|
size_t body_start = header_end + 4;
|
|
|
if (request.length() >= body_start + content_length) {
|
|
|
return true;
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 如果没有Content-Length,检查是否以空行结尾(表示没有body)
|
|
|
if (request.length() >= header_end + 4 && request.substr(header_end + 4).empty()) {
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
// 对于没有Content-Length的GET请求,有分隔符就认为完整
|
|
|
if (request.find("GET") == 0 || request.find("HEAD") == 0) {
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
void HTTPServer::logRequest(const std::string& method, const std::string& client_ip,
|
|
|
const std::map<std::string, std::string>& headers)
|
|
|
{
|
|
|
// 获取当前时间
|
|
|
std::time_t now = std::time(nullptr);
|
|
|
char time_str[100];
|
|
|
std::strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", std::localtime(&now));
|
|
|
|
|
|
std::cout << "[" << time_str << "] " << client_ip << " " << method;
|
|
|
|
|
|
auto user_agent = headers.find("User-Agent");
|
|
|
if (user_agent != headers.end()) {
|
|
|
std::cout << " (" << user_agent->second.substr(0, 50) << ")";
|
|
|
}
|
|
|
std::cout << std::endl;
|
|
|
}
|
|
|
|
|
|
void HTTPServer::handleConnection(int client_socket, const std::string& client_ip)
|
|
|
{
|
|
|
// 读取完整的请求
|
|
|
std::string request = readFullRequest(client_socket);
|
|
|
|
|
|
if (!request.empty()) {
|
|
|
handleRequest(client_socket, request);
|
|
|
} else {
|
|
|
std::cerr << "收到空请求或读取失败" << std::endl;
|
|
|
sendError(client_socket, 400, "Bad Request");
|
|
|
}
|
|
|
|
|
|
close(client_socket);
|
|
|
}
|
|
|
|
|
|
void HTTPServer::handleRequest(int client_socket, const std::string& request)
|
|
|
{
|
|
|
// 提取请求行
|
|
|
size_t line_end = request.find("\r\n");
|
|
|
if (line_end == std::string::npos) {
|
|
|
sendError(client_socket, 400, "Bad Request");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
std::string request_line = request.substr(0, line_end);
|
|
|
std::istringstream iss(request_line);
|
|
|
std::string method, path, protocol;
|
|
|
iss >> method >> path >> protocol;
|
|
|
|
|
|
// 提取头部
|
|
|
std::string headers_str;
|
|
|
size_t headers_end = request.find("\r\n\r\n");
|
|
|
if (headers_end != std::string::npos) {
|
|
|
headers_str = request.substr(line_end + 2, headers_end - line_end - 2);
|
|
|
}
|
|
|
|
|
|
auto headers = parseHeaders(headers_str);
|
|
|
|
|
|
for (const auto& header : headers) {
|
|
|
std::cout << header.first << ": " << header.second << std::endl;
|
|
|
}
|
|
|
|
|
|
// 记录请求日志
|
|
|
logRequest(method, "", headers);
|
|
|
|
|
|
// 处理请求
|
|
|
if (method == "POST") {
|
|
|
handlePOST(client_socket, request, headers);
|
|
|
} else if (method == "GET") {
|
|
|
handleGET(client_socket, request_line);
|
|
|
} else {
|
|
|
sendError(client_socket, 405, "Method Not Allowed");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void HTTPServer::handlePOST(int client_socket, const std::string& request,
|
|
|
const std::map<std::string, std::string>& headers)
|
|
|
{
|
|
|
std::cout << "\n=== 收到POST请求 ===\n";
|
|
|
|
|
|
// 获取请求体
|
|
|
int contentLength = getContentLength(headers);
|
|
|
std::string body;
|
|
|
|
|
|
if (contentLength > 0) {
|
|
|
size_t bodyPos = request.find("\r\n\r\n");
|
|
|
if (bodyPos != std::string::npos) {
|
|
|
bodyPos += 4; // 跳过空行
|
|
|
if (bodyPos < request.length()) {
|
|
|
body = request.substr(bodyPos);
|
|
|
// 确保body长度不超过Content-Length
|
|
|
if (body.length() > static_cast<size_t>(contentLength)) {
|
|
|
body = body.substr(0, contentLength);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
std::cout << "请求体长度: " << body.length() << " 字节" << std::endl;
|
|
|
|
|
|
// 如果有自定义处理器,使用它
|
|
|
if (custom_handler) {
|
|
|
custom_handler(client_socket, request, headers, body);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 默认处理逻辑
|
|
|
std::string contentType = getContentType(headers);
|
|
|
std::string response;
|
|
|
|
|
|
if (contentType.find("application/x-www-form-urlencoded") != std::string::npos) {
|
|
|
// 表单数据
|
|
|
auto params = parseQueryString(body);
|
|
|
|
|
|
response = "<html><body><h1>POST数据已接收</h1>";
|
|
|
response += "<h2>表单数据:</h2>";
|
|
|
|
|
|
if (params.empty()) {
|
|
|
response += "<p>没有表单数据</p>";
|
|
|
response += "<p>原始body: " + body + "</p>";
|
|
|
} else {
|
|
|
response += "<ul>";
|
|
|
for (const auto& param : params) {
|
|
|
response += "<li><strong>" + param.first + ":</strong> " + param.second + "</li>";
|
|
|
}
|
|
|
response += "</ul>";
|
|
|
}
|
|
|
response += "</body></html>";
|
|
|
|
|
|
// 打印到控制台
|
|
|
std::cout << "\n解析的表单数据:\n";
|
|
|
for (const auto& param : params) {
|
|
|
std::cout << param.first << " = " << param.second << std::endl;
|
|
|
}
|
|
|
|
|
|
} else if (contentType.find("application/Json") != std::string::npos) {
|
|
|
// JSON数据
|
|
|
this->PostMsg = body;
|
|
|
response = "<html><body><h1>JSON数据已接收</h1>";
|
|
|
response += "<h2>原始JSON:</h2>";
|
|
|
response += "<pre>" + body + "</pre>";
|
|
|
response += "</body></html>";
|
|
|
|
|
|
std::cout << "\nJSON数据:\n" << body << std::endl;
|
|
|
|
|
|
} else {
|
|
|
// 其他类型
|
|
|
response = "<html><body><h1>数据已接收</h1>";
|
|
|
response += "<p>Content-Type: " + (contentType.empty() ? "未指定" : contentType) + "</p>";
|
|
|
response += "<p>数据长度: " + std::to_string(body.length()) + " 字节</p>";
|
|
|
|
|
|
if (!body.empty() && body.length() < 1000) {
|
|
|
response += "<h2>原始数据:</h2>";
|
|
|
response += "<pre>" + body + "</pre>";
|
|
|
} else if (!body.empty()) {
|
|
|
response += "<h2>原始数据(前1000字节):</h2>";
|
|
|
response += "<pre>" + body.substr(0, 1000) + "...</pre>";
|
|
|
}
|
|
|
response += "</body></html>";
|
|
|
|
|
|
std::cout << "\n原始数据:\n" << body.substr(0, std::min(body.length(), (size_t)500)) << std::endl;
|
|
|
if (body.length() > 500) {
|
|
|
std::cout << "... (只显示前500字节)" << std::endl;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
sendResponse(client_socket, response);
|
|
|
std::cout << "\n=== 处理完成 ===\n\n";
|
|
|
}
|
|
|
|
|
|
void HTTPServer::handleGET(int client_socket, const std::string& request_line)
|
|
|
{
|
|
|
std::istringstream iss(request_line);
|
|
|
std::string method, path, protocol;
|
|
|
iss >> method >> path >> protocol;
|
|
|
|
|
|
// 默认提供测试表单页面
|
|
|
std::string response =
|
|
|
"<html>"
|
|
|
"<head><title>HTTP POST测试服务器</title>"
|
|
|
"<meta charset=\"utf-8\">"
|
|
|
"<style>"
|
|
|
"body { font-family: Arial, sans-serif; margin: 40px; }"
|
|
|
".container { max-width: 800px; margin: 0 auto; }"
|
|
|
"form { margin: 20px 0; padding: 20px; border: 1px solid #ddd; background: #f9f9f9; }"
|
|
|
"h2 { color: #333; border-bottom: 2px solid #007bff; padding-bottom: 10px; }"
|
|
|
"input, textarea, select { width: 100%; margin: 10px 0; padding: 8px; border: 1px solid #ccc; border-radius: 4px; }"
|
|
|
"button { padding: 10px 20px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; }"
|
|
|
"button:hover { background: #0056b3; }"
|
|
|
".log { background: #333; color: #fff; padding: 15px; border-radius: 4px; font-family: monospace; margin-top: 20px; }"
|
|
|
"</style>"
|
|
|
"<script>"
|
|
|
"function submitForm(formId, contentType) {"
|
|
|
" const form = document.getElementById(formId);"
|
|
|
" const formData = new FormData(form);"
|
|
|
" let body;"
|
|
|
" let headers = { 'Content-Type': contentType };"
|
|
|
" "
|
|
|
" if (contentType === 'application/json') {"
|
|
|
" const obj = {};"
|
|
|
" formData.forEach((value, key) => obj[key] = value);"
|
|
|
" body = JSON.stringify(obj);"
|
|
|
" } else if (contentType === 'application/x-www-form-urlencoded') {"
|
|
|
" const params = new URLSearchParams();"
|
|
|
" formData.forEach((value, key) => params.append(key, value));"
|
|
|
" body = params.toString();"
|
|
|
" } else {"
|
|
|
" body = formData;"
|
|
|
" }"
|
|
|
" "
|
|
|
" fetch('/', {"
|
|
|
" method: 'POST',"
|
|
|
" headers: headers,"
|
|
|
" body: contentType === 'multipart/form-data' ? formData : body"
|
|
|
" }).then(res => res.text()).then(html => {"
|
|
|
" document.getElementById('result').innerHTML = html;"
|
|
|
" document.getElementById('result').scrollIntoView();"
|
|
|
" });"
|
|
|
" return false;"
|
|
|
"}"
|
|
|
"</script>"
|
|
|
"</head>"
|
|
|
"<body>"
|
|
|
"<div class='container'>"
|
|
|
"<h1>HTTP POST测试服务器</h1>"
|
|
|
"<p>服务器运行在端口 " + std::to_string(port) + "</p>"
|
|
|
|
|
|
"<h2>1. 表单提交测试 (application/x-www-form-urlencoded)</h2>"
|
|
|
"<form id='form1' onsubmit='return submitForm(\"form1\", \"application/x-www-form-urlencoded\")'>"
|
|
|
"<label>姓名: <input type='text' name='name' value='张三' required></label><br>"
|
|
|
"<label>邮箱: <input type='email' name='email' value='test@example.com' required></label><br>"
|
|
|
"<label>消息: <textarea name='message' rows='4'>这是一个测试消息</textarea></label><br>"
|
|
|
"<button type='submit'>提交表单数据</button>"
|
|
|
"</form>"
|
|
|
|
|
|
"<h2>2. JSON提交测试 (application/json)</h2>"
|
|
|
"<form id='form2' onsubmit='return submitForm(\"form2\", \"application/json\")'>"
|
|
|
"<label>用户名: <input type='text' name='username' value='john_doe'></label><br>"
|
|
|
"<label>分数: <input type='number' name='score' value='95'></label><br>"
|
|
|
"<button type='submit'>提交JSON数据</button>"
|
|
|
"</form>"
|
|
|
|
|
|
"<h2>3. 使用curl测试</h2>"
|
|
|
"<div class='log'>"
|
|
|
"<p>表单数据:</p>"
|
|
|
"<code>curl -X POST http://localhost:" + std::to_string(port) + "/ \\<br>"
|
|
|
" -H \"Content-Type: application/x-www-form-urlencoded\" \\<br>"
|
|
|
" -d \"name=张三&email=test@example.com&message=Hello%20World\"</code><br><br>"
|
|
|
"<p>JSON数据:</p>"
|
|
|
"<code>curl -X POST http://localhost:" + std::to_string(port) + "/ \\<br>"
|
|
|
" -H \"Content-Type: application/json\" \\<br>"
|
|
|
" -d '{\"name\":\"李四\",\"age\":25,\"city\":\"北京\"}'</code>"
|
|
|
"</div>"
|
|
|
|
|
|
"<div id='result'></div>"
|
|
|
"</div>"
|
|
|
"</body></html>";
|
|
|
|
|
|
sendResponse(client_socket, response);
|
|
|
}
|
|
|
|
|
|
// ==================== 公有方法实现 ====================
|
|
|
|
|
|
HTTPServer::HTTPServer(int port) : port(port), server_fd(-1), custom_handler(nullptr) {}
|
|
|
|
|
|
HTTPServer::~HTTPServer() {
|
|
|
stop();
|
|
|
}
|
|
|
|
|
|
bool HTTPServer::start() {
|
|
|
// 创建socket
|
|
|
server_fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
if (server_fd == -1) {
|
|
|
std::cerr << "创建socket失败: " << strerror(errno) << std::endl;
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 设置socket选项,避免地址占用
|
|
|
int opt = 1;
|
|
|
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
|
|
|
std::cerr << "设置socket选项失败: " << strerror(errno) << std::endl;
|
|
|
close(server_fd);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 绑定地址和端口
|
|
|
struct sockaddr_in address;
|
|
|
address.sin_family = AF_INET;
|
|
|
address.sin_addr.s_addr = INADDR_ANY;
|
|
|
address.sin_port = htons(port);
|
|
|
|
|
|
if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {
|
|
|
std::cerr << "绑定端口 " << port << " 失败: " << strerror(errno) << std::endl;
|
|
|
close(server_fd);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 开始监听
|
|
|
if (listen(server_fd, 10) < 0) {
|
|
|
std::cerr << "监听失败: " << strerror(errno) << std::endl;
|
|
|
close(server_fd);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
std::cout << "HTTP服务器启动成功,监听端口: " << port << std::endl;
|
|
|
std::cout << "访问 http://localhost:" << port << " 进行测试" << std::endl;
|
|
|
std::cout << "按 Ctrl+C 停止服务器" << std::endl;
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
void HTTPServer::run()
|
|
|
{
|
|
|
fd_set readfds;
|
|
|
struct timeval timeout;
|
|
|
|
|
|
FD_ZERO(&readfds);
|
|
|
FD_SET(server_fd, &readfds);
|
|
|
|
|
|
// 设置1秒超时
|
|
|
timeout.tv_sec = 1;
|
|
|
timeout.tv_usec = 0;
|
|
|
|
|
|
int activity = select(server_fd + 1, &readfds, NULL, NULL, &timeout);
|
|
|
|
|
|
if (activity < 0)
|
|
|
{
|
|
|
std::cerr << "select error: " << strerror(errno) << std::endl;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (activity == 0)
|
|
|
{
|
|
|
// 超时,检查是否应该停止
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (FD_ISSET(server_fd, &readfds))
|
|
|
{
|
|
|
struct sockaddr_in client_address;
|
|
|
socklen_t client_len = sizeof(client_address);
|
|
|
|
|
|
int client_socket = accept(server_fd, (struct sockaddr *)&client_address, &client_len);
|
|
|
if (client_socket < 0)
|
|
|
{
|
|
|
std::cerr << "接受连接失败: " << strerror(errno) << std::endl;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
char client_ip[INET_ADDRSTRLEN];
|
|
|
inet_ntop(AF_INET, &client_address.sin_addr, client_ip, INET_ADDRSTRLEN);
|
|
|
handleConnection(client_socket, client_ip);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void HTTPServer::stop()
|
|
|
{
|
|
|
if (server_fd != -1)
|
|
|
{
|
|
|
close(server_fd);
|
|
|
server_fd = -1;
|
|
|
std::cout << "服务器已停止" << std::endl;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void HTTPServer::SetPort(int port)
|
|
|
{
|
|
|
this->port = port;
|
|
|
}
|
|
|
|
|
|
void HTTPServer::setRequestHandler(const RequestHandler& handler) {
|
|
|
custom_handler = handler;
|
|
|
} |