头文件
#pragma once
#include <iostream>
#include <sstream>
#include <vector>
#include <functional>
#include <string>
#include <cstring>
#include <winsock2.h>
#include <thread>
#pragma comment(lib, "ws2_32.lib")
class HTTPRequest {
public:
std::string method;
std::string path;
std::string version;
std::vector<std::pair<std::string, std::string>> headers;
std::string body;
};
class HTTPResponse {
public:
int status_code;
std::string status_desc;
std::vector<std::pair<std::string, std::string>> headers;
std::string body;
};
class Route {
public:
std::string method;
std::string path;
std::function<void(const HTTPRequest&, HTTPResponse&)> handler;
Route(const std::string& method_, const std::string& path_, std::function<void(const HTTPRequest&, HTTPResponse&)> handler_)
: method(method_), path(path_), handler(handler_) {}
};
class Router {
public:
void add_route(const std::string& method, const std::string& path, std::function<void(const HTTPRequest&, HTTPResponse&)> handler) {
routes.push_back(Route(method, path, handler));
}
void handle_request(const HTTPRequest& req, HTTPResponse& resp) {
for (const auto& route : routes) {
if (route.method == req.method && route.path == req.path) {
route.handler(req, resp);
return;
}
}
resp.status_code = 404;
resp.status_desc = "Not Found";
resp.body = "404 Not Found";
}
private:
std::vector<Route> routes;
};
class Server {
public:
Server(int port_) : port(port_), router() {}
void run() {
WSADATA wsaData;
int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != 0) {
std::cerr << "WSAStartup failed: " << result << std::endl;
return;
}
SOCKET listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listen_socket == INVALID_SOCKET) {
std::cerr << "socket failed: " << WSAGetLastError() << std::endl;
WSACleanup();
return;
}
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.s_addr = INADDR_ANY;
service.sin_port = htons(port);
result = bind(listen_socket, reinterpret_cast<SOCKADDR*>(&service), sizeof(service));
if (result == SOCKET_ERROR) {
std::cerr << "bind failed: " << WSAGetLastError() << std::endl;
closesocket(listen_socket);
WSACleanup();
return;
}
result = listen(listen_socket, SOMAXCONN);
if (result == SOCKET_ERROR) {
std::cerr << "listen failed: " << WSAGetLastError() << std::endl;
closesocket(listen_socket);
WSACleanup();
return;
}
std::cout << "Listening on port " << port << std::endl;
while (true) {
SOCKET client_socket = accept(listen_socket, nullptr, nullptr);
if (client_socket == INVALID_SOCKET) {
std::cerr << "accept failed: " << WSAGetLastError() << std::endl;
closesocket(listen_socket);
WSACleanup();
return;
}
std::thread t([this, client_socket]() {
char recvbuf[9600];
int recvbuflen = 9600;
int result = recv(client_socket, recvbuf, recvbuflen, 0);
if (result == SOCKET_ERROR) {
std::cerr << "recv failed: " << WSAGetLastError() << std::endl;
closesocket(client_socket);
return;
}
if (result < 9600)
{
recvbuf[result] = '\0';
}
HTTPRequest req;
parse_request(recvbuf, req);
HTTPResponse resp;
router.handle_request(req, resp);
std::stringstream response_stream;
response_stream << "HTTP/1.1 " << resp.status_code << " " << resp.status_desc << "\r\n";
for (const auto& header : resp.headers) {
response_stream << header.first << ": " << header.second << "\r\n";
}
response_stream << "\r\n";
response_stream << resp.body;
std::string response_str = response_stream.str();
result = send(client_socket, response_str.c_str(), response_str.length(), 0);
if (result == SOCKET_ERROR) {
std::cerr << "send failed: " << WSAGetLastError() << std::endl;
closesocket(client_socket);
return;
}
std::cout << req.method << " " << req.path << " - " << resp.status_code << std::endl;
closesocket(client_socket);
});
t.detach();
}
closesocket(listen_socket);
WSACleanup();
}
void add_route(const std::string& method, const std::string& path, std::function<void(const HTTPRequest&, HTTPResponse&)> handler) {
router.add_route(method, path, handler);
}
private:
int port;
Router router;
void parse_request(const char* request_str, HTTPRequest& req) {
std::stringstream request_stream(request_str);
request_stream >> req.method >> req.path >> req.version;
request_stream.ignore(2, '\n');
std::string line;
while (std::getline(request_stream, line) && !line.empty() && line.back() == '\r') {
if (line == "\r")
break;
line.pop_back();
std::size_t colon_pos = line.find(':');
if (colon_pos != std::string::npos) {
std::string header_name = line.substr(0, colon_pos);
std::string header_value = line.substr(colon_pos + 2);
req.headers.push_back(std::make_pair(header_name, header_value));
}
}
std::string body;
char c;
while (request_stream.get(c)) {
if (c == '\r' && request_stream.peek() == '\n' && request_stream.ignore() &&
request_stream.peek() == '\r' && request_stream.ignore()) {
std::getline(request_stream, req.body);
break;
}
else {
body += c;
}
}
req.body = std::move(body);
}
#if 0
void parse_request(const char* request_str, HTTPRequest& req) {
std::stringstream request_stream(request_str);
request_stream >> req.method >> req.path >> req.version;
request_stream.ignore(2, '\n');
std::string line;
while (std::getline(request_stream, line) && !line.empty() && line.back() == '\r') {
line.pop_back();
std::size_t colon_pos = line.find(':');
if (colon_pos != std::string::npos) {
std::string header_name = line.substr(0, colon_pos);
std::string header_value = line.substr(colon_pos + 2);
req.headers.push_back(std::make_pair(header_name, header_value));
}
}
if (request_stream.peek() != EOF) {
std::getline(request_stream, req.body);
}
}
#endif
};
用例
#include "ksam_cgin_nethttp.h"
#include <locale.h>
void handle_get_request(const HTTPRequest& req, HTTPResponse& resp) {
std::cout << "T1-Body:" << req.body << std::endl;
resp.body= req.body;
resp.headers.push_back(std::make_pair("Content-Type","application/json"));
}
int main() {
setlocale(LC_ALL, "");
Server server(8111);
server.add_route("GET", "/", [](const HTTPRequest&, HTTPResponse& resp) {
resp.body = "test";
});
server.add_route("GET", "/t1", handle_get_request);
server.add_route("GET", "/about", [](const HTTPRequest&, HTTPResponse& resp) {
resp.body = "This is a C++ web service.";
});
server.add_route("POST", "/echo", [](const HTTPRequest& req, HTTPResponse& resp) {
resp.headers.push_back(std::make_pair("Content-Type", "text/plain"));
resp.body = req.body;
});
server.run();
return 0;
}