上部分写的具体内容总结如下:
http协议
http协议相当于大佬为我们已经设计好了,我们只需要知道了解与使用。
认识URL(网址)
说白了,他就是确认哪一个资源在哪一个服务器上
下面图说明了URL的意义
urlencode和urldecode
像 / ? : 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现.
比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义.
转义的规则如下:
将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式.
举例:
urldecode---->urlencode的过程
urldecode就是urlencode的逆过程
HTTP协议(宏观认识)(明文传输,一般用80端口,在传输层基于TCP协议实现)
简化认识:
无论是请求还是响应,基本上http都是按照行\n为单位进行构建请求或者响应的!!
无论响应还是请求,几乎都是由3或4部分组成
下面画图来介绍http协议的请求与响应格式:
http操作(观察请求与响应)
1.接收接口recv,与read功能相同,所以二者用哪个都可以
2.发送接口send,与send功能相同,所以二者用哪个都可以
3.获取文件信息函数,包括了文件大小等信息,stat
下面验证GET与POST方法时在显示网页的时候用到了上述stat这个函数
请求与响应效果
因为http底层是tcp所以也是需要tcp通信的那些流程,sock, bind, listen,accept
Sock.hpp
#pragma once
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include<unistd.h>
using namespace std;
class Sock
{
public:
static int Socket()
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
cerr << "sock create fail" << errno << endl;
exit(2);
}
return sock;
}
static void Bind(int sock, uint16_t port)
{
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, (struct sockaddr *)&local, sizeof(local)) < 0)
{
cerr << "bind erro" << errno << endl;
exit(3);
}
}
static void Listen(int sock)
{
const int back_log = 5;
if (listen(sock, back_log) < 0)
{
cerr << "listen erro" << errno << endl;
exit(4);
}
}
static int Accept(int sock)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int fd = accept(sock, (struct sockaddr *)&peer, &len);
if(fd >= 0)
return fd;
return -1;
}
static void Connect(int sock, std::string ip, uint16_t port)
{
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ip.c_str());
if(connect(sock, (struct sockaddr*)&server, sizeof(server)) == 0)
{
cout << "Connect Success!" << endl;
}
else
{
cout << "Connect Failed!" << endl;
exit(5);
}
}
};
Http.cpp
#include "Sock.hpp"
void Usage()
{
cout << "格式有误, 应当:"
<< "./http + port" << endl;
}
void *HandlerHttpRequest(void *argv)
{
int sock = *(int *)argv;
delete (int *)argv;
pthread_detach(pthread_self());
char buffer[1024*10];
memset(&buffer, 0, sizeof(buffer));
ssize_t s = recv(sock, buffer, sizeof(buffer), 0);
if (s > 0)
{
buffer[s] = 0;
//这里没有换行,因此显示的结果的空行是属于http协议中的空行
cout << buffer; 查看http的请求格式! for test
//必须按照http协议的格式发送数据
string str = "http/1.0 200 OK\n";
str += "Content-Type: text/plain\n";// Content-Type表示正文的类型,text/plain意思是正文是普通的文本,
str += "\n";//传说中的空行
str += "hello my bro";
send(sock, str.c_str(), str.size(), 0);
}
else
{
cout<<"read fail"<<endl;
}
close(sock);
return nullptr;
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage();
return 1;
}
uint16_t port = atoi(argv[1]);
int listen_sock = Sock::Socket();
Sock::Bind(listen_sock, port);
Sock::Listen(listen_sock);
for (;;)
{
int sock = Sock::Accept(listen_sock);
if (sock > 0)
{
pthread_t tid;
int *parm = new int(sock);
pthread_create(&tid, nullptr, HandlerHttpRequest, parm);
}
}
return 0;
}
Makefile
http:Http.cpp
g++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:
rm -rf http
服务端启动后,浏览器输入ip:端口号,即可看到效果
此处显示的是接收到的请求端信息,下面按http协议的格式进行拆解,来观察格式是否是按我们之前说的样子:
http一般对正文不做长度限制
此处显示的响应的效果:
/ 代表网站的首页
如果我们请求的URL为 / 则网站会自动帮我们跳转到首页,首页一般在index.html文件中,服务器内部会自动帮我们处理的。
如何读到完整的报文
我们再读取请求时,缓冲区大小是自己定的
那么如果我的缓冲区大小,大于一条报文的大小,那是否会读到后面即下一条报文的内容呢?
如果真是这样的话,那么协议就失效了!
因此介绍一下关键字:Content_Length
Content_Length
以下图为例,说明其作用:
http版本
http1.0与http1.1的区别就是是否支持长链接
短链接:一个请求, 一个响应,close socket。
我们写的代码就是短链接,短链接的出现是因为过去的网速并不高,所以短链接在当时比较流行
http方法
验证GET与POST方法
首先设计一个网页能够,输入账号和密码!,如何设计网页对我们来说不重要!只要了解即可。
二者index.html在http方法处稍有不同,cpp文件相同
.Http.cpp
#include "Sock.hpp"
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fstream>
#define WWWROOT "./wwwroot/"
#define HOME_PAGE "index.html"
void Usage()
{
cout << "格式有误, 应当:"
<< "./http + port" << endl;
}
//其中wwwroot 就叫做web根目录,wwwroot目录下放置的内容,都叫做资源!!
// wwwroot目录下的index.html就叫做网站的首页
void *HandlerHttpRequest(void *argv)
{
int sock = *(int *)argv;
delete (int *)argv;
pthread_detach(pthread_self());
char buffer[1024 * 10];
memset(&buffer, 0, sizeof(buffer));
ssize_t s = recv(sock, buffer, sizeof(buffer), 0);
if (s > 0)
{
buffer[s] = 0;
//这里面存的就是请求的格式与内容,我们打印出来观察格式
cout << buffer; 查看http的请求格式! for test
std::string html_file = WWWROOT;
html_file += HOME_PAGE;
// string http_response = "http/1.1 301 Permanently moved\n";
// http_response += "Location: https://www.bilibili.com/\n";
// http_response +="\n";
// send(sock, http_response.c_str(), http_response.size(), 0);
std::ifstream in(html_file);
if (!in.is_open())
{
std::string http_response = "http/1.0 404 NOT FOUND\n";
// 正文部分的数据类型
http_response += "Content-Type: text/html; charset=utf8\n";
http_response += "\n";
http_response += "<html><p>你访问的资源不存在</p></html>";
send(sock, http_response.c_str(), http_response.size(), 0);
}
else
{
struct stat st;
stat(html_file.c_str(), &st);
// 返回的时候,不仅仅是返回正文网页信息,而是还要包括http的请求
std::string http_response = "http/1.0 200 ok\n";
// 正文部分的数据类型
http_response += "Content-Type: text/html; charset=utf8\n";
//响应的是网页,因此要带上网页文件的大小,如果不带的话会一次读取缓存区的大小
http_response += "Content-Length: ";
http_response += std::to_string(st.st_size);
http_response += "\n";
http_response += "\n";
//接下来,才是正文
std::string content;
std::string line;
while (std::getline(in, line))
{
content += line;
}
http_response += content;
http_response += "\n";
in.close();
send(sock, http_response.c_str(), http_response.size(), 0);
}
}
else
{
cout << "read error" << endl;
}
close(sock);
return nullptr;
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage();
return 1;
}
uint16_t port = atoi(argv[1]);
int listen_sock = Sock::Socket();
Sock::Bind(listen_sock, port);
Sock::Listen(listen_sock);
for (;;)
{
int sock = Sock::Accept(listen_sock);
if (sock > 0)
{
pthread_t tid;
int *parm = new int(sock);
pthread_create(&tid, nullptr, HandlerHttpRequest, parm);
}
}
return 0;
}
1.GET方法
2.POST方法
总结GET与POST的区别(重要)
http协议(理性认识)
http协议处理,本质是文本分析
所谓的文本分析
- http协议本身的字段
- 提取参数,如果有的话
GET 或者 POST 其实是前后端交互的一个重要方式
http状态码
最常见的状态码, 比如 200(OK), 404(Not Found)无效链接, 403(Forbidden)403:服务器接收到请求但拒绝提供服务,原因较多,比如权限不足,IP被拉入黑名单…, 301(永久重定向)302(Redirect, 临时重定向), 504(Bad Gateway)
不同的浏览器支持的状态码会有差别。
类似于404状态码,对浏览器没有任何指导意义,浏览器就是正常显示你的网页,只不过我们看到的404错误界面是服务端给显示的。
如下:可以证明404状态码是需要服务端来处理而不是给浏览器处理的,出现404状态码属于输入客户端的问题,因为客户端企图访问不存在的资源。
3XX重定向状态码
1.永久重定向:301
2.临时重定向:302,307
重定向:当我们访问某个网站会提示我们跳转到另一个网站或者我们上网时提示我们登录,自动跳转到登陆页面,登陆完毕后有自动跳转回来,这些情况就属于重定向。
模拟永久重定向301
重定向是需要浏览器给我们提供支持的,浏览器必须是别301,302,307,与404不同(404浏览器是不需要处理的)
server要告诉浏览器,我应该去哪里。
通过报头属性Location: 告诉浏览器新的地址!
HTTP常见Header
1.Content-Type: 数据类型(text/html等)
2.Content-Length: Body的长度
3.Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;
4.User-Agent: 声明用户的操作系统和浏览器版本信息;
5.referer: 当前页面是从哪个页面跳转过来的;(请求头部字段)
6.Location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问;(响应头部字段)
7.Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;(请求头部字段)
8.Connection:长连接或短链接;
9.DELETE:用来删除指定的资源,它会删除URI给出的目标资源的所有当前内容
Connection
当报文显示Connection:keep-alive说明支持长连接
当报文显示Connection:close或者不显示说明不支持长连接
1.短链接
2.长链接,长连接通过减少频繁建立tcp链接。来达到提高效率的目的
Cookie与Session
注意:http协议本身是一种无状态(简单)协议,他不会对历史发起的http有任何信息记录
但是,当我们成功登录一个网页,第一次会让我们输入用户名与密码,但如果我们关闭网页不退出浏览器的话,再次登录该网页就会自动登录了,这是因为当第一次成功登陆后,网址会自动将我们的身份信息存入cookie中,当再次访问时,网站的服务器端会先检查是否有Cookie
,有的话就会自动完成身份验证,不用再次登录了
让网站能够"认识"我们并不是http协议要解决的问题,他只负责解决网络获取资源的问题,但他可以给我们提供一些技术支持来帮助网址认识我们,于是有了Cookie。
设置Cookie信息 Set-Cookie
Session(隔一段时间更新)
由上可知Cookie文件中存了客户的隐私信息,而且由于客户的防御意识不够高,cookie文件被盗取的几率很大,一旦被盗取了,不法分子就可以用盗取的cookie文件中的身份认证信息,登录相应的网站了。而且如果cookie中如果有客户的账号和密码,后果会更严重!
因此,为了提高安全性,往往都是cookie+session组合使用,下面画图介绍session的作用。
Cookie与Session总结(重要)
cookie文件中存的时能够验证客户身份信息的内容,这样就不用每次访问都要重新登陆,提高了效率
通过session_id能让客户端的cookie中的文件存的不是客户的隐私信息而是id,减少了隐私泄露的风险。
但cookie文件还是会有被盗取的风险!俗话说道高一尺,魔高一丈,世界上没有不透风的墙我们只能不断提高防盗意识!
一般情况下Session是通过Cookie传递Session_ID实现的,禁用cookie则session就没法用了,但是劳动人民智慧是无穷的,可以将SESSION_ID附着在URL中来实现,也就是session并不一定完全依赖于cookie实现
https(加密传输,一般用443端口)
加密方式
如何防止数据被篡改(概念性知识,看不懂可配合后面CA的详细介绍)
如何选择加密算法?
只用对称密钥
结论:单独采用对称密钥,绝对不可能,非常不安全
只用一对非对称密钥
结论:只用一对非对称密钥只能保证单向数据安全
用两对非对称密钥
结论:1.依旧不安全 2.非对称加密算法特别浪费时间!
对称+非对称
结论:看似可行,实则还有漏洞!
对上述的破解手段
真正的加密手段
CA机构认证
真正加密解密流程
客户端如何得到CA的公钥?答:出厂内置的,或者访问网址的时候弹出来提示安装
总结:
1.客户端登陆浏览器访问服务端,服务端返回给客户端CA证书,证书里包含服务端公钥,证书日期之类的
2.客户端用CA公钥解密证书,验证数据是否被篡改,在TSL层生成随机数X并用服务器的公钥进行非对称加密,发送回服务器
3.服务端用自己的私钥解密获得随机数X,至此双方就拥有了唯一通信的随机数,以后传输数据先用随机数进行对称加密
4.客户端收到数据先用随机值解密,完成通信