首页 > 编程知识 正文

web服务器和web容器,搭建一个web服务器

时间:2023-05-05 10:18:02 阅读:52431 作者:2670

目录开头的无厘头正文Http工作流Http.hHttp.cpp个人网站的链接源地址

胡说八道

其实这篇文章我一直想写,没有时间,想一口气写完,回家的前一天晚上,在上海的小酒店里很兴奋,用c写一个简单的http服务器的方法。

我想大多数人都想要自己的网站。 即使坏了也是自己做的。 我当初是这么希望的。 我记得那是2020年的8月。 我查的各种资料终于掌握了套接字,迫不及待地写聊天程序,研究端口映射的方法。 那时还傻天真,一个连接来了就打开线程,离开了就放弃那个线程。 我也知道它创建和销毁线程的开销很大,但是我没想到其他好方法呢。 现在回想起来这个问题看起来很傻,但为什么没想到会使用线程池技术呢? 学习是一步一步进化的过程,不羡慕别人,不和别人比,只和自己比!

在本文中,您可以轻松地阅读代码,而无需处理许多复杂的概念或编写难以阅读的模板函数。 在这篇文章中,所有想用c写http服务的合作伙伴当然可以不看jjdxz们,twdhy哦。 因为我还是初学者。 把我在学习http时遇到的问题、想法写在本章里。

本文怎么写简单的http服务器? 很简单。 只需要回到基本的三样东西。

状态代码发送文件的长度发送文件的类型状态码例如200 (找到请求文件),404 (未找到请求文件),我实现的也比较简单,这个

文件长度例如,如果客户请求index.html页面,浏览器如何知道收到的此文件何时退出? 依靠文件的长度

文件类型html的类型为html格式,css的为css格式,图像为图像格式,zip有与zip格式的文件格式相对应的文件类型表

向客户端添加三个这样的东西所请求的文件即可(如果请求文件存在)。

如果想更复杂,请查看本文中介绍http各个参数的链接

Http工作流本节介绍大致的Http工作流。 客户端通过网站访问。 请记住,客户端主动请求连接,而服务端被动连接。 实际上是通过IP端口访问。 但是,http的默认端口号为80。 例如,如果没有域名、云服务等,如何在本地进行测试? 在浏览框中输入ip:port (例如127.0.0.1:9996 ),按回车可以在本地访问自己的http服务器。 当然,http必须为客户(委托人)提供首页。 如果客户没有指定主页,则只需列出域名或127.0.0.1:9996即可提供默认主页。 这也是我们必须实现的。 因为客户写了请求文件,所以我们判断是否存在,如果存在,就返回状态代码200和请求文件的内容。 如果不存在,直接返回404。 那么,怎么判断呢? 以最简单的GET为例。 其他也差不多。 感兴趣的同学可以自己研究其他事情。 使用正则表达式分析客户的请求是GET、POST还是其他请求,并将其解析为请求文件。 利用状态机思想判断文档是否存在,如果存在,判断文档的类型。 大概的流程是这个。

HTP.h # pragma once # include string # include unordered _ map # includesys/types.h # includesys/stat.h # includeudeunision std:string filePath_; //具体文件的绝对路径std:string fileType_; //请求文件的类型std:string header_; //http标头response int fileSize_; int fileFd_; 结构开始文件stat _; //POST请求时将消息保存到本地文件的bool isPostMode_=false; 公共: http (=default; void添加头(const STD 33603360 string head; 语音头(bool flag ); 添加一些//头文件信息,仅在成功时调用此函数,并返回//文件中的数据void processHead ()。 //请求文件的路径包含voidaddfilepath (常数STD 33603360 stringrequestfile ); //检索文件的类型voidanalysefiletype (const STD 33603360 stringrequestfile ); 布尔型安全文件(cons

