前瞻:
认识URL
1.ip+port
2.平时上网,就是进程间通信
3.上网行为,1.获取资源 2.上传数据 相当于I/O
4.http协议采用tcp协议
网页 图片 音乐其实都是资源
Http请求
http request
Method:Get/Post
资源/路径:网页 图片 视频资源的路径
版本:http/1.0 短连接 http/1.1(主流) 长连接 基于一条连接发送许多请求
http如何读到完整的报文
a.一直读直到读到空行 分离报文和有效载荷
b.Content-Length:XXX——表示正文部分的长度 读到Content则正文有数据
如何对http进行反序列化 根据/r/n
http response
状态码:200 表示成功
404 代表资源无法找到
请求行的内容
method url http_version
在使用 std::stringstream 进行输入操作时,默认情况下是以空格作为分隔符的
此时可以将他们提取出来
`std::stringstream` s1;
s1>>_method>>_url>>_http_version
资源url路径
要访问资源所在的路径
如何理解web根目录 /
可以将url中/提取出来,再识别成./wwwroot + firstpage 这样/访问就是访问首页
Content-Type:表示正文类型
a.你要求的所有资源都有资源类型
b.http response给浏览器响应的时候,会自动去识别资源类型去读取资源
所以我们没对资源进行访问时,都要有suffix后缀提供给浏览器识别读取
例如:
HTTP 请求或响应的 Content-Type 通常用于指定传输的数据类型。常见的 Content-Type 值:
text/html:HTML 文档
JPG 图像文件的 Content-Type 是:
image/jpeg
我们将suffix转换成shuffix后缀再交给responsecontent
工具:Fiddler
请求方法GET/POST
上网行为
我们获取资源
我们向服务器传参 登陆 注册 搜索 POST+ 利用html的表单
GET:既可以获取资源也可以上传参数
POST:上传
request and response
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <fstream>
using namespace std;
const string httpSep = "\r\n";
const string firstpagepath="firstpage.html";
const string workroot="./wwwroot";
const string spaceseq=" ";
class Request
{
public:
Request() : _blank_line(httpSep),_path(workroot)
{
}
bool GetLine(string &response, string *line)
{
int pos = response.find(httpSep);
if (pos == string::npos)
return false;
*line = response.substr(0, pos);
response.erase(0, pos + httpSep.size());
return true;
}
void Deserialize(string &response)
{
string line;
if (!GetLine(response, &line))
return;
_statu_line = line;
while (true)
{
string line;
bool ok = GetLine(response, &line);
if (ok && !response.empty()) //_vec_line
{
_vec_header.push_back(line);
continue;
}
else if (ok && response.empty())
{
_req_content = line;
break;
}
else
{
break;
}
}
}
void Parse_statu_line()
{
std::stringstream s1(_statu_line);
s1>>_method>>_url>>_http_version;
if(_url=="/")
{
_path+=_url+firstpagepath;
}
else
{
_path+=_url;
}
}
void Parse_suffix_type()
{
int pos=_path.find('.');
if(pos==string::npos)
{
_suffix=".html";
}
_suffix=_path.substr(pos);
}
std::string GetFileContentHelper(const std::string &path)
{
//文本
// std::ifstream in(path, std::ios::binary);
// if (!in.is_open())
// return "";
// std::string content;
// std::string line;
// while(std::getline(in, line)) // BUG
// {
// content += line;
// }
// in.close();
// return content;
std::ifstream in(path, std::ios::binary);
if (!in.is_open())
return "";
in.seekg(0, in.end);
int filesize = in.tellg();
in.seekg(0, in.beg);
std::string content;
content.resize(filesize);
in.read((char *)content.c_str(), filesize);
// std::vector<char> content(filesize);
// in.read(content.data(), filesize);
in.close();
return content;
}
void Parse()
{
Parse_statu_line();
Parse_suffix_type();
}
void RequestDebug()
{
cout << "_statu_line:" << _statu_line << endl;
for (auto &e : _vec_header)
{
cout << "---->" << e << endl;
}
cout << "_blank_line" << _blank_line << endl;
cout << "_req_content:" << _req_content;
}
~Request() {}
string& Path()
{
return _path;
}
string& Suffix()
{
return _suffix;
}
private:
string _statu_line;
vector<string> _vec_header;
string _blank_line;
string _req_content;
string _method;
string _url;
string _http_version;
string _path;
string _suffix;
};
class Response{
//"Http/1.0 200 ok\r\n";
public:
Response():_http_version("Http/1.0")
{}
bool SetCode(int code)
{
_code=to_string(code);
}
string ret_code()
{
return _code;
}
bool SetCodeDesc(const string codedesc)
{
if(codedesc.empty()) return false;
_code_desc=codedesc;
return true;
}
void make_statu_line()
{
_statu_line=_http_version+spaceseq+_code+spaceseq+_code_desc+"\r\n";
}
void head_push(string line)
{
_head_vector.push_back(line);
}
void Set_content(string content)
{
_rescontent=content;
}
string serialize()
{
string retmesaage("");
retmesaage+=_statu_line;
for(auto & e: _head_vector)
{
retmesaage+=e;
}
retmesaage+=_rescontent;
return retmesaage;
}
private:
string _statu_line;
vector<string> _head_vector;
string _rescontent;
string _http_version;
string _code;
string _code_desc;
};
main.cc
#include"TcpServer.hpp"
#include"HttpProtocol.hpp"
using namespace std;
using namespace NET_work;
void Usage(std::string proc)
{
std::cout << "Usage : \n\t" << proc << "local_port\n"
<< std::endl;
}
string _to_httptype(string type)
{
if (type == ".html" || type== ".html")
return "text/html";
else if (type == ".png")
return "image/png";
else if (type == ".jpg")
return "image/jpeg";
else
{
return "text/html";
}
}
string GetCodeDesc(int _code)
{
switch (_code)
{
case 200:
return "OK";
case 404:
return "Not Found";
case 307:
return "Temporary Redirect";
default:
break;
}
}
string Http_Handel(string & requestmessage)
{
Request req;
req.Deserialize(requestmessage);
req.Parse();
req.RequestDebug();
string responsemessage;
string content=req.GetFileContentHelper(req.Path());
Response res;
int code=200;
if(content.empty())
{
code=404;
content=req.GetFileContentHelper("./wwwroot/404.html");
}
//code=307;
res.SetCode(code);
res.SetCodeDesc(GetCodeDesc(code));
res.make_statu_line();
res.Set_content(content);
string httphead="Content-Length: " + std::to_string(content.size()) + "\r\n";
res.head_push(httphead);
httphead="Content_type: "+_to_httptype(req.Suffix())+"\r\n";
res.head_push(httphead);
if(res.ret_code()==to_string(307))
{
string httphead307;
httphead307="Location:http://www.qq.com"+string("\r\n");
res.head_push(httphead307);
}
httphead="Set-Cookie: "+string("username=bossface")+"\r\n";
res.head_push(httphead);
httphead="Set-Cookie: "+string("password=123465")+"\r\n";
res.head_push(httphead);
httphead="\r\n";
res.head_push(httphead);
//content;
responsemessage=res.serialize();
return responsemessage;
}
int main(int argc, char *argv[])
{
// 遗留问题,客户端只发了一次数据就退出了?
if (argc != 2)
{
Usage(argv[0]);
cerr << "Usage error" << endl;
exit(0);
}
uint16_t port = stoi(argv[1]);
TcpServer *server = new TcpServer(port,Http_Handel);
server->Loop();
}
表单
FROM表单不写方法默认是GET方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>我的网站</title>
</head>
<body>
<h4>This is my first paragraph.</h4>
<!-- <a href="https://www.qq.com/">点我</a> -->
<a href="http://8.137.19.140:8888/dira/dirb/x.html">跳转到登录</a>
<form action="/dira/dirb/x.html" method="post">
First name:<br>
<input type="text" name="myname" value="aaaa">
<br>
Last name:<br>
<input type="password" name="mypasswd" value="">
<br><br>
<input type="submit" value="登录">
</form>
<!-- <img src="/image/1.png" alt="cat"> -->
</body>
</html>
GET vs POST
GET利用url传参 POST利用正文传参
GET有字节数限制 POST没有字节数限制
GET私密性更差一点,但是GET/POST方法都不安全
我们要对数据进行加密和解密
HTTP的状态码
HTTP状态码对照表,HTML常用字符查询表,html特殊字符对照表_TendCode
例如200 success 404 failed 403 Forbiden 禁止访问
设计一个404
可以直接创建一个404.html 当获取内容为空时 code设置为404 改为获取404.html文件中的内容
设计一个307 重定向功能
结合location
在响应报头里添加 Location: www.qq.com
重定向可以进行页面的跳转
301永久重定向和307临时重定向有什么区别?https://tendcode.com/tool/html-special-characters/
搜索网页,搜索的关键字会与无数个URL网络链接相关联(例如baidu),网站过期了,我们可以用301,去让用户获取新的链接,之后用户所有的访问都会去新的永久重定向的地址,旧地址就不使用了
cookie和session
http
1.无连接
2.无状态
网站为什么要一直认识我?
网站根据用户身份,要区分用户的权限。
那当点击网站内任意一链接时,为什么网站又不去一直让我登陆?
因为server无状态,所以无法一直识别用户,此时server会传入传入一个Cookie,Cookie字段会报曾经登陆过的账号密码自动保留,当下一次访问server就可以不用登陆了。
当将Cookie移除时,用户就需要重新登陆了
如何实现?
在http报头字段加入 Set-Cookie:username= ?
Set-Cookie: password=?
我们的Cookie有两类:文件级,内存级
保存的位置要么在文件要么在内存
tips:登陆网站后退出,当再次登陆,不需要输用户名密码则是在文件中储存。
我们Cookie是基于会话管理的
我们自己设置一般的cookie存在一些问题
原因;当我们的电脑被入侵,黑客拿到了我们的Cookie,黑客可以直接拿我们的Cookie登陆我们的账号,就比如在之前我们的QQ特别容易被盗。
解决方案--->引入Sessionid
当用户输入账户和密码时,服务器返回的Cookie中不存用户的账户密码,而是存入Session,再构建Sessionid,Sessionid具有唯一性,返回给浏览器,这样我们用户的账户和密码就保存在了服务端,而不是客户端,而客户端再次访问时,客户端发给服务端Sessionid,服务端再用Sessionid去认证。
这时候又有个问题,黑客就算拿不到用户账户密码,也能拿到Sessionid,再通过服务端访问,Sessionid的引入好像只是解决了用户账户密码的暴漏问题。
这个问题我们无法解决,因为我们一旦把账户密码发送到客户端,用户信息就暴漏了。但是服务端有很多种策越解决这个问题,例如IP认证。一旦检测到陌生IP访问,服务端可以直接让Sessionid失效。