@[TOC](全文概要:) ![>在这里插入图片描述](https://img-blog.csdnimg.cn/20210609205049405.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MjI3MDIyMw==,size_16,color_FFFFFF,t_70) # 1、HTTP概念: - **HTTP协议**是`Hyper Text Transfer Protocol`(超文本传输协议)的缩写,是一个简单的请求-响应协议,是用于从万维网(WWW:World Wide Web)服务器传输超文本到本地浏览器的传送协议; - HTTP是基于TCP/IP通信协议来传递数据; >TCP不熟悉的可以看看,这个|: > >——————————————下面连接: [TCP看了都会系列,点我。](https://blog.csdn.net/weixin_52270223/article/details/117599003?spm=1001.2014.3001.5501) ——————————————上面连接| - HTTP默认端口号为80,但是也可以改为其他端口; >**https就是对http进行了加密,提升了安全性;** # 2、HTTP协议格式: ### 2.0、文字描述: http协议格式:http数据结构、http协议实现 首行:请求行、响应行(对于请求与响应的关键描述) 头部:对于请求或者响应或者正文的描述; 由一个个键值对组成key: val,每个键值对以\r\n结尾; 空行:\r\n,间隔头部与正文;\r\n\r\n——头部结尾; 正文:客户端提交给服务端,或者服务端响应给客户端的数据; ### 2.0、结构图说明: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210609183506184.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MjI3MDIyMw==,size_16,color_FFFFFF,t_70) ### 2.0、举例说明(图): >**下图备注:,为使图片内容可以说明情况即可,头部和正文有部分被删除;** ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210609184808199.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MjI3MDIyMw==,size_16,color_FFFFFF,t_70) ## 2.1、首行-请求行(请求方法 ,URL(URI),协议版本\r\n) ### 2.1.1、请求方法:——对请求的描述 >`GET`:从服务区获取实体资源,请求没有正文,但是也可以提交数据,但是提交的数据没有在正文中而是在URL中; **1.GET提交数据是不安全。 2.URL长度有限制。(4kb)数据有限制,正文没有限制;** `HEAD`:功能与GET类似,但是不要正文实体; `POST`:向服务端提交数据;数据在正文中; ... - HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD方法。 - HTTP1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210609194409846.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MjI3MDIyMw==,size_16,color_FFFFFF,t_70) ### 2.1.2、URL:——网址--同一资源定位符--用于定位网络中某个主机上的某个资源 (URI:同一资源标识符) >**组成**:协议名称://用户名:密码@域名:端口/资源路径?查询字符串 `https://user:pass@www.baidu.com:80/s?ie=utf-8&wd=ASCII#ch` 域名:服务器别名--最终访问服务器需要经过域名解析得到服务器IP。 `/`资源路径:这个路径是一个相对根目录。 `?`查询字符串:提交给服务器的数据,由一个个key=val形式键值对组成,键值对之间以`&`符号间隔; `#`跳转到`ch`字符串处; - urlencode:编码--用户请求的资源路径,或者查询或者查询字符串中存在特殊字符,则有可能与url中的特殊字符冲突; - urldecode:解码遇到%则认为紧随其后的两个字符进行了编码,将这两个字符转换为数字,第一个数字左移4位加上第二个数字。 ### 2.1.3、协议版本:0.9 ,1.0 ,1.1 ,2.0 > **0.9**:最早期的版本,只支持GET方法,并且协议还没有当前的规范,只支持超文本数据传输。 > > **1.0**:规范了http协议格式,并且新增支持GET、HEAD、POST请求方法,支持各种多媒体资源传输,简单的缓存控制。 **1.1**:更多的是对1.0版本进行性能的优化,支持了更多请求方法以及特性(支持长连接,更加完善缓存控制,分块传输...) **2.0**:因为http协议的庞大冗余,因此2.0不是新增特性,而是重定义http协议; (1).使用二进制数据传输; (2).支持主动推送; (3).多路复用服务器进行长连接响应,不需要按序进行;(那个准备好发那个,无顺序); - 0.9只支持GET方法; - 1.0规范了协议格式,支持了更多请求方法,支持多媒体资源传输;但是对性能没有更多改进,因此 - 1.1针对传输性能进行大量改进,比如支持长连接,更加完善缓存控制。。。。但是依然存在缺陷,比如管线化传输的队头阻塞问题; - 2.0因为1.1过于冗余庞大,因此不适合新增而重新定义,2.0采用二进制数据传输,支持服务器推送依赖数据,长连接响应无需按序,解决了队头阻塞问题; Connection:用于控制长连接的关闭打开状态close/keep-alive; ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210608220630411.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MjI3MDIyMw==,size_16,color_FFFFFF,t_70) ## 2.2、首行—响应行:协议版本、响应状态码、状态码描述\r\n ### 2.2.1、协议版本(见2.1.3) ### 2.2.2、响应状态码——直观向客户端反馈处理结果 1xx:一些描述信息;101-协议切换状态码 2xx:表示本次请求正确处理;200 3xx:重定向-表示本次请求的资源移动到了新的连接处,但是原链接依然可用。 www.image.com/img/flower.jpg www.image.com/img/flower/rose.jpg 301 302 4xx:表示客户端错误;404 5xx:表示服务器错误;502-代理服务器未收到正确响应;504-超时 [HTTP响应状态码](https://www.runoob.com/http/http-status-codes.html) ### 2.2.3、状态码描述——就是状态码的文字描述; ## 2.3、头部——关于请求或响应或者正文的关键描述字段; > 组成: > key: val\r\n > key: val\r\n > ... >典型的头部字段: `Connection`:长短连接控制:keep-alive/close; Referer:记录本次请求的来源链接; `Content-Type`:用于表示正文数据格式; `Content-Length`:用于表示正文的长度——http解决粘包问题 `Location`:用于指定重定向的新连接地址,与`3xx`搭配使用 `Cookie`与`session`:涉及的头部字段请求头Cookie,响应头Set-Cookie .... ... ## 2.4、空行:\r\n 是头部最后一个字段的结尾\r\n组成连续的\r\n\r\n作为特殊标志,作为http头部结束的标志; ## 2.5、正文; 完; # 3、http协议是一个无状态协议 1.一个客户端登录之后,服务端验证登录,成功后,通过Set-Cookie字段设置Cookie信息(用户信息、状态。。。)返回给客户端 2.客户端收到响应后,将Set-Cookie字段的Cookie信息保存下来,下次请求从Cookie文件中读取Cookie信息,通过Cookie字段发送给服务器; Cookie是一个维护http通信状态的技术——但是存在安全隐患 解决方案:session session是服务端针对每个客户端所建立的会话,当客户端登录成功后,创建会话,在会画中记录客户端用户信息以及状态。。。,通过Set-Cookie字段将session_id返回给给客户端; 用户的隐私信息一直保存在服务器防止泄露; >Cookie和session的区别: Cookie是维护http通信转状态的技术,将关键信息保存在客户端,每次请求服务器时,读取出来发送给客户端(存在安全隐患) session是解决Cookie安全隐患的技术,将关键信息保存在服务器,将session_id发送给客户端,作为Cookie保存起来,往后请求传输session_id即可,解决了Cookie泄密的风险; # 4、写一个HTTP的简单协议: - http是一个应用层协议,只是应用程序如何沟通的一种数据格式约定,在传输层是基于tcp实现的; - http客户端实际上就是一个tcp客户端,http服务器实际上就是一个tcp服务器只不过http客户端与服务端的通信用的是http协议来约定数据格式而已; ## 4.1、简单的http服务器搭建: 1.搭建tcp服务端 2.获取新建连接; 3.使用新建连接,等待接受数据(http协议的请求数据) 4.接受过程:先接收http头部,解析头部-Content-Length确定正文长度; 5.接受指定长度的正文; 6.根据请求方法以及资源路径确定客户端的请求目的 7.进行具体对应的业务处理 8.组织http协议格式的响应数据,对客户端进行回复; 9.如果是短连接,则直接关闭套接字,如果是长连接,则继续等待接受数据 ```cpp //回复数据: Hello Me ``` ## 4.2、代码实现: 实现后运行代码+ip+端口号 然后在网页中直接搜索ip和端口,即可看到效果; ```cpp //头文件在底下: 1 #include"tcpsocket.hpp" 2 3 int main(int argc,char *argv[]){ 4 //通过程序运行参数指定服务端要绑定的地址信息 5 // ./tcp_srv 192.168.106.133 9050 6 if(argc!=3){ 7 std::cout<<"usage: ./tcp_srv 192.168.106.133 9050"<**头文件**:`tcpsocket.hpp` 头文件用tcp,具体看一看: [TCP具体了解 ](https://blog.csdn.net/weixin_52270223/article/details/117599003?spm=1001.2014.3001.5501) ```cpp #include #include #include #include #include #include #include #define CHECK_RET(q) if((q)==false){return -1;} #define LISTEN_BACKLOG 5 class TcpSocket{ private: int _sockfd; public: TcpSocket():_sockfd(-1){} bool Socket() { _sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if (_sockfd < 0) { perror("socket error"); return false; } return true; } bool Bind(const std::string &ip, const uint16_t port){ sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(&ip[0]); socklen_t len = sizeof(sockaddr_in); int ret = bind(_sockfd, (sockaddr*)&addr, len); if (ret < 0) { perror("bind error"); return false; } return true; } bool Listen(int backlog = LISTEN_BACKLOG) { //listen(描述符,同一时间连接数) int ret = listen(_sockfd, backlog); if (ret < 0) { perror("listen error"); return false; } return true; } bool Connect(const std::string &ip,const int port) { sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(&ip[0]); socklen_t len = sizeof(sockaddr_in); //connect(描述符,服务端地址, 地址长度) int ret = connect(_sockfd, (sockaddr*)&addr, len); if (ret < 0) { perror("connect error"); return false; } return true; } bool Accept(TcpSocket *sock, std::string *ip = NULL, uint16_t *port = NULL) { //int accept(监听套接字, 回去客户端地址, 长度) sockaddr_in addr; socklen_t len = sizeof(sockaddr_in); int newfd = accept(_sockfd,(sockaddr*)&addr,&len); if (newfd < 0) { perror("accept error"); return false; } sock->_sockfd = newfd; if (ip != NULL) { *ip = inet_ntoa(addr.sin_addr); } if (port != NULL) { *port = ntohs(addr.sin_port); } return true; } bool Recv(std::string *buf) { //int recv(描述符,空间,数据长度,标志位) //返回值:实际获取大小, 0-连接断开; -1-出错了 char tmp[4096] = {0}; int ret = recv(_sockfd, tmp, 4096, 0); if (ret < 0) { perror("recv error"); return false; }else if (ret == 0) { printf("peer shutdown"); return false; } buf->assign(tmp, ret); return true; } bool Send(const std::string &data) { //int send(描述符,数据,长度,标志位) int total = 0; while(total < data.size()) { int ret = send(_sockfd, &data[0] + total, data.size() - total, 0); if (ret < 0) { perror("send error"); return false; } total += ret; } return true; } bool Close() { if (_sockfd != -1) { close(_sockfd); } return true; } }; ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210609205456910.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MjI3MDIyMw==,size_16,color_FFFFFF,t_70)