[TCP/IP]:应用层(HTTP协议)

南楼画角 提交于 2020-02-24 04:11:07

1.HTTP是什么

HTTP即HypertText TranSport Protocol(超文本传输协议),建立在TCP和IP协议之上。

1.1 HTTP/0.9 - 单线协议

TTP的初始版本没有版本号; 它后来被称为0.9来区分它和更高版本。HTTP / 0.9非常简单:请求由一行代码组成,并从唯一可能的方法开始,GET然后是资源路径(不是URL,因为连接到服务器后不需要协议,服务器和端口)。

1.2 HTTP/1.0 - 构建扩展性

HTTP/0.9的功能非常有限,所以HTTP/1.0多了以下特性:

  • 版本信息在每个请求中发送。
  • 状态码行也在响应开始时发送,允许浏览器自己了解请求的成功或失败并调整其行为。
  • 已经引入了HTTP标头的概念,包括请求和响应,允许传输元数据并使协议非常灵活和可扩展。
  • 在新的HTTP标题的帮助下,增加了传输其他文档而不是纯HTML文件的功能。

1.2 HTTP/1.1 - 标准化协议

HTTP/1.1在1.0上做出了很多改进的地方:

  • 连接可以重复使用,节省重新打开多次的时间,以显示嵌入到检索到的单个原始文档中的资源。
  • 流水线已添加,允许在第一个请求的答案完全传输之前发送第二个请求,从而降低通信延迟。
  • 分块响应现在也支持。
  • 额外的缓存控制机制已经被引入。
  • 内容协商(包括语言,编码或类型)已经引入,并允许客户和服务器就最适合交换的内容达成一致。

2.HTTP协议格式

2.1 请求格式

在这里插入图片描述

可以看到,请求格式分为首行,头部,正文。

  • 首行:方法+URL+状态码。
  • 头部:请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束。
  • 正文:空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个 Content-Length属性来标识Body的长度。

2.2 响应格式

在这里插入图片描述

和请求格式一样,响应格式也分为首行+头部+正文。

  • 首行: [版本号] + [状态码] + [状态码解释] 。
  • 头部: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束。
  • 正文: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个 Content-Length属性来标识Body的长度; 如果服务器返回了一个html页面, 那么html页面内容就是在 body中。

2.3常见的头部信息

Content-Type: 数据类型(text/html等) 。
Content-Length: Body的长度。
Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上。
User-Agent: 声明用户的操作系统和浏览器版本信息。
referer: 当前页面是从哪个页面跳转过来的。
location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问。
Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能。

3.URL是什么

URL — Uniform Resource Location统一资源定位符
例如这个url:
https://cloud.tencent.com/developer/section/1189853
https是协议,cloud.tencent.com是域名,后面的是查询路径,
再来看下面这个url:
在这里插入图片描述

注意?后面的内容,是提供给 Web 服务器的额外参数。这些参数是用&符号分隔的键/值对列表。在将资源返回给用户之前,Web 服务器可以使用这些参数来执行额外的工作。每个 Web 服务器都有自己的参数规则,知道特定 Web 服务器如何处理参数的唯一可靠方法是询问 Web 服务器所有者。

4.简单的http服务器实现

#include<iostream>
#include<string>
#include<unistd.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/types.h>
#include <arpa/inet.h>
#define CHECK_RET(q) if((q) == false){return -1;}
class Tcpsocket{
    public:
        Tcpsocket():_sockfd(-1){}
        ~Tcpsocket(){Close();}
        bool Sock(){
            _sockfd = socket(AF_INET,SOCK_STREAM,0);
            if(_sockfd < 0){
              std::cerr << "create error\n";
                return false;
            }
            return true;
        }
        bool Bind(const std::string& ip,uint16_t port){
            struct sockaddr_in server_socket;
            server_socket.sin_family = AF_INET;
            server_socket.sin_port = htons(port);
            server_socket.sin_addr.s_addr = inet_addr(&ip[0]);
            int ret = bind(_sockfd,(struct sockaddr*)& server_socket,sizeof(sockaddr_in));
            if(ret < 0){
              std::cerr << "bind error\n";
                return false;
            }
            return true;
        }
        bool Listen(int BACKLOGS = 5){
            int ret = listen(_sockfd,BACKLOGS); 
            if(ret < 0){
              std::cerr << "listen error\n";
                return false;
            }
            return true;
        }
        void SetFd(int fd){
            _sockfd = fd;
        }
        bool Accept(Tcpsocket& cli_sock){
            struct sockaddr_in addr;
            socklen_t len = sizeof(struct sockaddr_in);
            int fd = accept(_sockfd,(struct sockaddr*)&addr,&len);
            if(fd < 0){
                std::cerr << "accept error\n";
                return false;
            }
            cli_sock.SetFd(fd);
            return true;
        }
        bool Connect(std::string& ip,uint16_t port){
            int ret;
            struct sockaddr_in client_socket;
            client_socket.sin_family = AF_INET;
            client_socket.sin_port = htons(port);
            client_socket.sin_addr.s_addr = inet_addr(&ip[0]);
            socklen_t len = sizeof(struct sockaddr_in);
            ret = connect(_sockfd,(struct sockaddr*)& client_socket,len);
            if(ret < 0) {
                std::cerr << "connect error\n";
                return false;
            }
            return true;
        }
        bool Send(std::string buff){
            int ret = send(_sockfd,&buff[0],buff.size(),0);
            if(ret < 0){
                std::cerr << "send error\n";
                return false;
            }
            return true;
        }
        bool Recv(std::string buff){
            char tmp[4096] = {0};
            int ret = recv(_sockfd,tmp,4096,0);
            if(ret < 0){
                std::cerr << "recv error\n";
                return false;
            }else if(ret == 0){           //recv返回值为0表示连接已断开
                std::cerr << "peer shutdown\n";
                return false;
            }
            buff.assign(tmp,ret);
            return true;
        }
        bool Close(){
            if(_sockfd >= 0){
                close(_sockfd);
                _sockfd = -1;
            }
        }
  private:
      int _sockfd;
};
© 2020 GitHub, Inc.
#include"tcpsocket.hpp"
#include<sstream>
int main(int argc, char* argv[])
{
    Tcpsocket sock;
    CHECK_RET(sock.Sock());
    CHECK_RET(sock.Bind("0.0.0.0",9000));
    CHECK_RET(sock.Listen());
    while(1)
    {
        Tcpsocket clisock;
        if(sock.Accept(clisock) == false){
            continue;
        }
        std::string buff;
        clisock.Recv(buff);
        std::cout << "req:[" << buff << "]\n";
        std::string body = "<html><body><h1>hello</h1></body></html>";
        std::string first = "HTTP/1.1 200 OK\r\n";
        std::string head;
        std::stringstream ss;
        ss << "Content-Length: " << body.size() << "\r\n";
        head = ss.str();
        std::string blank = "\r\n";
        clisock.Send(first);
        clisock.Send(head);
        clisock.Send(blank);
        clisock.Send(body);
        clisock.Close();
    }
    sock.Close();
    return 0;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!