t std::string& request); void SendFile(int clientFd,bool isRequestOk); bool fileIsExist(); // 用户自定义的回调函数要正确的处理异常和自己负责关闭套接字 void ReadCallback(TcpClient* t);}; Http.cpp #include "Http.h"#include "TcpClient.h"#include "Logger.h"#include <regex>#include <sys/socket.h>#include <sys/sendfile.h>#include <fcntl.h>#include <unordered_map>// 用于test#include <iostream>using namespace std;void Http::addHeader(const string& head){ if (!head.empty()) { header_ += head; header_ += "rn"; // Console.WriteLine("我在这里 head!= null" + header_); } // 自动加个结尾 else { header_ += "rn"; // Console.WriteLine("我在这里 head == null" + header_); }}void Http::Header(bool flag){ // 判断要发送的头部 true 表示200 false 表示404 if(flag == true) { header_ = "HTTP/1.1 200 OKrn"; } else { header_ = "HTTP/1.1 404 NOTFOUNDrnContent-Length:0rnrn"; }}void Http::processHead(){ string ContentType = "Content-Type:"; if (fileType_ == "html") { ContentType += "text/html"; } else if(fileType_ == "js") { ContentType += "application/x-javascript"; } else if(fileType_ == "css") { ContentType += "text/css"; } else if(fileType_=="jpg" || fileType_== "png") { ContentType += "image/" + fileType_; } else if (fileType_== "zip" || fileType_ == "tar") { ContentType += "application/" + fileType_; } addHeader(ContentType); // 代完善,要打开文件 filePath_是请求文件的路径 fileSize_= fileStat_.st_size; string ContentLength = "Content-Length:" + to_string(fileSize_); addHeader(ContentLength); // 最后加了一个结尾 addHeader(""); // Console.WriteLine("process fileContent_:" + );}void Http::addFilePath(const string& requestFile){ filePath_ += requestFile;}void Http::analyseFileType(const string& requestFile){ for (int i = 0; i < requestFile.size(); ++i) { if (requestFile[i] == '.') { // 获取请求文件以什么结尾的 fileType_ = requestFile.substr(i + 1); } }}bool Http::fileIsExist(){ fileFd_ = ::open(filePath_.c_str(),O_CLOEXEC | O_RDWR); if (fileFd_ < 0) { // 说明为找到请求的文件 return false; } return true;}bool Http::analyseFile(const string& request){ // 调用header的 // 在[]的^是以什么什么开头,放在[]里面的是非的意思 string pattern = "^([A-Z]+) ([A-Za-z./1-9-]*)"; regex reg(pattern); smatch mas; regex_search(request,mas,reg); // 因为下标0是代表匹配的整体 if(mas.size() < 3){ LOG_INFO("不是正常请求"); // 啥都不是直接返回false return false; } string requestMode = mas[1]; if(requestMode == "POST"){ isPostMode_ = true; cout << "POST请求!!!!!" << endl; } // 请求的具体文件 string requestFile = mas[2]; // 先获取请求的文件 bool flag; if (requestFile == "/") { // 如果是/的话就给默认值 filePath_.clear(); // 先清个零 filePath_ = path_; filePath_ += "/run.html"; // 文件的类型也要给人家加上 fileType_ = "html"; } else { filePath_.clear(); // 先清个零 filePath_ = path_; addFilePath(requestFile); // 利用open函数 } flag = fileIsExist(); // 未找到文件的话 if(!flag){ LOG_INFO("未找到客户要的文件"); cout << filePath_ << endl; return false; } ::fstat(fileFd_,&fileStat_); // 如果文件不存在的话也就不需要解析类型 analyseFileType(requestFile); return true;}void Http::SendFile(int clientFd,bool isRequestOk){ long len = 0; // 头部一定是有的。 while(len < header_.size()){ len += ::send(clientFd,header_.c_str(),header_.size(),0); cout << "len header" << header_ <<endl; } // 发完了头,在发请求文件的信息。如果是404这里是没有的 if (isRequestOk == true) { len = 0;int num = 0; int tmpLen = 0;// 连续好几次没变的话就加一个num while (len < fileSize_) { // 发送的文件个数已经写入在len当中了 ::sendfile(clientFd,fileFd_,(off_t*)&len,fileStat_.st_size- len); cout << "len sendfile" <<"len:" << len << "fileSize" << fileSize_ <<endl; if(len <= 0 ){break; } if(tmpLen == len){++num;if(num > 10){break;} } tmpLen = len;} }}void Http::ReadCallback(TcpClient* t){ cout << "ReadCallback" << endl; int sockFd = t->getFd(); char buff[1024]; int r = ::recv(sockFd,buff,sizeof(buff),0); if (r == 0) { t->CloseCallback(); return; } buff[r] = ''; string str = buff; cout << str << endl; // 未找到文件直接回应404. bool flag = analyseFile(str); Header(flag); if(!flag){ SendFile(sockFd,false); // t->CloseCallback(); return ; } // 这个修改头文件的,先调用这个 processHead(); //这是文件找到了发送的 SendFile(sockFd,true); if(isPostMode_){ int fd = ::open("./postLog/message.txt",O_RDWR); if(fd < 0){ LOG_ERROR("未找到文件"); } else{ // 文件偏移到末尾 ::lseek(fd,0,SEEK_END); ::write(fd,str.c_str(),str.size()); close(fd); } isPostMode_ = true; } // 关闭文件套接字 close(fileFd_); // 发完就关闭连接,主要是为了多去几个线程还能跑的快一些 //t->CloseCallback();}

不考虑高并发的情况,设计一个同步阻塞的epoll即可,看完http必备的三要素已经能够写出一个服务器了,我的底层socket采用的是自己封装的网络库,Reactor模型,one loop per thread的代码文件比较多,所以就没有放上来,但只要把状态码、文件类型(那一大段if)、文件的长度这三个实现了就可以搭建一个简易的http服务器了。可以利用sendfile零拷贝来发送文件

在补充一点就是,http协议是rn结尾。最后还有一个rn,就比如404,HTTP/1.1 404 NOTFOUNDrnContent-Length:0rnrn,最后面再跟一个rn,结束一段跟一个rn

个人网站链接

个人网站

源码地址

2021.10.25更,最近终于有空把自己的代码重构了一下,感觉写的还可以,附上链接github链接有兴趣可以点个星哈哈哈哈,可以去github上看一看

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。