
HTTP基础知识一、网址的构成1.协议告诉浏览器用户用什么规则访问。2.域名域名通过域名解析转变为ip地址对于对外提供服务的Web服务器默认使用80HTTP和443HTTPS端口如果没写端口号访问http://会自动补上:80。3.URL统一资源定位符所有网络上的资源都可以被唯一的一个字符串标识也就是URL统一资源定位符4.文件路径浏览器不可能把所有网站的所有资源都存起来一个网站的资源几种存放在服务器的指定的目录下web根目录而网址中显示的文件路径是客户端当前访问资源对应的路径。5.冲突字符的处理网络行为包括获取数据与传输数据而传输数据如果采用的是时可能因为域名通过域名解析变为ip地址二、HTTP协议格式输入网址按下enter本质是向服务器发送了请求服务器处理后发送对应的网页资源这才实现了网页页面的跳转。下面详细介绍HTTP请求和响应的协议格式。1.请求协议HTTP请求协议格式如上图所示行间使用\r\n进行分隔而行内使用空格分开附属性键值对的“Key:”后面也要加一空格下面对各部分进行说明。(1)请求行请求方法一般用GET和POST这两种方法后文会展开来讲空一格后写URL接着是协议版本客户端和服务器的版本可能不同导致支持的功能有所差异后文解释所以需要带上。(2)请求属性请求属性以键值对格式填写实际上也的确使用键值对做查找匹配。下面为常见属性(3)空行空出一行用于区分报头和报文。服务端读到空行时说明报头已完整读取而报头里包含内容请求正文长度这一属性这两个特点共同保证报文的完整性。因此一个请求至少要有请求行和空行。(4)请求正文即用户要上传的数据。2.响应协议响应协议和请求协议类似仅状态行有所不同也有协议版本,而状态码和状态码描述用于表述响应状态如404 Not Found3.协议细节(1)http方法GET方法使用把要上传的数据放到url里面POST方法提交的参数是放在请求正文内的两种方法的使用时机get方法的参数个数有限制 另外参数在搜索的时候会回显到网址框那里不安全post方法相对安全但不加密也是不安全的本质是不直接泄漏用户的信息。(2)表单无论采用上述哪一种方法用户的数据都是通过前端的表单提交的如下面代码form action/ methodget name: input typetext namenamebr passwd: input typetext namepasswdbr input typesubmit value提交 /form效果如下图(emm乱码那是提交按键)输入并提交后浏览器上传数据如下http://182.254.173.195:8080/?namedasdsapasswddadasdsasdasda表单还可以有其它作用比如在上文表单代码示例中如果action指向一个可执行程序时服务器收到请求后会创建子进程执行该程序并将表单数据通过环境变量或标准输入传递给它实现动态处理如用户注册、数据录入。(3)状态码响应报头中的状态码向客户端说明处理状态类似于错误码。如常见的404就是代表无法找到页面的状态码。下面讲下3开头的状态码也就是重定向相关的状态码。301(永久重定向)资源永久迁移到新 URL浏览器会缓存跳转结果下次直接访问新地址适用于域名更换、资源永久搬家。302(临时重定向)资源暂时跳转浏览器不会缓存每次仍然访问原 URL适用于临时维护、活动页跳转。(4)常见的报头Content-Type: 数据类型(text/html等)Content-Length: Body的长度Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;User-Agent: 声明用户的操作系统和浏览器版本信息可用于反爬。referer: 当前页面是从哪个页面跳转过来的;可配合浏览器的前进和后退页面的功能实现location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问;Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;(5)长短连接一个网页页面通常包含多个元素而每一个元素就是一个资源。如果服务器和浏览器对每个资源都做请求和响应会非常耗费时间。一次请求响应一个资源随后关闭连接就是短链接http1.0默认使用短链接如果要使用长连接则Connection属性要设置为keep-alive。建立链接后一次性发送或返回多个http的请求或响应的过程就是长链接。http1.1默认使用长链接也可使用短连接。之所以一开始要交换版本信息就是为了防止通信双方中有一方只支持 HTTP/1.0连接自动降级为短连接模式。(6)Cookie(缓存)http本身默认是无状态的也就是没有缓存。在网页注册账户后浏览器会帮忙保存cookie也就是你的账号密码此后用户登录该网页时浏览器向网页服务器发送请求会自动带上账密从而跳过到了登陆环节。cookie有文件级别也有内存级别。顾名思义内存级cookie是保存在浏览器的内存中的关闭浏览器后下次再次打开并登录网页需要重新输入。而文件级别的cookie可将cookie放在浏览器文件夹中长期保留。在服务端的响应字段中加入Set-Cookie属性从而设立一个cookie见下面参考代码response Set-Cookie: ussrnamezzspasswd12345\r\n;//缓存设置(7)cookie的安全问题1.可能被盗取cookie被外部拿到本身不可避免但sessionid由服务器统一管理可随时销毁所以异地登录可要求认证也就是重新设置sessionid。2.个人私有信息泄漏登陆账号也是上传数据的过程也就是密码可能出现在URL中。应对方法不再直接将密码作为cookie而是由服务器生成的sessionid传给浏览器下次通过id匹配登录服务器可将sessionid交由redis数据库管理。黑客虽然仍可拿取cookie,但这时候回显的只是id真正的密码由网站管理使得私人信息不被泄漏。HTTP的实现实现目的结合上述HTTP知识利用TCP实现一HTTP服务器并编写简单网页打通前后端隔阂理解HTTP的应用。实现思路和TCP的封装及应用类似首先利用socket接口建立连接然后基于TCP协议进行数据传输。而对于HTTP而言难点在于服务端需要遵守协议规范具有正确读取并解析请求、并返回正确响应的功能此外还涉及到网页代码和文件操作相关知识这里不对这两点以及服务器的运作方式多做深挖直接采用多线程的方式接收请求线程函数负责处理请求并返回响应。反序列化对请求做反序列化和以往需要拆解字符串本身不同请求本身已规范使用\r\n作为分隔符这里的任务是把各行拆开放入数组并对请求行做拆解赋值给成员变量。具体代码如下std::string linesep \r\n;//行分隔符 std::string wordsep \0;//行内分隔符 struct thread_info//线程信息 { HttpServer *hsp;//服务器指针传this要用 int fd; }; class Request { public: Request() {} void deserialization(std::string target)//对请求做反序列化 { // 按行读取 std::string line; while (true) { size_t pos target.find(linesep); std::string temp target.substr(0, pos); if (temp.empty()) { break;//空行被找到 } header.push_back(temp); target.erase(0, pos linesep.size());//将缓冲区内对应的字符删除避免影响后续存储的请求。 } // 找到空行后剩下的就是请求正文直接赋值 text target; } void Parse() { std::stringstream ss(header[0]); //流式切割获取请求头信息url最为重要用于定位用户需要访问的资源路径 ss method url http_version; } std::string GetUrl() { return url; } private: //用于存放请求头和请求报头内容的数组 std::vectorstd::string header; //请求正文 std::string text; //请求行信息 std::string method; std::string url; std::string http_version; };负责处理请求的线程函数这里直接省略完整代码从线程函数开始讲解完整代码见本章末。接收到的参数是封装好的线程信息包括服务器指针和sockfd.struct thread_info//线程信息 { HttpServer *hsp;//服务器指针传this要用 int fd; };Run方法负责接收请求和发送响应而处理并构建响应由handler方法处理。static void *Run(void *argv) { // 接收 处理并得到响应handler 发送 thread_info *info (thread_info *)argv; pthread_detach(pthread_self()); char buf[10240] {0}; int n recv(info-fd, buf, sizeof(buf) - 1, 0); // 接收http请求 if (n 0) { std::string request buf; std::string sendmessage info-hsp-handler(request); send(info-fd, sendmessage.c_str(), sendmessage.size(), 0); delete info; } else if (n 0) { LOG(INFO, Client exit); delete info; } else { LOG(FATAL, recv fail); delete info; } return nullptr; }handler负责拆解请求并判断用户输入的路径根据路径获取对应的资源Getcontent方法实现用于填充响应正文至于响应头主要是检查资源类型补充的Content_Type属性响应正文的长度Content-Length以及缓存设置Set-Cookie然后返回整个响应。content_type对应的unordermap直接在构造函数中硬编码。std::string Getcontent(std::string sourcepath) { // 图片文件需要用读取为二进制方式读取 std::ifstream in(sourcepath, std::ios::binary); if (!in.is_open()) { return ; // 文件打开失败返回空字符串 } in.seekg(0, std::ios::end); size_t size in.tellg(); in.seekg(0, std::ios::beg); std::string content(size, \0); std::coutsize:sizestd::endl; in.read(content[0], size); in.close(); return content; } std::string CheckType(std::string url){ size_t posurl.rfind(.); //如果没有找到默认是网页资源 if (pos std::string::npos) { return content_type_[.html]; } std::string typeurl.substr(pos); auto it content_type_.find(type); if(itcontent_type_.end()){ return content_type_[.html]; } else{ return content_type_[type]; } } std::string handler(const std::string recvmessage) { // 负责处理请求,判断路径获取资源Getcontent方法实现返回响应 //构建请求类做反序列化 Request rq; rq.deserialization(recvmessage); rq.Parse();//获取请求行各参数信息 rq.DebugPrint();//博客中省略debugprint // 对资源路径做判断 //注意一般用户不会吧web根目录写上来服务器在这里直接帮忙添加 std::string sourcepath; std::string url rq.GetUrl(); bool redirect false; if (url / homepage || url /)//如果用户给的是/或本来就是首页直接把首页返回给浏览器不可能把整个根目录下所有资源给用户 { sourcepath wwwroot / homepage;//首页路径 } else if (url /oldpakage.html)//若该网址搬迁自动跳转到新网站中 { sourcepath wwwroot / newpakage.html; redirect true; } else { sourcepath wwwroot url;//正常资源路径正常处理 } std::string content Getcontent(sourcepath);//获得响应正文 std::string response_line;// if (content.empty()) { //如果内容为空说明文件路径错误给用户返回404页面 std::string errpath wwwroot / notfound.html; content Getcontent(errpath); response_line HTTP/1.0 404 Notfound\r\n;//响应行直接硬编码后文相同 } else if (redirect)//如果前文发现用户要访问旧网址响应行给302状态码和描述 { response_line HTTP/1.0 302 Redirect\r\n; } else { response_line HTTP/1.0 200 OK\r\n; } std::cout content: content std::endl; //检查用户要访问的资源类型并构建Content_Type属性 std::string ContentTypeContent_Type: CheckType(url)\r\n; std::string blank_line \r\n; //组装响应 std::string response; response response_line; responseContentType; response Content-Length: std::to_string(content.size()) \r\n; response Connection: close\r\n; response Set-Cookie: ussrnamezzspasswd12345\r\n;//缓存设置 //检查重定向 if (redirect) { response Location: /newpakage\r\n; } response blank_line; response content; return response; }html文件这里省略如果要查看完整代码和文件详见HTTP的封装与应用https://gitee.com/zhang-zheshun/linux-learning-repository/tree/master/net_learning/HTTP