计算机网络---应用层以及HTTP协议

网络层是程序员接触最多的一个层级,应用层是层级体系中的最上层的一级,是我们做逻辑处理最多的。

  • 应用层的功能
  • 什么是url
  • http协议

应用层的功能
是程序员写的一个一个解决的实际的问题都是在应用层,是做逻辑运算和业务处理的地点。

什么是url
统一资源定位符。
包括了协议名称://用户名:密码@服务器地址:服务器端口/资源路径/查询字符串/片段标识符
https://blog.csdn.net/boke_fengwei
比如上面的一个网站就是一个url。
https:协议名称
bolg.csdn.net:就是域名,对应的就是IP地址
boke_fengwei:资源路径。
在上面的url中是包含一些特殊字符的,此时需要做出特殊的处理,采用编和解码。这是为了防止数据的二义性。
采用urlencode编码
转移规则:将数据转为16进制,然后从右到左,取4位(不足四位的时候直接处理),没两位作以为,前面加上%,编码成%XY的形式即可。%就是标识这是一个转义的url,在对端的时候就可以进行解码。
有兴趣的童鞋可以看看urlencode和urldecode的实现函数

HTTP协议
HTTP的请求格式
在这里插入图片描述

请求行

  • 常用的请求方法
    GET:获取资源
    POST:传输实体主体
    PUT:传输文件
    HEAD:获得报文首部
    OPTIONS:询问支持的方法
    DELETE:删除文件
    LINK:建立和资源之间的联系
    最常用的就是GET和POST
    GET所提交的内容在url中,POST体检的内容在请求体中。
  • 协议版本

HTTP1.1默认使用长连接,可有效减少TCP的三次握手开销。 HTTP
1.1支持只发送header信息(不带任何body信息),如果服务器认为客户端有权限请求服务器,则返回100,否则返回401。客户端如果接受到100,才开始把请求body发送到服务器。这样当服务器返回401的时候,客户端就可以不用发送请求body了,节约了带宽。另外HTTP还支持传送内容的一部分。这样当客户端已经有一部分的资源后,只需要跟服务器请求另外的部分资源即可。这是支持文件断点续传的基础。
HTTP1.0是没有host域的,HTTP1.1才支持这个参数。
HTTP2.0使用多路复用技术(Multiplexing),多路复用允许同时通过单一的 HTTP/2 连接发起多重的请求-响应消息。
“HTTP1.1在同一时间对于同一个域名的请求数量有限制,超过限制就会阻塞请求”。多路复用底层采用"增加二进制分帧层"的方法,使得不改变原来的语义、首部字段的情况下提高传输性能,降低延迟。
二进制分帧将所有传输信息分割为更小的帧,用二进制进行编码,多个请求都在同一个TCP连接上完成,可以承载任意数量的双向数据流。HTTP/2更有效的使用TCP连接,得到性能上的提升。

请求头

  • 常用的头部信息
    (1)Content-Type:数据类型(text/html)等等
    (2)Content-Length:Body的长度
    (3)Host:客户端告知服务器,所请求的资源在哪个主机的那个端口上
    (4)User-Agent:声明用户的操作系统和浏览器的版本信息
    (5)referer:当前页面是从那个页面跳转过来的
    (6)location:转发信息
    (7)Cookie:用户客户端存储少量的信息
    空行:“\r\n”空行是必不可少的,是区分请求头和请求体的一个重要标志

HTTP的响应格式
在这里插入图片描述

响应行

  • 协议版本:和请求行中的协议版本一样
  • 状态码:是表示我们请求之后返回的状态,标志我们在请求之后所造的事情
    1XX:信息性状态码
    2XX:成功状态码
    3XX:重定向状态码
    4XX:客户端错误状态码
    5XX:服务器处理请求错误
    常见的状态码和状态码解释
    200 OK:标志我们访问成功
    404 NOT FOUND:标志我们没有查找到内容
    403 FORBIDDEN:标志着被静止
    302 REDIRECT:重定向。
    504 BADGATEWAY:网关坏了

响应头:和请求信息中是相似的
空行:空行的作用也是标志响应头的结束,区分响应头和响应体
响应体:响应之后返回的内容。

实现一个简单的HTTP的服务器。就是在在访问我们的IP地址的时候显示一个hello word的语句。
连接上我们之前实现的tcp的c++头文件

