【C/C++】web服务器项目开发总结【请求 | 响应 | CGI】
博客主页:花果山~程序猿-CSDN博客
文章分栏:Linux_花果山~程序猿的博客-CSDN博客
关注我一起学习,一起进步,一起探索编程的无限可能吧!让我们一起努力,一起成长!
目录
一,背景
二,目标
三,基本描述
四,技术特点
网络基本认识补充
web服务器(http服务器工作原理)
http 1.0与http 1.1
区分URI & URL & URN
五,技术要点
1. http请求&响应
2. 请求方法
3.http响应报文状态码设置
4.CGI机制
CGI实现原理
5.线程池优化
嗨!收到一张超美的图,愿你每天都能顺心!
一,背景
二,目标
三,基本描述
四,技术特点
- 网络编程(TCP/IP协议, socket流式套接字,http协议)
- 多线程技术
- cgi技术
- shell脚本
- 线程池
项目定位:研发岗
开发环境:centos7 + vim/g++/vscode + c/c++
网络基本认识补充
web服务器(http服务器工作原理)
http 1.0与http 1.1
目前主流的浏览器使用http1.1
http1.0优点:
- 简单快速,HTTP服务器的程序规模小,因而通信速度很快。
- 灵活,HTTP允许传输任意类型的数据对象,正在传输的类型由Content-Type加以标记。
- 无连接,每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。(http/1.0具有的功能,http/1.1兼容)
http1.1的优化:可是,随着web的发展,因为无状态而导致业务处理变的棘手起来。比如保持用户的登陆状态(由于无状态,所以每次访问不知道用户信息,而又不可能让用户每次登录)。
区分URI & URL & URN
例如:
URI: /home/index.htmlURL: www.xxx.com:/home/index.html
五,技术要点
1. http请求&响应
下面是对应的示意图
请求详细的:
响应详细的:
在此项目中对请求,响应报文头进行简化,代表大致即可,主要是首行和正文。
测试工具:telnet(linux),postman(windows)
2. 请求方法
GET(多服务器请求资源):
- GET 请求中的参数会被附加在 URL 后面,因此可以被浏览器缓存,并且可以在浏览历史中看到,也可能被记录在网络日志中。这使得 GET 请求不适合传输敏感信息。
- 有浏览器URL长度限制;
- 安全性较差;
POST(多向服务器上传数据):
- POST 请求的数据不会显示在 URL 中,也不会被缓存或保存在历史记录中,因此相对更安全。
- 数据在正文,大小没有URL限制;
- 正文信息,不会被URL记录,安全性较高;
等等其他请求方法用的少,感兴趣可以查查
3.http响应报文状态码设置
如:
200 OK
404 资源未找到
具体状态码详细可以查看详细资料
4.CGI机制
理论上,可以使用任何语言来编写CGI 程序(如:java,php等以及脚本语言)。
CGI实现原理
适合条件:参数量小,如带参的GET方法
适合条件:参数量大,如POST方法
而进行程序替换后,我们并不知道具体的管道描述符,因此在替换前,需要将pipe的输入输出重定向给标准输入输出,这样子进程只需要使用cout(1),cin(0)即可与向父进程(server线程)进行进程间通信,传递参数&结果。
期间也需要通过环境变量告知子进程参数大小。
CGI代码如下:
int ProcessCGI()
{
std::string tmp_iurlb = ".";
tmp_iurlb += address;
int output[2]; // 线程发数据
int inget[2]; // 线程接收结果
pipe(output);
pipe(inget);
pid_t pd = fork();
// 线程为主视角
if (pd == 0)
{
// 子
close(output[1]);
close(inget[0]);
// 1.接收数据,准备程序替换
std::string room = "METHOD="; //请求类型环境变量
room += method;
putenv((char *)room.c_str());
std::string tmp; // get参数 环境变量
std::string room_length; // post正文的长度环境变量
if (method == "POST")
{
room_length = "CONTENT-LENHTH=";
room_length += std::to_string(content_length);
putenv((char *)room_length.c_str());
}
else if (method == "GET" && parameter.size() != 0)
{
tmp = "PARAMENTER=";
tmp += parameter;
putenv((char *)tmp.c_str());
}
// std::cerr << "debug : excel:" << address.c_str() << std::endl;
// 约定:子进程只需从标准输入输出进行获取数据
dup2(output[0], 0);
dup2(inget[1], 1);
execl(("." + address).c_str(), nullptr); // 疑问:既然通过环境变量来传参数,那buff到时候传过去的就是
close(0);
close(1);
std::cerr << "GET EXECL FAIL:" << std::endl;
return -1;
}
else if (pd > 0)
{
// 父
close(output[0]);
close(inget[1]);
if (method == "POST");
{
const char* str = request_body.c_str();
int total = 0;
int size = 0;
while ((total <= content_length) &&
(size = write(output[1], str + total, request_body.size() - total) > 0))
{
total += size;
}
}
//接收CGI 返回值
while (1)
{
char x = 'g';
int set = read(inget[0], &x, 1);
if ( x == '\n')
break;
httpresponse.Respone_body.push_back(x);
}
// std::cout << "res CGI:" << httpresponse.Respone_body << std::endl;
int status = 0;
waitpid(pd, &status, 0); // 线程阻塞试等待
if (WIFEXITED(status) == 0)
Logmessage(WARN, "CGI exit with error code");
close(output[1]);
close(inget[0]);
return 0;
}
5.线程池优化
- 大量链接过来导致服务器内部进程或者线程暴增,进而导致服务器效率严重降低或者挂掉
- 节省链接请求到来时,创建线程的时间成本
- 让服务器的效率在一个恒定的稳定区间内(线程个数不增多,CPU调度成本不变)
本项目采用曾经线程池博客进行简单修改,参考博客:
线程池小项目【Linux & C/C++】(踩坑分享)_c++linux项目-CSDN博客
下面用一张示意图梳理一下流程:
- 实现支持http1.1,长连接,以及处理链接管理,黏包问题
- 支持更高并发的epoll
- 添加redis,mysql等
- 实现为该请求转发器(代理服务)
- 尝试打包成组件,实现http快速搭建
- 个人简历
- 个人博客等等
项目代码
Linux: 从0到1 - Gitee.com
结语
本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论,如果给小伙伴带来一些收获,请动动你发财的小手点个免费的赞,你的点赞和关注永远是博主创作的动力源泉。