本文主要是介绍TinyWebSever源码逐行注释()_简单代码的整合,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
前言
项目源码地址
项目详细介绍
项目简介:
Linux下C++轻量级Web服务器,助力初学者快速实践网络编程,搭建属于自己的服务器.
- 使用 线程池 + 非阻塞socket + epoll(ET和LT均实现) + 事件处理(Reactor和模拟Proactor均实现) 的并发模型
- 使用状态机解析HTTP请求报文,支持解析GET和POST请求
- 访问服务器数据库实现web端用户注册、登录功能,可以请求服务器图片和视频文件
- 实现同步/异步日志系统,记录服务器运行状态
- 经Webbench压力测试可以实现上万的并发连接数据交换
以下是该项目中比较简单的代码的源码注释:
main.cpp
#include "config.h"int main(int argc, char *argv[])
{//需要修改的数据库信息,登录名,密码,库名string user = "root";string passwd = "root";string databasename = "qgydb";//命令行解析Config config;config.parse_arg(argc, argv);WebServer server;//初始化server.init(config.PORT, user, passwd, databasename, config.LOGWrite, config.OPT_LINGER, config.TRIGMode, config.sql_num, config.thread_num, config.close_log, config.actor_model);//日志server.log_write();//数据库server.sql_pool();//线程池server.thread_pool();//触发模式server.trig_mode();//监听server.eventListen();//运行server.eventLoop();return 0;
}
webserver.h
#ifndef WEBSERVER_H
#define WEBSERVER_H// 导入必要的头文件,用于处理网络编程、文件描述符、epoll机制、线程池等功能
#include <sys/socket.h> // 套接字相关
#include <netinet/in.h> // IP地址相关
#include <arpa/inet.h> // IP转换相关
#include <stdio.h> // 标准输入输出
#include <unistd.h> // POSIX 操作系统 API,如close等
#include <errno.h> // 错误处理
#include <fcntl.h> // 文件控制(非阻塞设置)
#include <stdlib.h> // 标准库函数,如malloc等
#include <cassert> // 断言,用于调试
#include <sys/epoll.h> // epoll相关// 导入线程池和HTTP连接相关模块
#include "./threadpool/threadpool.h"
#include "./http/http_conn.h"// 常量定义
const int MAX_FD = 65536; // 最大文件描述符数量
const int MAX_EVENT_NUMBER = 10000; // epoll 监听的最大事件数
const int TIMESLOT = 5; // 定时器的最小超时时间单位(秒)class WebServer
{
public:// 构造函数WebServer();// 析构函数~WebServer();// 初始化函数,设置服务器端口、数据库信息、日志等void init(int port , string user, string passWord, string databaseName,int log_write , int opt_linger, int trigmode, int sql_num,int thread_num, int close_log, int actor_model);// 初始化线程池void thread_pool();// 初始化数据库连接池void sql_pool();// 初始化日志系统void log_write();// 设置触发模式(边沿触发或水平触发)void trig_mode();// 设置监听事件(epoll监听)void eventListen();// 事件循环,处理服务器运行中的各类事件void eventLoop();// 处理定时器的回调,管理客户端连接void timer(int connfd, struct sockaddr_in client_address);// 调整定时器的时间void adjust_timer(util_timer *timer);// 处理超时的定时器,关闭连接void deal_timer(util_timer *timer, int sockfd);// 处理客户端数据的读取事件bool dealclientdata();// 处理信号(如关闭服务器、超时等)bool dealwithsignal(bool& timeout, bool& stop_server);// 处理读事件void dealwithread(int sockfd);// 处理写事件void dealwithwrite(int sockfd);public:// 基础参数int m_port; // 服务器端口号char *m_root; // 服务器根目录int m_log_write; // 日志写入方式int m_close_log; // 是否关闭日志int m_actormodel; // 事件处理模式(Reactor/Proactor)// 管道文件描述符(用于处理信号)int m_pipefd[2];// epoll 文件描述符int m_epollfd;// 所有客户端的 HTTP 连接数据http_conn *users;// 数据库相关connection_pool *m_connPool; // 数据库连接池string m_user; // 数据库用户名string m_passWord; // 数据库密码string m_databaseName; // 数据库名称int m_sql_num; // 数据库连接数量// 线程池相关threadpool<http_conn> *m_pool; // 线程池指针int m_thread_num; // 线程数量// epoll 事件相关epoll_event events[MAX_EVENT_NUMBER]; // 用于存储epoll等待到的事件// 套接字相关int m_listenfd; // 监听套接字int m_OPT_LINGER; // 是否使用优雅关闭连接(linger选项)int m_TRIGMode; // 触发模式(边沿或水平触发)int m_LISTENTrigmode; // 监听套接字的触发模式int m_CONNTrigmode; // 连接套接字的触发模式// 定时器相关client_data *users_timer; // 用户定时器数据Utils utils; // 工具类,管理定时器和信号
};
#endif
Config.cpp
这里面也只有一个解析命令行的代码:
void Config::parse_arg(int argc, char*argv[]){int opt;const char *str = "p:l:m:o:s:t:c:a:";while ((opt = getopt(argc, argv, str)) != -1){switch (opt){case 'p':{PORT = atoi(optarg);break;}case 'l':{LOGWrite = atoi(optarg);break;}case 'm':{TRIGMode = atoi(optarg);break;}case 'o':{OPT_LINGER = atoi(optarg);break;}case 's':{sql_num = atoi(optarg);break;}case 't':{thread_num = atoi(optarg);break;}case 'c':{close_log = atoi(optarg);break;}case 'a':{actor_model = atoi(optarg);break;}default:break;}}
}
这段代码是 Config::parse_arg
函数的实现,它用于解析命令行参数,将传入的命令行参数根据不同的标志(如 -p
, -l
, -m
等)转换为对应的配置值。
下面是详细的解释:
1. 函数签名
void Config::parse_arg(int argc, char *argv[])
- 这是一个
Config
类的成员函数,名字是parse_arg
。 argc
是命令行参数的个数,argv
是存储命令行参数的字符数组。argc
和argv
通常在main
函数中作为参数传递。- 这个函数的作用是根据命令行输入的参数,解析并设置类中的相关配置项。
2. 定义变量
int opt;
const char *str = "p:l:m:o:s:t:c:a:";
opt
:用于存储解析到的选项字符(如-p
,-l
等)。str
:定义了命令行参数选项的格式。每个字符代表一个参数的标志,后面的冒号(:
)表示该选项需要一个参数。例如,'p'
后面有冒号,因此-p
选项必须带有一个参数。
3. getopt
函数
while ((opt = getopt(argc, argv, str)) != -1)
getopt
是一个用于解析命令行参数的标准库函数,它依次解析由argv
传入的参数,并根据str
中定义的选项返回对应的标志字符(如p
、l
等)。- 当
getopt
返回-1
时,表示已经没有更多的选项可供处理。
4. switch
语句处理选项
- 根据
getopt
返回的标志字符(存储在opt
中),switch
语句分别处理不同的命令行选项。
switch (opt)
{case 'p':PORT = atoi(optarg);break;// 其他选项处理
}
- 每个
case
语句处理对应的标志选项(如-p
,-l
等)。 atoi(optarg)
:optarg
是getopt
提供的当前选项的参数值(它是一个字符串)。atoi
函数用于将字符串转换为整数。例如,当用户输入-p 8080
时,optarg
是"8080"
,而atoi(optarg)
将其转换为整数8080
。- 这些参数分别赋值给类中的成员变量(如
PORT
,LOGWrite
,TRIGMode
等)。
5. 解析的命令行选项
-p
:解析端口号,赋值给PORT
。-l
:日志写入方式,赋值给LOGWrite
。-m
:触发模式,赋值给TRIGMode
。-o
:设置 linger 选项,赋值给OPT_LINGER
。-s
:数据库连接池的连接数,赋值给sql_num
。-t
:线程池中的线程数,赋值给thread_num
。-c
:是否关闭日志,赋值给close_log
。-a
:选择处理模型,赋值给actor_model
。
6. 默认行为
- 如果传入了未定义的选项(即不在
str
中的标志),default
部分将不做任何处理。
Config.h
#ifndef CONFIG_H
#define CONFIG_H#include "webserver.h"using namespace std;class Config
{
public:Config();~Config(){};void parse_arg(int argc, char*argv[]);//端口号int PORT;//日志写入方式int LOGWrite;//触发组合模式int TRIGMode;//listenfd触发模式int LISTENTrigmode;//connfd触发模式int CONNTrigmode;//优雅关闭链接int OPT_LINGER;//数据库连接池数量int sql_num;//线程池内的线程数量int thread_num;//是否关闭日志int close_log;//并发模型选择int actor_model;
};#endif
这篇关于TinyWebSever源码逐行注释()_简单代码的整合的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!