扫描二维码关注公众号,回复: 6634669 查看本文章
  1 #include <string>
  2 #include <stdio.h>
  3 #include <unistd.h>
  4 #include <stdlib.h>
  5 #include <errno.h>
  6 #include <string.h>
  7 #include <netinet/in.h>
  8 #include <arpa/inet.h>
  9 #include <sys/socket.h>
 10 
 11 #define CHECK_RET(q) if((q) == false){return -1;}
 12 
 13 typedef struct calculator_info_t{
 14     int num1;
 15     int num2;
 16     char op;
 17 }calculator_info;
 18 
 19 class TcpSocket
 20 {
 21     public:
 22         TcpSocket() : _sockfd(-1){
 23         }
 24         void SetSockFd(int fd){
 25             _sockfd = fd;
 26         }
 27         int GetSockFd(){
 28             return _sockfd;
 29         }
 30         bool Socket() {
 31             _sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 32             if (_sockfd < 0) {
 33                 perror("socket error");
 34                 return false;
 35             }
 36             return true;
 37         }
 38         bool Bind(std::string &ip, uint16_t port){
 39             struct sockaddr_in addr;
 40             addr.sin_family = AF_INET;
 41             addr.sin_port = htons(port);
 42             addr.sin_addr.s_addr = inet_addr(ip.c_str());
 43 
 44             socklen_t len = sizeof(struct sockaddr_in);
 45             int ret = bind(_sockfd, (struct sockaddr*)&addr, len);
 46             if (ret < 0) {
 47                 perror("bind error");
 48                 return false;
 49             }
 50             return true;
 51         }
 52         bool Listen(int backlog = 10) {
 53             //int listen(int sockfd, int backlog);
 54             //backlog:最大并发连接数--内核中已完成连接队列的最大节点数
 55             int ret = listen(_sockfd, backlog);
 56             if (ret < 0) {
 57                 perror("listen error");
 58                 return false;
 59             }
 60             return  true;
 61         }
 62         bool Connect(std::string &ip, uint16_t port) {
 63             //int connect(int fd, struct sockaddr *addr,socklen_t len);
 64             //addr: 要连接的服务端地址信息
 65             struct sockaddr_in addr;
 66             addr.sin_family = AF_INET;
 67             addr.sin_port = htons(port);
 68             addr.sin_addr.s_addr = inet_addr(ip.c_str());
 69             socklen_t len = sizeof(struct sockaddr_in);
 70 
 71             int ret = connect(_sockfd, (struct sockaddr*)&addr, len);
 72             if (ret < 0) {
 73                 perror("connect error");
 74                 return false;
 75             }
 76             return true;
 77         }
 78         bool Accept(TcpSocket &csock, struct sockaddr_in *addr = NULL){
 79             //int accept(int sockfd, sockaddr *addr, socklen_t *len);
 80             //addr: 客户端的地址信息
 81             //len: 输入输出参数,既要指定接收长度,还要接收实际长度
 82             //返回值:为新客户端新建的socket套接字描述符
 83             //通过这个返回的描述符可以与指定的客户端进行通信
 84             struct sockaddr_in _addr;
 85             socklen_t len = sizeof(struct sockaddr_in);
 86             int newfd = accept(_sockfd, (struct sockaddr*)&_addr, &len);
 87             if (newfd < 0) {
 88                 perror("accept error");
 89                 return false;
 90             }
 91             if (addr != NULL) {
 92                 memcpy(addr, &_addr, len);
 93             }
 94             csock.SetSockFd(newfd);
 95             //_sockfd--仅用于接收新客户端连接请求
 96             //newfd----专门用于与客户端进行通信
 97             return true;
 98         }
 99         bool Recv(std::string &buf) {
100             char tmp[4096] = {0};
101             //ssize_t recv(int sockfd, void *buf, size_t len, int flags)
102             //flags:0-默认阻塞接收  MSG_PEEK-获取数据但是不从缓冲区移除
103             //返回值:实际接收的数据长度    失败:-1    连接断开:0
104             int ret = recv(_sockfd, tmp, 4096, 0);
105             if (ret < 0) {
106                 perror("recv error");
107                 return false;
108             }else if (ret == 0) {
109                 printf("peer shutdown\n");
110                 return false;
111             }
112             buf.assign(tmp, ret);
113             return true;
114         }
115         bool Send(std::string &buf) {
116             //ssize_t send(int sockfd, void *buf, size_t len, int flags)
117             int ret = send(_sockfd, buf.c_str(), buf.size(), 0);
118             if (ret < 0) {
119                 perror("send error");
120                 return false;
121             }
122             return true;
123         }
124         bool Close() {
125             close(_sockfd);
126             _sockfd = -1;
127         }
128     private:
129         int _sockfd;
130 };  

实现cpp文件

  1 #include "tcpsocket.hpp"
  2 #include <iostream>
  3 #include <sstream>
  4 int main(int argc,char* argv[]){
  5     if(argc != 3){
  6         std::cout<<"./http_server ip port"<<std::endl;
  7         return -1;
  8     }
  9     std::string ip = argv[1];
 10     uint16_t port = atoi(argv[2]);
 11     TcpSocket sock;
 12     //1.创建套接字
 13     CHECK_RET(sock.Socket());
 14     //2.绑定连接
 15     CHECK_RET(sock.Bind(ip,port));
 16     //设置监听
 17     CHECK_RET(sock.Listen(10));
 18     //开始通信
 19     while(1){
 20         //首先创建一个sockaddr_in
 21         struct sockaddr_in client;
 22         TcpSocket cli;
 23         if(sock.Accept(cli,&client) == false){
 24             continue;
 25         }
 26         //接受连接请求
 27         std::string buf;
 28         cli.Recv(buf);
 29         std::cout<<"接受到的数据["<<buf<<"]"<<std::endl;
 30         //发送一个简单的页面请求
 31         //使用一个数据流来格式化数据
 32         std::string body;
 33         body = "<html><body><h1>hello world</h1></body></html>";
 34         std::stringstream ss;
 35         ss << "http/1.1 200 OK\r\n";//响应行
 36         ss << "Content_Length: "<<body.size()<<"\r\n";
 37         ss << "Content_Type: text/html"<<"\r\n";
 38         ss << "Location: http://www.baidu.com\r\n";
 39         ss<<"\r\n";
 40         std::string header = ss.str();
 41         cli.Send(header);
 42         cli.Send(body);
 43         cli.Close();
 44     }
 45     sock.Close();
 46     return 0;
 47 } 

猜你喜欢

转载自blog.csdn.net/boke_fengwei/article/details/91040424