网站seo优化技术入门,做网站怎样实现网上支付,曹鹏 wordpress,怎么建设公司网站文章目录 1. 实现一个基础的HTTP Web服务器1.1 功能实现#xff1a;1.2 Log.hpp-日志记录器1.3 HttpServer.hpp-网页服务器1.4 Socket.hpp-网络通信器1.5 HttpServer.cc-服务器启动器 1. 实现一个基础的HTTP Web服务器
1.1 功能实现#xff1a; 总体功能#xff1a; 提供We… 文章目录 1. 实现一个基础的HTTP Web服务器1.1 功能实现1.2 Log.hpp-日志记录器1.3 HttpServer.hpp-网页服务器1.4 Socket.hpp-网络通信器1.5 HttpServer.cc-服务器启动器 1. 实现一个基础的HTTP Web服务器
1.1 功能实现 总体功能 提供Web服务响应客户端浏览器的HTTP请求 支持静态文件服务如HTML、图片等 多线程处理并发请求 带日志记录功能 具体工作流程
浏览器 → 发送HTTP请求 → 服务器↓解析请求↓查找文件↓返回响应↓
浏览器 ← 显示页面 ← 服务器各模块职责 日志记录器(Log.hpp) 记录服务器运行状态错误追踪和调试 网页服务器(HttpServer.hpp) 解析HTTP请求处理静态文件生成HTTP响应多线程处理请求 网络通信器(Socket.hpp) 处理底层网络通信管理TCP连接 服务器启动器(HttpServer.cc) 程序入口初始化和启动服务 1.2 Log.hpp-日志记录器
Log.hpp
#pragma once // 防止头文件重复包含// 系统头文件包含
#include iostream // 标准输入输出
#include time.h // 时间相关函数
#include stdarg.h // 可变参数处理
#include sys/types.h // 基本系统数据类型
#include sys/stat.h // 文件状态
#include fcntl.h // 文件控制选项
#include unistd.h // UNIX标准函数
#include stdlib.h // 标准库函数// 基础配置宏定义
#define SIZE 1024 // 缓冲区大小
#define LogFile log.txt // 默认日志文件名// 日志级别定义按严重程度递增
#define Info 0 // 普通信息记录系统正常操作信息
#define Debug 1 // 调试信息记录调试相关信息
#define Warning 2 // 警告信息记录潜在问题
#define Error 3 // 错误信息记录错误但不影响系统运行
#define Fatal 4 // 致命错误记录导致系统崩溃的错误// 日志输出方式定义
#define Screen 1 // 输出到屏幕直接显示在终端
#define Onefile 2 // 输出到单个文件所有日志记录到同一个文件
#define Classfile 3 // 分类输出根据日志级别输出到不同文件class Log {
private:int printMethod; // 日志输出方式std::string path; // 日志文件存储路径public:// 构造函数初始化日志系统Log() {printMethod Screen; // 默认输出到屏幕path ./log/; // 默认日志目录}// 设置日志输出方式void Enable(int method) {printMethod method;}// 将日志级别转换为对应的字符串std::string levelToString(int level) {switch (level) {case Info: return Info;case Debug: return Debug;case Warning: return Warning;case Error: return Error;case Fatal: return Fatal;default: return None;}}// 根据设置的输出方式打印日志void printLog(int level, const std::string logtxt) {switch (printMethod) {case Screen: // 输出到屏幕std::cout logtxt std::endl;break;case Onefile: // 输出到单个文件printOneFile(LogFile, logtxt);break;case Classfile: // 根据日志级别输出到不同文件printClassFile(level, logtxt);break;}}// 将日志输出到指定文件void printOneFile(const std::string logname, const std::string logtxt) {std::string _logname path logname;// 以追加方式打开文件如果不存在则创建int fd open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);if (fd 0) return; // 打开失败则直接返回write(fd, logtxt.c_str(), logtxt.size()); // 写入日志内容close(fd); // 关闭文件描述符}// 根据日志级别将日志输出到不同文件void printClassFile(int level, const std::string logtxt) {std::string filename LogFile;filename .;filename levelToString(level); // 构造文件名如log.txt.DebugprintOneFile(filename, logtxt);}// 重载函数调用运算符实现日志记录的核心功能void operator()(int level, const char *format, ...) {// 1. 获取当前时间time_t t time(nullptr);struct tm *ctime localtime(t);// 2. 格式化日志头部时间和级别信息char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), [%s][%d-%d-%d %d:%d:%d], levelToString(level).c_str(),ctime-tm_year 1900, ctime-tm_mon 1, ctime-tm_mday,ctime-tm_hour, ctime-tm_min, ctime-tm_sec);// 3. 处理可变参数格式化日志内容va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);// 4. 组合完整的日志消息char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), %s %s, leftbuffer, rightbuffer);// 5. 输出日志printLog(level, logtxt);}
};// 创建全局日志对象方便在程序各处使用
Log lg;/* 示例用法
int main() {lg.Enable(Screen); // 设置输出到屏幕lg(Info, Server started on port %d, 8080);lg(Error, Failed to connect to %s, database);return 0;
}
*/1.3 HttpServer.hpp-网页服务器
HttpServer.hpp 功能 HTTP请求处理多线程服务静态文件响应Cookie支持错误页面处理 #pragma once // 防止头文件重复包含// 基础库和系统库引入
#include iostream // 标准输入输出
#include string // 字符串处理
#include pthread.h // POSIX线程库
#include fstream // 文件流操作
#include vector // 动态数组
#include sstream // 字符串流
#include sys/types.h // 基本系统数据类型
#include sys/socket.h // Socket通信
#include unordered_map // 哈希表// 自定义头文件
#include Socket.hpp // Socket封装类
#include Log.hpp // 日志系统// 全局常量定义
const std::string wwwroot./wwwroot; // web服务器根目录
const std::string sep \r\n; // HTTP消息分隔符
const std::string homepage index.html; // 默认主页static const int defaultport 8082; // 默认端口号class HttpServer; // 前向声明// 线程数据结构存储每个线程处理的连接信息
class ThreadData
{
public:ThreadData(int fd, HttpServer *s) : sockfd(fd), svr(s) {}public:int sockfd; // 客户端连接的socket描述符HttpServer *svr; // HTTP服务器对象指针
};// HTTP请求解析类
class HttpRequest
{
public:// 反序列化HTTP请求void Deserialize(std::string req) {while(true){std::size_t pos req.find(sep);if(pos std::string::npos) break;std::string temp req.substr(0, pos);if(temp.empty()) break;req_header.push_back(temp); // 保存请求头req.erase(0, possep.size()); // 移除已处理部分}text req; // 保存请求体}// 解析HTTP请求处理URL和文件路径void Parse(){// 解析请求行方法、URL、HTTP版本std::stringstream ss(req_header[0]);ss method url http_version;// 构建文件路径file_path wwwroot; if(url / || url /index.html) {file_path /;file_path homepage; // 处理默认主页}else file_path url; // 其他页面// 获取文件后缀auto pos file_path.rfind(.);if(pos std::string::npos) suffix .html;else suffix file_path.substr(pos);}// 调试打印函数void DebugPrint(){// 输出请求信息用于调试for(auto line : req_header){std::cout -------------------------------- std::endl;std::cout line \n\n;}std::cout method: method std::endl;std::cout url: url std::endl;std::cout http_version: http_version std::endl;std::cout file_path: file_path std::endl;std::cout text std::endl;}public:std::vectorstd::string req_header; // 请求头部std::string text; // 请求正文// 解析后的请求信息std::string method; // 请求方法GET、POST等std::string url; // 请求URLstd::string http_version; // HTTP协议版本std::string file_path; // 请求文件路径std::string suffix; // 文件后缀
};// HTTP服务器类
class HttpServer
{
public:// 构造函数初始化端口和支持的内容类型HttpServer(uint16_t port defaultport) : port_(port){content_type.insert({.html, text/html});content_type.insert({.png, image/png});}// 启动服务器bool Start(){// 初始化Socket// 1. 创建Socketlistensock_.Socket();/* 这一步完成以下操作a) 调用系统函数 socket(AF_INET, SOCK_STREAM, 0) 创建TCP Socket- AF_INET: 使用IPv4协议族- SOCK_STREAM: 使用TCP协议- 0: 使用默认协议b) 设置Socket选项- SO_REUSEADDR: 允许地址重用避免服务器重启时的地址已被使用错误*/// 2. 绑定端口listensock_.Bind(port_);/* 这一步完成以下操作a) 创建sockaddr_in结构体设置- sin_family AF_INET (IPv4)- sin_port htons(port_) (设置端口号转换为网络字节序)- sin_addr.s_addr INADDR_ANY (监听所有网卡接口)b) 调用bind()函数将Socket与地址绑定- 如果端口已被占用或权限不足会失败*/// 3. 开始监听listensock_.Listen();/* 这一步完成以下操作a) 调用listen()函数将Socket转换为监听状态- backlog参数设置为10表示等待连接队列的最大长度- 超过此长度的新连接请求会被拒绝b) 此后Socket就能接受客户端连接请求- 服务器调用Accept()接受新的连接*/// 主循环接受并处理连接for (;;){// 准备变量存储客户端信息std::string clientip; // 将存储客户端的IP地址uint16_t clientport; // 将存储客户端的端口号// 接受新的客户端连接int sockfd listensock_.Accept(clientip, clientport);/* Accept函数做了这些事1. 等待客户端连接2. 获取客户端的IP和端口3. 返回新的socket描述符用于与该客户端通信*/// 连接失败则继续等待下一个连接if (sockfd 0) continue;// 记录新连接日志lg(Info, get a new connect, sockfd: %d, sockfd);// 创建新线程处理请求// 1. 声明线程ID变量pthread_t tid; // 用于存储新创建线程的ID// 2. 创建线程数据结构传入连接描述符和当前服务器对象ThreadData *td new ThreadData(sockfd, this);/* ThreadData包含- sockfd与客户端通信的socket描述符- this当前服务器对象的指针用于访问服务器的方法*/// 3. 创建新线程处理请求pthread_create(tid, nullptr, ThreadRun, td);/* 参数含义- tid存储新线程ID- nullptr使用默认线程属性- ThreadRun线程将执行的函数- td传递给线程函数的参数*/// 新线程会执行ThreadRun函数处理客户端请求// 主线程继续循环等待新的连接}}// 读取HTML文件内容static std::string ReadHtmlContent(const std::string htmlpath){// 1. 打开文件std::ifstream in(htmlpath, std::ios::binary);/* 说明- binary模式打开确保文件按原样读取- 不会对换行符进行转换*/// 文件打开失败则返回空字符串if(!in.is_open()) return ;// 2. 获取文件大小in.seekg(0, std::ios_base::end); // 将读指针移到文件末尾auto len in.tellg(); // 获取当前位置即文件大小in.seekg(0, std::ios_base::beg); // 将读指针移回文件开头// 3. 读取文件内容std::string content; // 用于存储文件内容content.resize(len); // 预分配空间// 一次性读取整个文件内容到字符串中in.read((char*)content.c_str(), content.size());// 4. 关闭文件in.close();return content; // 返回文件内容}// 根据文件后缀获取Content-Typestd::string SuffixToDesc(const std::string suffix){// 在content_type映射表中查找文件后缀对应的MIME类型auto iter content_type.find(suffix);// 如果找不到对应的MIME类型if(iter content_type.end()) return content_type[.html]; // 默认返回html的MIME类型text/htmlelse return content_type[suffix]; // 返回找到的MIME类型/* 例如- .html - text/html- .png - image/png这个MIME类型会被用在HTTP响应头的Content-Type字段中*/}// 处理HTTP请求void HandlerHttp(int sockfd){// 1. 接收HTTP请求char buffer[10240];ssize_t n recv(sockfd, buffer, sizeof(buffer) - 1, 0);/* 参数解释1. sockfd: 套接字描述符用于标识与客户端的连接2. buffer: 接收数据的缓冲区3. sizeof(buffer) - 1: 最大接收长度预留1个字节给\04. 0: 标志位使用默认行为返回值n- 大于0实际接收的字节数- 等于0连接已关闭- 小于0接收错误*/if (n 0){buffer[n] 0; // 字符串结束符// 2. 解析HTTP请求HttpRequest req;req.Deserialize(buffer); // 反序列化请求内容req.Parse(); // 解析请求获取方法、URL、版本等// 3. 读取请求的文件内容std::string text;bool ok true;text ReadHtmlContent(req.file_path); // 读取请求的文件if(text.empty()) // 文件不存在或读取失败{ok false;// 返回错误页面std::string err_html wwwroot /err.html;text ReadHtmlContent(err_html);}// 4. 构建HTTP响应// 4.1 响应行std::string response_line;if(ok)response_line HTTP/1.0 200 OK\r\n;elseresponse_line HTTP/1.0 404 Not Found\r\n;// 4.2 响应头std::string response_header Content-Length: ;response_header std::to_string(text.size());response_header \r\n;response_header Content-Type: ;response_header SuffixToDesc(req.suffix); // 设置正确的MIME类型response_header \r\n;response_header Set-Cookie: namehahapasswd12345; // 设置Cookieresponse_header \r\n;// 4.3 空行std::string blank_line \r\n;// 4.4 组装完整响应响应行响应头空行响应体std::string response response_line response_header blank_line text;// 5. 发送响应给客户端send(sockfd, response.c_str(), response.size(), 0);}// 6. 关闭连接close(sockfd);}// 线程运行函数static void *ThreadRun(void *args){pthread_detach(pthread_self()); // 设置线程分离ThreadData *td static_castThreadData *(args);td-svr-HandlerHttp(td-sockfd);delete td;return nullptr;}~HttpServer() {}private:Sock listensock_; // 监听socketuint16_t port_; // 服务器端口std::unordered_mapstd::string, std::string content_type; // 支持的内容类型映射
};1.4 Socket.hpp-网络通信器
Socket.hpp 功能 TCP连接封装地址绑定端口监听客户端连接处理错误处理 #pragma once // 防止头文件重复包含// 系统相关头文件
#include iostream // 标准输入输出
#include string // 字符串处理
#include unistd.h // UNIX标准函数定义
#include cstring // C字符串处理
#include sys/types.h // 基本系统数据类型
#include sys/stat.h // 文件状态
#include sys/socket.h // Socket接口
#include arpa/inet.h // IP地址转换函数
#include netinet/in.h // IP协议家族
#include Log.hpp // 日志系统// 错误枚举定义
enum
{SocketErr 2, // Socket创建错误BindErr, // 绑定错误ListenErr, // 监听错误
};// 监听队列长度
const int backlog 10;// Socket封装类
class Sock
{
public:Sock() {}~Sock() {}public:// 创建Socketvoid Socket(){// 创建TCP Socketsockfd_ socket(AF_INET, SOCK_STREAM, 0);if (sockfd_ 0){// 创建失败记录错误日志并退出lg(Fatal, socker error, %s: %d, strerror(errno), errno);exit(SocketErr);}// 设置Socket选项地址重用int opt 1;setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(opt));}// 绑定端口void Bind(uint16_t port){// 创建并初始化地址结构struct sockaddr_in local;memset(local, 0, sizeof(local));local.sin_family AF_INET; // IPv4local.sin_port htons(port); // 端口号local.sin_addr.s_addr INADDR_ANY; // 监听所有网卡// 绑定地址和端口if (bind(sockfd_, (struct sockaddr *)local, sizeof(local)) 0){// 绑定失败记录错误日志并退出lg(Fatal, bind error, %s: %d, strerror(errno), errno);exit(BindErr);}}// 开始监听void Listen(){// 启动监听if (listen(sockfd_, backlog) 0){// 监听失败记录错误日志并退出lg(Fatal, listen error, %s: %d, strerror(errno), errno);exit(ListenErr);}}// 接受连接int Accept(std::string *clientip, uint16_t *clientport){// 准备接收客户端地址信息struct sockaddr_in peer;socklen_t len sizeof(peer);// 接受新连接int newfd accept(sockfd_, (struct sockaddr*)peer, len);if(newfd 0){// 接受连接失败记录警告日志lg(Warning, accept error, %s: %d, strerror(errno), errno);return -1;}// 获取客户端IP地址char ipstr[64];inet_ntop(AF_INET, peer.sin_addr, ipstr, sizeof(ipstr));*clientip ipstr;// 获取客户端端口号*clientport ntohs(peer.sin_port);return newfd; // 返回新连接的文件描述符}// 连接服务器客户端使用bool Connect(const std::string ip, const uint16_t port){// 准备服务器地址信息struct sockaddr_in peer;memset(peer, 0, sizeof(peer));peer.sin_family AF_INET;peer.sin_port htons(port);inet_pton(AF_INET, ip.c_str(), (peer.sin_addr));// 建立连接int n connect(sockfd_, (struct sockaddr*)peer, sizeof(peer));if(n -1) {// 连接失败std::cerr connect to ip : port error std::endl;return false;}return true; // 连接成功}// 关闭Socketvoid Close(){close(sockfd_);}// 获取Socket文件描述符int Fd(){return sockfd_;}private:int sockfd_; // Socket文件描述符
};1.5 HttpServer.cc-服务器启动器
HttpServer.cc 功能 程序入口参数解析服务器初始化智能指针管理 // 包含必要的头文件
#include HttpServer.hpp // HTTP服务器类定义
#include iostream // 标准输入输出
#include memory // 智能指针
#include pthread.h // POSIX线程库
#include Log.hpp // 日志系统using namespace std;int main(int argc, char *argv[])
{// 检查命令行参数if(argc ! 2) // 要求必须提供端口号参数{exit(1); // 参数错误退出程序}// 将命令行参数转换为端口号uint16_t port std::stoi(argv[1]); // 字符串转换为整数// 创建HTTP服务器实例// 以下是三种方式注释掉的是不推荐的方式// 方式1不推荐普通指针需要手动管理内存// HttpServer *svr new HttpServer();// 方式2语法错误unique_ptr的错误声明方式// std::uniqueHttpServer svr(new HttpServer());// 方式3推荐使用智能指针unique_ptr自动管理内存std::unique_ptrHttpServer svr(new HttpServer(port));// 启动服务器svr-Start(); // 开始监听和处理请求// 程序正常退出return 0;
}