制作网站赚钱,海南省建设考试网站,wordpress数据库链接,免费的分销小程序#x1f941;作者#xff1a; 华丞臧. #x1f4d5;专栏#xff1a;【网络】 各位读者老爷如果觉得博主写的不错#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方#xff0c;欢迎在评论区指出。 推荐一款刷题网站 #x1f449; LeetCode刷题网站 文章… 作者 华丞臧. 专栏【网络】 各位读者老爷如果觉得博主写的不错请诸位多多支持(点赞收藏关注)。如果有错误的地方欢迎在评论区指出。 推荐一款刷题网站 LeetCode刷题网站 文章目录前言一、http协议1.1 认识URLhttp是做什么的urlencode和urldecode1.2 http格式http请求http响应1.3 http的方法1.4 http的状态码1.5 http常见Header二、http服务器2.1 最简单的http服务器2.2 简单的http服务器2.3 html的http服务器2.4 表单getpostget和post特点2.5 永久和临时重定向2.6 Cookie前言 在上一篇文章中我们见识了序列化和反序列化其是处于应用层的是进行网络通信双方进行一种约定在网络发展至今已经有了一些非常成熟的场景并且有大佬自定义的一些协议写的很好因此成为了应用层特定协议的标准 一、http协议
虽然我们说应用层协议是我们程序猿自己定的。但实际上已经有大佬们定义了一些现成的又非常好用的应用层协议供我们直接参考使用http(超文本传输协议)就是其中之一底层主流采用的是tcp协议http是无连接的。
1.1 认识URL
平时我们所说的网址就是URL。
服务器地址是域名域名就是字符串类型的字段域名在我们访问网网站的时候必须被转换称为IP地址访问网络服务必须具有网络号即端口号网络通信的本质就是socketIPport。 一般在使用确定协议的时候再url上面会缺省端口号在进行网络通信时端口号会被浏览器自动添加上所以浏览器访问指定url时浏览器必须给我们自动添加port浏览器就必须知道这些端口所以特定的众所周知的服务端口号都是并且必须是确定的。因此在前面学习端口号时我们说用户自己使用的套接字不能在0~1023。
http -- 80
https -- 443
sshd -- 22http是做什么的
http是以网页的形式呈现的比如说查阅文档、查看视频所以http是获取网页资源的视频、音频等也都是文件而网页也是一个.html文件。 http是向特定服务器申请特定资源的获取到本地进行展示或者某种使用的。 网页上的资源在该网页的网络服务器软件所在的服务器硬件上而服务器都是Linux资源文件存放在Linux服务器上用户在网页上申请资源时服务器需要根据路径找到该文件所以URL中需要添加资源文件的路径。/是Linux下的路径分隔符但是需要注意这个/不一定为根目录因为软件层面可以做一些处理比如在这个目录前添加一个路径。
urlencode和urldecode
像 / ? 等这样的字符已经被url当做特殊意义理解了因此这些字符不能随意出现。 比如, 某个参数中需要带有这些特殊字符就必须先对特殊字符进行转义。在进行网络传输进行编码时尤其是http中有些字段也会进行编码服务端进行解码。编码是浏览器做的解码是服务器做的。 转义的规则如下 将需要转码的字符转为16进制然后从右到左取4位(不足4位直接处理)每2位做一位前面加上%编码成%XY格式。
“” 被转义成了 “%2B” urldecode就是urlencode的逆过程 urlencode工具
网页的内容在开发者工具中可以看到如下图
1.2 http格式
http是基于行的协议。
http请求 首行[方法] [url] [版本]Header请求的属性 冒号分割的键值对每组属性之间使用\n分隔遇到空行表示Header部分结束 。Body空行后面的内容都是BodyBody允许为空字符串。如果Body存在则在Header中会有一个 Content-Length属性来标识Body的长度。
http响应 首行 [版本号] [状态码] [状态码解释]Header请求的属性冒号分割的键值对每组属性之间使用\n分隔;遇到空行表示Header部分结束。Body空行后面的内容都是BodyBody允许为空字符串。如果Body存在 则在Header中会有一个 Content-Length属性来标识Body的长度如果服务器返回了一个html页面那么html页面内容就是在 body中。
1.3 http的方法
方法说明支持的HTTP协议版本GET获取资源1.0、1.1POST传输实体主体1.0、1.1PUT传输文件1.0、1.1HEAD获取报文首部1.0、1.1DELETE删除文件1.0、1.1OPTIONS询问支持的方法1.1TRACE追踪路径1.1CONNECT要求用隧道协议链接代理1.1LINK建立和资源之间的联系1.0UNLINE断开连接关系1.0
其中最常用的就是GET方法和POST方法.
1.4 http的状态码
类别原因短语1XXInformational(信息状态码)接收的请求正在处理2XXSuccess(成功状态码)请求正常处理完毕3XXRedirection(重定向状态码)需要进行附加操作以完成请求4XXClient Error(客户端错误状态码)服务器无法处理请求5XXServer Error(服务器错误状态码)服务器处理请求出错
最常见的状态码, 比如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)
1.5 http常见Header
Content-Type: 数据类型(text/html等)Content-Length: Body的长度Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;User-Agent: 声明用户的操作系统和浏览器版本信息;referer: 当前页面是从哪个页面跳转过来的;location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问;Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;
二、http服务器
2.1 最简单的http服务器
使用tcp协议的服务器端在浏览器中可以连接该服务器浏览器上默认使用http协议进行连接。在前几篇文章中已经编写了tcp套接字这里只需要编写提供服务的代码即可。
#include iostream
#include stdio.h
#include strings.h
#include unistd.h
#include string.h
#include errno.h
#include pthread.h
#include netinet/in.h
#include arpa/inet.h
#include sys/types.h
#include sys/socket.h
#includesys/types.h
#includesys/wait.h
#include Log.hppusing namespace std;
volatile bool quitSer false;void Usage(void *vgs)
{std::cout Usage:./tcpserver port ip std::endl;
}
//提供服务
void Serverhttp(int sock)
{char buffer[10240];ssize_t s read(sock, buffer, sizeof buffer);cout s endl;if(s 0){logMessage(DEBUG, read success..);std::cout buffer;}
}class server
{
public:server(int port, std::string ip ): sockfd_(-1), ip_(ip), port_(port){}~server(){}public:void init(){// 1. 创建套接字sockfd_ socket(AF_INET, SOCK_STREAM, 0);if (sockfd_ 0){logMessage(FATAL, socket:%s[%d], strerror(errno), sockfd_);exit(SOCK_ERR);}logMessage(DEBUG, socket success..);// 2.填充域struct sockaddr_in local;bzero(local, sizeof(local));local.sin_family AF_INET;local.sin_port htons(port_);ip_.empty() ? (local.sin_addr.s_addr INADDR_ANY) : (inet_aton(ip_.c_str(), local.sin_addr));// 3. 绑定网络信息if (bind(sockfd_, (const struct sockaddr *)local, sizeof(local)) 0){logMessage(FATAL, bind:%s[%d], strerror(errno), sockfd_);exit(BIND_ERR);}logMessage(DEBUG, bind success...);// 4. 监听套接字if (listen(sockfd_, 5) 0){logMessage(FATAL, listen:%s[%d], strerror(errno), sockfd_);exit(LISTEN_ERR);}logMessage(DEBUG, listen success...);// 完成}void start(){char inbuffer_[1024]; // 用来接收客户端发来的消息// 提供服务while (true){quitSer false;struct sockaddr_in peer;socklen_t len sizeof(peer);// 5. 获取连接, accept 的返回值是一个新的socket fdlogMessage(DEBUG,start1);int serviceSock accept(sockfd_, (struct sockaddr *)peer, len);logMessage(DEBUG,start2);if (serviceSock 0){// 获取链接失败logMessage(WARINING, accept: %s[%d], strerror(errno), serviceSock);continue;}logMessage(DEBUG, accept success);pid_t id fork();if(id 0){pid_t tid fork();if(tid 0){uint16_t peerPort htons(peer.sin_port);std::string peerIp inet_ntoa(peer.sin_addr);Serverhttp(serviceSock);exit(0);}exit(0);}close(serviceSock);int ret waitpid(id, nullptr, 0);if(ret 0){logMessage(WARINING, wait child error:%d, errno);}logMessage(DEBUG, wait success...);}}private:int sockfd_;uint16_t port_;std::string ip_;
};// ./tcpserver port ip
int main(int argc, char *argv[])
{if (argc 2 || argc 3){Usage(argv[0]);exit(USAGE_ERR);}uint16_t port atoi(argv[1]);std::string ip;if (argc 3)ip argv[2];std::cout port : ip std::endl; server tcpSer(port, ip);tcpSer.init();tcpSer.start();return 0;
}服务端并没有提供服务所以访问服务器时网页显示如下图
在Linux服务端可以看到请求的相关属性如下图
2.2 简单的http服务器
服务器收到请求提供服务向对应的套接字文件中写入一个“hello world”注意响应的格式为首行headerbody其中header和body之间需要空一行。
void Serverhttp(int sock)
{char buffer[10240];ssize_t s read(sock, buffer, sizeof buffer);cout s endl;if(s 0){logMessage(DEBUG, read success..);std::cout buffer;}//开始响应string response;response HTTP/1.0 200 OK\r\n; //响应首行response \r\n; //空行response hello world!!; //正文send(sock, response.c_str(), response.size(), 0);
}服务器收到的请求报文如下图 浏览器收到的响应如下图 使用telnet可以查看服务器给用户的响应。
//安装telnet
sudo yum install -y telnet//使用格式
telnet ip port
//connected后
//出现escape character时使用ctrl}
//输入请求2.3 html的http服务器
作为服务端它并不关心html的信息也不需要管html的格式是什么服务器只负责网络请求并且把网页信息给用户推过去就行了所以实际上在进行网站开发时网页的资源都是通过html文件推送给用户的请求的文件在请求行中其第二个字段就是用户需要访问的文件如下图 其中/并不是根目录而是web根目录可以设置成为根目录。 //获取资源路径
string getPath(string in)
{size_t pos in.find(CRLF);string request in.substr(0, pos);cout request: request endl;// get path http/1.0size_t first request.find(SPACE); //第一个空格cout first: first endl;if(first string:: npos) return nullptr;size_t second request.rfind(SPACE); //第二个空格cout second: second endl;if(second string::npos) return nullptr;string path request.substr(first SPACE_LEN, second- (first SPACE_LEN));if(path.size() 1 path[0] /) path HOME_PAGE; //用户访问根目录就返回主页cout getPath: path endl;return path;
}
//获取资源
string readFile(string recource)
{ifstream in(recource);if(!in.is_open()) return 404;string content;string line;while(getline(in, line)) content line;return content;
}//提供服务
void Serverhttp(int sock)
{//std::cout peerIp : peerPort std::endl;char buffer[10240];ssize_t s read(sock, buffer, sizeof buffer);cout s endl;if(s 0){logMessage(DEBUG, read success..);std::cout buffer;}// 1. 获取路径string path getPath(buffer);string recource ROOT_PATH;recource path;cout recource endl;// 获取对应文件内容string html readFile(recource);// 开始响应string response;response HTTP/1.0 200 OK\r\n;response Content-Type:text/html\r\n;response (Content-Length: std::to_string(html.size()) \r\n);response \r\n;//response htmlh1hello world!!/h1html\r\n;response html;send(sock, response.c_str(), response.size(), 0);
}
关于html的编写方法可以参考菜鸟教程
//index.html
!DOCTYPE html
html
head
meta charsetutf-8 //
title华丞臧(runoob.com)/title //网页名称
/head
body //正文h1我的第一个标题/h1 //标题p我的第一个段落。/p
/body
/html在浏览器中访问服务器可以看到网页显示如下图所示 Liunx上收到的请求如下图左为服务器收到的请求右为客户端收到的响应。
2.4 表单
用户的网络行为无非有两种
用户将自己的远端资源拿到本地get /index.html http/1.1用户想将自己的属性字段提交到远端。
在浏览器上网页通常会让用户注册一个账号用来标识该用户用户的存放在远端的资源也可以使用该账号标识在html中这种端口称为表单。
get
在http中get会以明文方式将用户对应的参数信息拼接到url中。
!DOCTYPE html
html
head
meta charsetutf-8
title华丞臧(runoob.com)/title
/head
bodyh1hello world/h1p我的第一个段落。/pform actionhtml_form_action.php methodgetUsername: input typetext nameuserbrPassword: input typepassword namepasswdbrinput typesubmit valueSubmit/form
/body
/html表单形式如下图所示 post
在http中post会以明文方式将用户对应的参数信息拼接到正文中。
!DOCTYPE html
html
head
meta charsetutf-8
title华丞臧(runoob.com)/title
/head
bodyh1hello world/h1 p我的第一个段落。/pform actionhtml_form_action.php methodpostUsername: input typetext nameuserbr //用户名br标识换行Password: input typepassword namepasswdbr //密码input typesubmit valueSubmit/form
/body
/html表单形式如下图所示 在表单中填入用户名和密码后可以看到url中并没有拼接用户参数如下图 使用progress telerik fiddler可以查看用户发送的请求报文Raw可以看到用户参数被拼接到正文当中如下图 在服务端也可以收到用户发来的请求如下图
get和post特点
不难发现不管是get方法还是post方法都是不安全的都是以明文的方式传输如果有第三方来攻击用户无疑能非常简单的获取用户的数据与get方法相比post方法只是更为私密但还是明文传输不过post方法会将参数添加到请求的正文中因此当用户需要传输大资源的时候无疑使用post方法更加合适。
2.5 永久和临时重定向
301永久重定向。
void Serverhttp(int sock)
{char buffer[10240];ssize_t s read(sock, buffer, sizeof buffer);cout s endl;if(s 0){logMessage(DEBUG, read success..);std::cout buffer;}string response HTTP/1.1 301 Permanent redirect\r\n; //永久重定向response Location: https://blog.csdn.net/qq_59456417?spm1011.2415.3001.5343r\n;response \r\n;send(sock, response.c_str(), response.size(), 0);
}访问服务器结果如下
302临时重定向。
void Serverhttp(int sock)
{char buffer[10240];ssize_t s read(sock, buffer, sizeof buffer);cout s endl;if(s 0){logMessage(DEBUG, read success..);std::cout buffer;}string response HTTP/1.0 302 Temporary redirect\r\n; //临时重定向response Location: https://www.qq.com/r\n;response \r\n;send(sock, response.c_str(), response.size(), 0);
}访问服务器结果如下
临时重定向当客户端发起请求时服务端对请求不进行处理但是服务端会给可会端返回一个302状态码并且在属性字段还会返回一个Location字段客户端会根据这个字段进行自动跳转如果服务器只是做一个局部性的临时的升级或者短时间不能使用这时我们就可以进行临时重定向如果有用户访问就将临时重定向到对应的服务器上。。永久重定向当客户端发起请求时服务端对请求不进行处理但是服务端会给可会端返回一个301状态码并且在属性字段还会返回一个Location字段客户端会根据这个字段进行自动跳转如果服务器永久不再使用但是该服务器积累了一定的用户这时我们就可以搞一个同样的服务器进行永久重定向如果有用户访问就重定向到对应的服务器上。临时和永久重定向的区别在于客户端浏览器的处理对于临时重定向浏览器不会记录重定向的目标位置对于永久重定向浏览器会记录重定向的目标位置所以永久重定向访问一次后再次访问时浏览器就会直接访问记录的服务器注意并不是所有浏览器都会去做将永久重定向的网址更新。服务器的资源是否愿意被临时访问还是愿意永久访问决定是临时重定向还是永久重定向。
2.6 Cookie
http状态特点之一无状态用户各种资源的请求http协议不会记录也就是说http不知道用户访问服务器次数虽然http不会记录但http可以借助其他的手段来记录用户的访问。
为什么需要记录呢 因为用户需要会话保持网站中某些资源是需要进行用户认证的如果访问不被记录就会导致每次访问资源都需要进行用户认证比如在腾讯视频上每次访问vip影视都需要用户进行一次登录这显然是非常麻烦的的所以服务器需要保持用户会话。 我们可以登录一下bilibili如下所示点击网址左边的锁可以看到有一个CookieCookie是一种浏览器会话保持的策略。
在Cookie类表中可以找到以bilibili开头的 当你把Cookie中关于bilibili的全部删除下一次再进入bilibili时就需要重新登陆了。
Cookie内容
用户名密码到期时间地址等等
void Serverhttp(int sock)
{char buffer[10240];ssize_t s read(sock, buffer, sizeof buffer);cout s endl;if(s 0){logMessage(DEBUG, read success..);std::cout buffer;}// 1. 获取路径string path getPath(buffer);string recource ROOT_PATH;recource path;cout recource endl;string html readFile(recource);// 开始响应string response;response HTTP/1.0 200 OK\r\n;response Content-Type:text/html\r\n;response (Content-Length: std::to_string(html.size()) \r\n);response Set-Cookie: this is my Cookie content\r\n;response \r\n;response html;send(sock, response.c_str(), response.size(), 0);
}Cookie就是浏览器给用户维护的一个文件Cookie可以在磁盘上也可以在内存中内存中的Cookie将浏览器关闭后Cookie就会释放再次启动浏览器访问服务器时就需要重新登录。Cookie对用户的安全极大的影响第三方如果拿到用户的Cookie第三方就可以通过Cookie使用用户的身份登录服务器更严重的第三方还是以通过Cookie拿到用户的用户名和密码。
Cookie策略存在极大的安全隐患所以现在主流的方案不是Cookie而是Cookie session的方式。服务器将用户名和密码形成一个session文件这个文件的文件名session_id在服务器上具有唯一性服务器将session_id返回客户端客户端将session_id写入本地的Cookie后续访问都会通过session_id去访问服务器此时用户的用户名和密码就是存放在服务器上软件厂商来维护session和cookie大公司有充足的动力和能力来保护自己不受攻击并且还会有各种措施来防护。
补充
http-pipeline怎么请求的就按顺序来响应。