网站建设数据中心,汉中网站建设电话,庆元县建设局网站,wordpress外网访问错误一、HTTP协议简单介绍
1.1 什么是HTTP协议 HTTP#xff08;超文本传输协议#xff09;是一种用于在Web浏览器和Web服务器之间传输数据的应用层协议。它是一种无状态协议#xff0c;即服务器不会保留与客户端的任何连接状态信息#xff0c;每个请求都被视为一个独立的事务。…一、HTTP协议简单介绍
1.1 什么是HTTP协议 HTTP超文本传输协议是一种用于在Web浏览器和Web服务器之间传输数据的应用层协议。它是一种无状态协议即服务器不会保留与客户端的任何连接状态信息每个请求都被视为一个独立的事务。 假设你使用Web浏览器例如Chrome访问一个网页。当你在浏览器中输入网址并按下Enter键时浏览器会向服务器发送一个HTTP请求。你也可以理解为HTTP协议是在客户端浏览器和服务器之间传输数据的基础约定。 1.2 再次理解协议 协议是指在通信过程中参与方之间所达成的一种约定或规范。在网络通信中协议是用来定义数据传输规则和通信方式的一组规范。 具体来说基于HTTP协议它定义了客户端例如Web浏览器和服务器之间进行通信时所需遵循的规范。 HTTP协议主要包含以下几个方面的规定 请求方式HTTP协议定义了一系列的请求方法如GET、POST、PUT、DELETE等用于告知服务器进行何种操作。 请求和响应格式HTTP协议规定了请求消息和响应消息的格式。请求消息由请求行、请求头部和请求正文组成而响应消息由状态行、响应头部和响应正文组成。 状态码HTTP协议定义了一系列的状态码用于表示服务器对请求的处理结果。例如200表示成功、404表示资源未找到、500表示服务器内部错误等。 头部信息HTTP协议通过头部字段来携带各种元数据例如Content-Type用于指示请求或响应的数据类型Content-Length表示消息正文的长度等。 连接管理HTTP协议还定义了一些机制用于管理连接如持久连接keep-alive允许多个请求和响应复用同一个TCP连接以减少连接建立的开销。
二、HTTP请求
2.1 HTTP的工作过程 我们不妨先来了解一下HTTP的工作过程。当你在浏览器中输入一个网址并按下Enter键时浏览器就会向服务器发送一个HTTP请求。请求时浏览器会给服务器发送请求报文。当服务器收到请求后它会根据请求报文进行相应的处理并生成一个HTTP响应响应报文返回给浏览器。一个请求再加一个回应就完成了客户端与服务器的数据传输与交互。 上述讲述的都是概念。下面我们结合一段代码来理解。在看代码之前强调一下HTTP 是一种应用层协议是基于 TCP/IP 通信协议来传递数据的。具体也可看下图 demo代码 首先我们需要基于套接字实现一个服务端HttpServer.hpp #include iostream
#include signal.h
#include Sock.hppclass HttpServer
{
public:using func_t std::functionvoid(int);private:Sock _serverSock;int _sock;std::string _ip;uint16_t _port;func_t _func;public:HttpServer(uint16_t port, func_t func, std::string ip 0.0.0.0):_port(port),_func(func),_ip(ip){_sock _serverSock.Socket();_serverSock.Bind(_sock, _port, _ip);_serverSock.Listen(_sock);}void start(){signal(SIGCHLD, SIG_IGN);while(true){std::string clientIP;uint16_t clientPort 0;int sockfd _serverSock.Accept(_sock, clientIP, clientPort);if(sockfd 0)continue;if(fork() 0){close(_sock);_func(sockfd);close(sockfd);exit(0);}close(sockfd);}}~HttpServer(){if(_sock 0) close(_sock);}
}; 下面是对套接字操作的封装代码Sock.hpp
#pragma once#include iostream
#include string
#include cstring
#include cerrno
#include cassert
#include unistd.h
#include memory
#include sys/types.h
#include sys/socket.h
#include arpa/inet.h
#include netinet/in.h
#include ctype.h
#include LogTest.hppclass Sock
{
private:const static int gbacklog 20;public:Sock() {}int Socket(){int listensock socket(AF_INET, SOCK_STREAM, 0);if (listensock 0){LogMessage(FATAL, create socket error, %d:%s, errno, strerror(errno));exit(2);}LogMessage(NORMAL, create socket success, listensock: %d, listensock);return listensock;}void Bind(int sock, uint16_t port, std::string ip 0.0.0.0){struct sockaddr_in local;memset(local, 0, sizeof local);local.sin_family AF_INET;local.sin_port htons(port);inet_pton(AF_INET, ip.c_str(), local.sin_addr);if (bind(sock, (struct sockaddr *)local, sizeof(local)) 0){LogMessage(FATAL, bind error, %d:%s, errno, strerror(errno));exit(3);}}void Listen(int sock){if (listen(sock, gbacklog) 0){LogMessage(FATAL, listen error, %d:%s, errno, strerror(errno));exit(4);}LogMessage(NORMAL, init server success);}// 一般经验// const std::string : 输入型参数// std::string *: 输出型参数// std::string : 输入输出型参数int Accept(int listensock, std::string *ip, uint16_t *port){struct sockaddr_in src;socklen_t len sizeof(src);int servicesock accept(listensock, (struct sockaddr *)src, len);if (servicesock 0){LogMessage(ERROR, accept error, %d:%s, errno, strerror(errno));return -1;}if(port) *port ntohs(src.sin_port);if(ip) *ip inet_ntoa(src.sin_addr);return servicesock;}bool Connect(int sock, const std::string server_ip, const uint16_t server_port){struct sockaddr_in server;memset(server, 0, sizeof(server));server.sin_family AF_INET;server.sin_port htons(server_port);server.sin_addr.s_addr inet_addr(server_ip.c_str());if(connect(sock, (struct sockaddr*)server, sizeof(server)) 0) return true;else return false;}~Sock() {}
}; 下面我们要做的就是启动服务器然后用Web浏览器访问我们所启动的服务器这时候是浏览器向我们所写的服务器发送请求。根据上述HTTP协议的工作过程这时候会像服务器发送一个请求报文。我们启动服务器HTTPServer.cc #include iostream
#include memory
#include vector
#include fstream#include Util.hpp
#include HttpServer.hppvoid Usage(std::string name)
{std::cout \nUsage : name Port\n std::endl;
}void HandlerHttpRequest(int sockfd)
{// 1. 读取请求 for testchar buffer[10240];ssize_t s recv(sockfd, buffer, sizeof(buffer) - 1, 0);if (s 0){buffer[s] 0;std::cout buffer --------------------\n std::endl;}}int main(int argc, char* argv[])
{if(argc ! 2){Usage(argv[0]);exit(0);}std::unique_ptrHttpServer httpServer(new HttpServer(atoi(argv[1]), HandlerHttpRequest));httpServer-start();return 0;
}运行结果 我们看到确实我们所写的服务器发送了一些信息。该信息就是请求报文。但发现无法打开此页面是因为我们并没有向浏览器发送任何响应数据。接下来我们详细了解一下HTTP的请求。
2.2 URL介绍 URLUniform Resource Locator是用于标识和定位互联网上资源的字符串。URL由多个组件构成包括协议、域名或IP地址、端口号、路径和查询参数等。 下面是一个示例URLhttp://www.example.com:8080/path/to/resource?param1value1param2value2 解释 协议URL的第一部分是协议这里是http。协议指定了浏览器与服务器之间的通信规则常见的有HTTP和HTTPS。域名或IP地址在示例中域名是www.example.com。域名是用于标识互联网上特定站点的字符串也可以使用IP地址来代替。端口号示例中的端口号是8080。默认情况下HTTP使用80端口HTTPS使用443端口但可以使用不同的端口号来访问特定的服务。路径路径指定了在服务器上资源的位置示例中是/path/to/resource。路径可以是文件、目录或其他资源的位置。查询参数在示例中查询参数是?param1value1param2value2。查询参数用于向服务器传递额外的信息以便执行特定的操作或获取特定的结果。 平时我们俗称的 网址 其实就是说的 URL。 具体也可看下图 域名就是服务器地址。浏览器会对域名进行解析解析后就会转换为对应的地址。一个服务器地址再加上端口号这就标示了该服务器的唯一进程。端口号后面用 ‘ / ’ 分隔的就是我们所请求资源在该服务器上的路径。
2.3 HTTP 请求格式
服务器收到一个HTTP请求后请求格式如下 请求行浏览器发送的第一部分是请求行它包含了请求的方法例如GET、要访问的资源路径例如/index.html以及使用的HTTP版本例如HTTP/1.1。 请求头部接下来浏览器发送请求头部其中包含一些额外的信息例如浏览器类型、所支持的编码方式、语言首选项等。 空行请求头部之后是一个空行用于分隔请求头部和请求正文。 请求正文可选有些请求可能包含请求正文例如表单数据或上传的文件。
其实我们对照我们刚刚举例的运行结果也可总结出请求报文的格式具体如下图 当服务器拿到请求报文后会对请求报文进行分析。例如其中就包含了请求的方法例如GET、请求的资源路径和协议版本结合请求报头就会对此进行分析找到资源并形成响应报文进行返回。其中有许多细节并未解释后文会详细解释。下面我们先来看一下响应报文的格式。
三、HTTP响应
上述我们例子中并未看到有任何界面。原因就是在于Web浏览器并未收到任何响应。根本在于我们所写的服务器就没有对此进行响应。我们不妨先看一下响应的实例。
3.1 响应demo HttpServer.cc // 一般http都要有自己的web根目录
#define ROOT ./wwwroot // ./wwwroot/index.html
// 如果客户端只请求了一个/,我们返回默认首页
#define HOMEPAGE index.htmlvoid Usage(std::string name)
{std::cout \nUsage : name Port\n std::endl;
}void HandlerHttpRequest(int sockfd)
{// 1. 读取请求 for testchar buffer[10240];ssize_t s recv(sockfd, buffer, sizeof(buffer) - 1, 0);if (s 0){buffer[s] 0;// std::cout buffer --------------------\n std::endl;}std::vectorstd::string vline;Util::cutString(buffer, \n, vline);std::vectorstd::string vblock;Util::cutString(vline[0], , vblock);std::string file vblock[1];std::string target ROOT;if (file /)file /index.html;target file;std::cout target std::endl;std::string content;std::ifstream in(target);if (in.is_open()){std::string line;while (std::getline(in, line)){content line;}in.close();}std::string HttpResponse;if (content.empty())HttpResponse HTTP/1.1 404 NotFound\r\n;elseHttpResponse HTTP/1.1 200 OK\r\n;HttpResponse \r\n;HttpResponse content;// 2. 试着构建一个http的响应send(sockfd, HttpResponse.c_str(), HttpResponse.size(), 0);
}int main(int argc, char *argv[])
{if (argc ! 2){Usage(argv[0]);exit(0);}std::unique_ptrHttpServer httpServer(new HttpServer(atoi(argv[1]), HandlerHttpRequest));httpServer-start();return 0;
} 这里有一个细节当我们输入URL没有请求资源路径时浏览器会自动加上一个 ’ / ‘代表着根目录。这里的根目录与Liunx 的根目录是不同的。一般服务器都会设置默认的Web根目录。这时候就是访问的默认界面。 index.html: !DOCTYPE html
html langenheadmeta charsetUTF-8meta http-equivX-UA-Compatible contentIEedgemeta nameviewport contentwidthdevice-width, initial-scale1.0titleHTTP响应/title
/headbodyh3现在你能够看到我了/h3p我是一个Linux的学习者我正在进行http的测试工作/pp我是一个Linux的学习者我正在进行http的测试工作/pp我是一个Linux的学习者我正在进行http的测试工作/pp我是一个Linux的学习者我正在进行http的测试工作/pp我是一个Linux的学习者我正在进行http的测试工作/pp我是一个Linux的学习者我正在进行http的测试工作/pp我是一个Linux的学习者我正在进行http的测试工作/p
/body
/html 运行结果 确实有了界面也正是我们所设计的界面。通过HTTP客户端可以获取到Web服务器上的各种资源例如HTML文档、图像、视频、样式表等。
3.2 HTTP响应格式 当服务器收到请求后它会进行相应的处理并生成一个HTTP响应返回给浏览器。 响应状态行响应的第一部分是状态行它包含了响应的HTTP版本例如HTTP/1.1、响应状态码例如200表示成功以及对应的状态消息例如OK。 响应头部接下来服务器发送响应头部其中包含一些额外的信息例如服务器类型、响应时间、返回的数据类型等。 空行响应头部之后是一个空行用于分隔响应头部和响应正文。 响应正文响应正文包含了服务器返回的实际数据例如HTML页面、图像、CSS样式表等。 具体也可结合下图理解