文章目录
http协议
本质是文本分析:1.http协议本身的字段;2.提取参数(如果有的话)
URL/网址:
统一资源定位服务,定位网络资源的一种方式
IP+Port:唯一确定一个进程
IP:唯一确定一台主机
绝对路径:唯一确定一个资源(Linux下所有资源都是以文件形式保存的)
所以,IP+Linux路径可以唯一确定一个网络资源
举例
https://blog.csdn.net/qq_56101220?type=blog
协议+域名+资源路径
https:协议方案名
blog.csdn.net:ip
qq_56101220:资源路径
?type=blog:查询字符串
请求的结构:(自顶向下)
请求的结构 |
---|
请求行:请求方法 /url /http版本 |
请求报文(多行)(key:value\n) |
空行 |
请求正文:用户提交的数据 |
响应的结构:
响应的结构 |
---|
状态行:http版本 /状态码 /状态码描述 |
响应报头(多行) |
空行 |
响应正文:html、css、音乐、图片、视频等 |
http的请求和响应:
被看作是一个大的字符串,
http的解包和封装:
空行:特殊字符,用空行将长字符串一切为二,解包时将报头按行读取,读到空行说明报头读取结束,剩下的内容是有效载荷;封装时构建http请求,之后加上空行,空行之后才是有效载荷
分用:
不是http解决,而是具体的应用代码解决的,http需要有接口来帮助上层获取参数
代码实现
#include "socket.hpp" //套接字方法的封装
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
using namespace ns_sock;
void *Routine(void *args)
{
int sock = *(int *)args;
delete (int *)args;
pthread_detach(pthread_self());
#define SIZE 1024*10
char buf[SIZE];
memset(buf,0,sizeof(buf));
ssize_t s = recv(sock,buf,sizeof(buf),0);//read
if(s>0){
buf[s] = 0;
std::cout<<buf;
std::string http_response = "HTTP1.0 200 OK\n";
http_response+="Content-Type:text/plain\n";
http_response+="\n";
http_response+="hello\n";
send(sock,http_response.c_str(),http_response.size(),0);//write
}
close(sock);
}
void Usage(std::string proc)
{
std::cout << "Usage:\n\t" << proc << " port" << std::endl;
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(1);
}
uint16_t port = atoi(argv[1]);
int sock = Sock::Socket();
Sock::Bind(sock, port);
Sock::Listen(sock);
for (;;)
{
int new_sock = Sock::Accept(sock);
pthread_t pid;
int *parm = new int(new_sock);
pthread_create(&pid, nullptr, Routine, parm);
}
}
浏览器显示的结果:
http请求的结果:
第一行:请求行
中间部分:请求报文
最后:空行
http中的重要属性(Header):
(1)Content-Type:用于定义网络文件的类型和网页的编码,决定文件接收方将以什么形式、什么编码读取这个文件,比如text/html 表示html文件 content-type对照表
(2 )Content-Length:响应正文的长度
(3)Location:搭配3XX状态码,告诉客户端接下来去哪里访问
(4)connection:长/短连接;短连接:每个资源都要发起http请求,Http/1.0;长连接:客户端发起请求设置一个字段connection:keep-alive(保持活跃),http/1.1之后
短连接:一个大网页由多个元素组成,访问这个网页时需要多次进行http请求,而http协议是基于tcp协议的,因此每次请求都需要进行建立连接、传送数据、断开连接的过程,耗费时间
长连接:建立一个连接,网页中的资源都可以通过这一个连接得到,通过减少频繁建立tcp连接,来达到提高效率的目的
(5)Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;
(6)User-Agent: 声明用户的操作系统和浏览器版本信息;
(7)referer: 当前页面是从哪个页面跳转过来的;
(8)Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;
GET:获取资源
参数提交到url中
默认一般获取所有的网页都是GET方法,GET通过URL来进行参数拼接,从而将参数提交给服务端
POST:传输实体主体
一般通过正文提交参数
对比:
1.参数提交的位置不同,POST方法比较私密,GET方法不私秘,会将信息回显到url输入框中,增加了被盗取的风险
2.GET通过url传参,url通常有大小限制,具体和浏览器有关,POST通过正文传参,没有大小限制
结论:
GET和POST是前后端交互的重要请求方法,如果提交的参数不敏感且长度短,可以选择GET,否则就使用POST方法
源码:
// http.cc
#include "socket.hpp"
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fstream>
#define PATH "./wwwroot/"
#define FILE_NAME "index.html"
using namespace ns_sock;
void *Routine(void *args)
{
int sock = *(int *)args;
delete (int *)args;
pthread_detach(pthread_self());
#define SIZE 1024 * 10
char buf[SIZE];
memset(buf, 0, sizeof(buf));
ssize_t s = recv(sock, buf, sizeof(buf), 0);
if (s > 0)
{
buf[s] = 0;
std::cout << buf;
// 文件版
std::string http_file = PATH;
http_file += FILE_NAME;
std::ifstream in(http_file);
if (in.is_open())
{
// std::cout << "open success" << std::endl;
std::string http_response = "HTTP/1.0 200 OK\n";
struct stat st;
stat(http_file.c_str(), &st);
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 (getline(in, line))
{
content += line;
}
http_response += content;
// std::cout << http_response << std::endl;
in.close();
send(sock, http_response.c_str(), http_response.size(), 0);
}
}
close(sock);
}
void Usage(std::string proc)
{
std::cout << "Usage:\n\t" << proc << " port" << std::endl;
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(1);
}
uint16_t port = atoi(argv[1]);
int sock = Sock::Socket();
Sock::Bind(sock, port);
Sock::Listen(sock);
for (;;)
{
int new_sock = Sock::Accept(sock);
pthread_t pid;
int *parm = new int(new_sock);
pthread_create(&pid, nullptr, Routine, parm);
}
}
<!-- wwwroot下的index.html文件 -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h3>首页</h3>
<h5>表单</h5>
<!-- get方法 -->
<form action="/" method="post">
姓名:<input type="text" name="name"><br/>
密码:<input type="password" name="password"><br/>
<input type="submit" value="登录"/>
</form>
</body>
</html>
HTTP状态码
类别 | 原因短语 | |
---|---|---|
1XX | 信息性状态码 | 接收的请求正在处理 |
2XX | 成功状态码 | 请求正常处理完成 |
3XX | 重定向状态码 | 需要进行附加操作以完成请求 |
4XX | 客户端错误状态码 | 服务器无法处理请求 |
5XX | 服务器错误状态码 | 服务器处理请求出错 |
常见状态码
200:ok;404:not found;403:forbidden;504:bad gateway;301:永久重定向;302/307:临时重定向
重定向:访问某一个网站时跳转到另一个网址,或者比如支付时的自动跳转
永久重定向:Permanently moved新的域名替 换旧的域名,如网站搬迁,域名更换,书签的网址也会更换
临时重定向:Found,一般是业务的某个环节,比如登录后、支付后自动跳转的情况
cookie和session
http是一种无状态协议
cookie:
浏览器角度:是一个保存用户私密信息的文件(内存版(关掉浏览器就没了)和文件版(重启电脑也在))
http协议角度:在对有cookie信息的网站发起任何请求时,都会在request中携带cookie信息
网页登录一次后自动登录的原理:第一次登录成功后将账号密码等私密信息保存在cookie中,在后续的请求中,浏览器会在每一个请求的请求报头属性中自动携带对应的cookie
set-cookie:服务器向浏览器设置一个cookie
风险:
如果别人盗取了我们的cookie文件,那么他就可以以我的身份进行认证访问特定的资源,还可以得到我的账号密码,因此单纯使用cookie是具有安全隐患的
session:
本质:将用户的私密信息保存在服务端
客户端用户发送携带账号密码的http请求进行登录 ,服务端对输入的内容进行认证,接下来给该用户形成一个保存他的私密信息的session文件,而每个session文件有它唯一的会话id:session_id,之后服务端再构建http响应,给对应客户设置Cookie,其中保存了session_id。
所有的http请求,都会由浏览器自动携带cookie内容,即当前用户的session_id,实现会话保持功能(server可以做到认识client)
cookie+session仍旧有风险,因为cookie文件保存在用户主机,也会被盗取,可以和用户访问相同的网站
防护措施:异地登录认证(IP识别),重新登陆(重新生成新的session_id);强制下线(服务端直接清掉session文件)
结论:cookie+session可以提高用户访问网站或平台的体验,使无状态的http协议的上下文记录下用户的状态
最终源代码
#include "socket.hpp"
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fstream>
#define PATH "./wwwroot/" //设置的web根目录,此目录下的文件都叫做资源
#define FILE_NAME "index.html" //首页,默认进入
using namespace ns_sock;
void *Routine(void *args)
{
int sock = *(int *)args;
delete (int *)args;
pthread_detach(pthread_self());
#define SIZE 1024 * 10
char buf[SIZE];
memset(buf, 0, sizeof(buf));
ssize_t s = recv(sock, buf, sizeof(buf), 0);
if (s > 0)
{
buf[s] = 0;
std::cout << buf;
// // 重定向
// std::string http_response = "HTTP/1.1 301 Permanently moved\n";
// http_response += "Location: https://www.qq.com/\n";
// http_response += "\n";
// send(sock, http_response.c_str(), http_response.size(), 0);
// std::string http_response = "HTTP1.0 200 OK\n";
// http_response+="Content-Type:text/plain\n";
// http_response+="\n";
// http_response+="hello\n";
// send(sock,http_response.c_str(),http_response.size(),0);
// 文件版
std::string http_file = PATH;
http_file += FILE_NAME;
std::ifstream in(http_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
{
//std::cout << "open success" << std::endl;
std::string http_response = "HTTP/1.0 200 OK\n";
struct stat st;
stat(http_file.c_str(), &st);
http_response += "content-type: text/html; charset=utf8\n";
http_response += "content_length:";
http_response += std::to_string(st.st_size);
http_response += "\n";
// cookie
// set-cookie:服务器向浏览器设置一个cookie
http_response+="set-cookie:name=111\n";
http_response+="set-cookie:password=123\n";
http_response += "\n";
std::string content;
std::string line;
while (getline(in, line))
{
content += line;
}
http_response += content;
// std::cout << http_response << std::endl;
in.close();
send(sock, http_response.c_str(), http_response.size(), 0);
}
}
close(sock);
}
void Usage(std::string proc)
{
std::cout << "Usage:\n\t" << proc << " port" << std::endl;
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(1);
}
uint16_t port = atoi(argv[1]);
int sock = Sock::Socket();
Sock::Bind(sock, port);
Sock::Listen(sock);
for (;;)
{
int new_sock = Sock::Accept(sock);
pthread_t pid;
int *parm = new int(new_sock);
pthread_create(&pid, nullptr, Routine, parm);
}
}