在此之前我们对网络套接字编程有了一定的基础和了解,接下来我们将自顶向下学习Linux网络分层协议栈,透过对协议栈的深入学习从而加深我们对网络的理解
URL(Uniform Resource Lacator)叫做统一资源定位符,也就是我们通常所说的网址,是因特网的万维网服务程序上用于指定信息位置的表示方法
www.alibaba.com
,www.qq.com
,www.baidu.com
ping
命令获取域名解析后的IP地址:要获取(访问)的应用资源的路径,即资源的存储位置,一般会使用“/”来分级描述
/
,Windows的路径分隔符是\
用于获取资源时,向服务器端传递参数,可以一个或多个,多个则以”&”连接,通常以“?”作为开始符号,例如例子“?q=java”表示传递的搜索参数java,即该应用url表示搜索java方面的内容
也叫做哈希值,通常以#开始,表示定位到页面某个位置(或者说定位到页面的锚点,熟悉前端的人应该知道锚点是指页面某个部分的id),这部分内容不传到服务器端,而是用于前端页面定位显示
将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式
http_server.hpp:
#pragma once
#include<iostream>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>
#include<strings.h>
namespace ns_Http
{
class HttpServer
{
private:
uint16_t port;
int listen_sock;
public:
HttpServer(uint16_t _port):port(_port),listen_sock(-1){}
~HttpServer()
{
if(listen_sock>=0) close(listen_sock);
}
void InitHttpServer()
{
listen_sock=socket(AF_INET,SOCK_STREAM,0);
if(listen_sock<0)
exit(2);
struct sockaddr_in local;
socklen_t len=sizeof(local);
bzero(&local,len);
local.sin_family=AF_INET;
local.sin_port=htons(port);
local.sin_addr.s_addr=INADDR_ANY;
if(bind(listen_sock,(struct sockaddr*)&local,len)<0)
exit(3);
if(listen(listen_sock,5)<0)
exit(4);
}
static void* Routine(void* args)
{
int sock=*((int*)args);
delete (int*)args;
pthread_detach(pthread_self());
//逻辑处理
std::cout<<">-------------------------------begin-------------------------------<sock:"<<sock<<std::endl;
char buffer[1024]={0};
ssize_t s=recv(sock,buffer,sizeof(buffer)-1,0);
if(s>0)
{
std::cout<<buffer<<std::endl;
}
std::cout<<">--------------------------------end--------------------------------<sock:"<<sock<<std::endl;
close(sock);
return nullptr;
}
void Loop()
{
while(true)
{
struct sockaddr_in peer;
socklen_t len=sizeof(peer);
bzero(&peer,len);
int sock=accept(listen_sock,(struct sockaddr*)&peer,&len);
if(sock<0)
continue;
pthread_t tid;
int* p=new int(sock);
pthread_create(&tid,nullptr,Routine,p);
}
}
};
}
http_server.cc:
#include "http_server.hpp"
int main(int argc,char* argv[])
{
if(argc!=2)
{
std::cout<<"Usage:\n\t./http_server port"<<std::endl;
exit(1);
}
ns_Http::HttpServer hs(atoi(argv[1]));
hs.InitHttpServer();
hs.Loop();
return 0;
}
[请求方法] [url] [版本] (空格分开)
Name:[空格]内容(一行就是一个属性,这里的“行”是以换行符作为标准)
理论上 HTTP 不需要向上交付,HTTP已经是最上一层的协议,但是上一层还有用户,需要将正文、请求方法和属性等交给用户
std::string response;
std::ifstream in(HOME_PAGE,std::ios::in|std::ios::binary);
std::string body;
std::string line;
if(!in.is_open())//打开失败则发送404页面
{
std::ifstream _in(PAGE_404,std::ios::in|std::ios::binary);
while(getline(_in,line))
body+=line;
response += "HTTP/1.0 404 Not Found\n";
response += "Content-Type: text/html\n";
response += ("Content-Length: " + std::to_string(body.size()) + "\n");
response += "\n";
response += body;
send(sock, response.c_str(), response.size(), 0);
}
else//打开成功则发送首页页面
{
while(getline(in,line))
body+=line;
response += "HTTP/1.0 200 OK\n";
response += "Content-Type: text/html\n";
response += ("Content-Length: " + std::to_string(body.size()) + "\n");
response += "\n";
response += body;
send(sock, response.c_str(), response.size(), 0);
}
注:客户端在发起HTTP请求是会告诉服务器自己所使用的http版本,此时服务器就可以根据客户端使用的http版本,为客户端提供对应的服务,而不至于因为双方使用的http版本不同而导致无法正常通信
方法 | 说明 | 支持的HTTP协议版本 |
---|---|---|
GET | 获取资源 | 1.0、1.1 |
POST | 传输实体主体 | 1.0、1.1 |
PUT | 传输文件 | 1.0、1.1 |
HEAD | 获得报文首部 | 1.0、1.1 |
DELETE | 删除文件 | 1.0、1.1 |
OPTIONS | 询问支持的方法 | 1.1 |
TRACE | 追踪路径 | 1.1 |
CONNECT | 要求用隧道协议连接代理 | 1.1 |
LINK | 建立和资源之间的联系 | 1.0 |
UNLINK | 断开连接关系 | 1.0 |
注:其中最常用的就是GET方法和POST方法
注:表单当中的method属性指定参数提交的方法,action属性表示将表单中的参数提交给服务器上的哪个资源位置
在开发好了网站后,用户通过URL对资源进行操作,服务器端要告诉用户交互的结果,比如新增资源是成功还是失败了。一个较好的办法就是遵循HTTP协议,使用请求响应的HTTP状态码(Status Code)来进行判断
状态码 | 类别 | 原因短语 |
---|---|---|
1XX | Informational(信息性状态码) | 接收的请求正在处理 |
2XX | Success(成功状态码) | 请求正常处理完毕 |
3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 |
4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 |
5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 |
注:最常见的状态码如200(OK),404(Not Found),403(Forbidden请求权限不够),302(Redirect),504(Bad Gateway)
进行临时重定向时需要用到Location字段,Location字段是HTTP报头当中的一个属性信息,该字段表明了你所要重定向到的目标网站
//构建HTTP响应
std::string response = "HTTP/1.0 307 Temporary Redirect\n"; //状态行
response += "Location: https://coca1cole.blog.csdn.net/\n"; //跳转页面
response += "\n"; //空行
send(sock, response.c_str(), response.size(), 0);
close(sock);
cookie虽然在持久保存客户端数据提供了方便,但是如果cookie被人拦截了,那人就可以取得期中的参数信息。如果是账号和密码,那么就存在账号被盗以及账号被利用做坏事
注:引入session技术后,浏览器当中的cookie文件保存的是SessionID,同样的这个cookie文件可能被盗取,但是账号和密码并不会被泄漏,而是对应的SessionID是会泄漏的,此时非法用户仍然可以盗取我的SessionID去访问我曾经访问过的服务器,相当于依旧存在利用SessionId进行账号登录并利用账号做坏事
在服务器给客户端的HTTP响应当中设置Set-Cookie字段,即使用cookie技术
response += "HTTP/1.0 200 OK\n";
response += "Content-Type: text/html\n";
response += ("Content-Length: " + std::to_string(body.size()) + "\n");
response += "Set-Cookie: sessionid=123456\n"; //添加Set-Cookie字段
response += "\n";
response += body;
send(sock, response.c_str(), response.size(), 0);