文章目录
协议
协议是一种 “约定”. socket api的接口, 在读写数据时, 都是按 “字符串” 的方式来发送接收的. 如果我们要传输一些"结构化的数据" 怎么办呢?
协议就要出现了。所谓协议就是大家都按照这个规定发送数据给服务器,服务器都按照这个规定发送数据给本地。
其实网络通信的本质就是发送请求和接收回应,也就是发送resquest,接收respond。
定义结构体发送信息到网络叫序列化,定义结构体从网络发到本地叫反序列化
序列化反序列化用工具 json xml
url
url,url就是网址。
- 登录信息一般是不显示的,忽略的。
- 服务器端口是不可以省略的,但是为什么随便打开一个网址,服务器端口号都没有显示出来。原因是服务器端口号和协议是强绑定的。http的端口号就是80,https的端口号就是443,不会变。
- 端口号后面的/是web根目录,就是linux下一个特定的根目录。 后面可以接上相对路径。(其实从这个/也可以看出,浏览器是用linux做os实现的,windows的文件分割符是)
- 文件路径其实就是是URI.
- ?往后跟的是参数。参数是以key-value来传参的。互联网是有行为的,1是把服务器的数据拿下来,2是把自己的数据拿到服务器。参数是和互联网行为有关的概念。
举个例子:
editor.csdn.net是域名,也就是host,DNS解析后就是IP地址。
/是web根目录
md是URI,特定的文件资源
?后面接的是参数
urlencode和urldecode
有一些特殊字符,已经倍url当作特殊意义理解了,因此不能随意出现。如果参数中带了这些特殊字符,就会被转义。
比如加号+是特殊字符,搜索c++的时候参数的++会被转义成%2B%2B,
urldecode就是把%2B%2B变回++
URL & URI & URN(项目用)
- URI,是uniform resource identifier,统一资源标识符,用来唯一的标识一个资源
- URL,是uniform resource locator,统一资源定位符,它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源。
- URN,uniform resource name,统一资源命名,是通过名字来标识资源,比如mailto:javanet@java.sun.com。
简单来说:URL是具体的URI,URI是抽象的概念。所有链接都是URL,因为它不仅仅可以标识链接的唯一性,还可以通过链接标识它的位置。(URI和URL其实可以混着说)
URN用的很少,可以通过名字来标识资源。
举个例子:
URI: /home/index.html
URL: www.xxx.com:/home/index.html
/home/index.html可以标识这台主机上的资源(其实这个例子不太好,这个东西只能标识本地主机的资源,不能标识全局主机的资源, 但是确实是这么称呼的)
www.xxx.com:/home/index.html可以标识并找到这个资源。
再总结一下:
端口号(域名)后面的就是URI,加上域名就是URL,也就是整个网址。
浏览器URL格式(项目用)
HTTP URL (URL是一种特殊类型的URI,包含了如何获取指定资源)的格式如下:
http://host[":"port][abs_path]
- http表示要通过HTTP协议来定位网络资源
- host表示合法的Internet主机域名或者IP地址,本主机IP:127.0.0.1
- port指定一个端口号,为空则使用缺省端口80
- abs_path指定请求资源的URI
- 如果URL中没有给出abs_path,那么当它作为请求URI时,必须以“/”的形式给出,通常这个工作浏览器自动帮我们完成。
如:
输入: www.baidu.com,浏览器自动转换成:https://www.baidu.com/
如果用户的URL没有指明要访问的某种资源(没有给URI),虽然浏览器会默认添加/(web根目录),但是依旧没有告知服务器要访问什么资源!此时,默认返回浏览器的首页(相当于返回web根目录了)
http
http是超文本传输协议。
基本特征:
1.无链接
2.无状态
3.简单快速
关于无链接的解释:
http不是基于tcp之上的吗,为什么是无链接的?
http是应用层的,tcp是传输层的。http并不关心底层到底要干嘛。
也就是说tcp建立连接和http无关,http直接向对方发送http request即可。
关于无状态的解释:
http本身是无状态的,并不会记录任何用户信息,http只进行简单的响应和请求,但是我们发现现实中网站是会记录用户信息的,是cookie+session来记录的
关于简单快速的解释:
简单体现在http只会request和respond。快速体现在他底层使用的是tcp协议。
短链接进行文本传输 http/1.0
http/1.1 长链接
http构成
http主要有request和respond构成。它们的结构也很相像。
request
respond
服务器是如何读取request,客户端是如何读取respond的呢?(由于tcp是字节流传输,读取可能会出现错误)
抓包原理
抓包工具相当于一个代理。本来浏览器直接向网络发送request,现在先把request发送给抓包工具,fiddler再发给网络。
telnet命令行抓包
用telnet连上网之后给服务器发送request,它会给你返回respond
发送请求GET / HTTP/1.1 这就是刚刚讲的请求行,然后没有请求报头,再输入多一个空行,没有请求正文。
相当于这样:
respond:
第一行是回应行
HTTP/1.1 200 OK
第二行到空行前是相应报头。
空行后面都是相应正文。
fiddler抓包
fiddler抓https的包更加直观一些。
request
respond
请求行
GET / HTTP/1.1
里面的/是什么东西?
是web根目录,就是linux下的一个特定的路径
post和get的对比
POST通过正文传参, GET通过url传参。
GET还可以从服务器上拿资源下来。
POST在传参的时候比GET更私密一些。
啥是传参,输密码就是一种传参。
比如
账号密码就是参数。
抓包看到以post发送request的参数是在请求正文里面回显出来的。
如果以get发送参数就会在url上体现出来,比如百度搜索的时候,输入关键字搜索。
抓包看一下是否是get,果然是!
http方法
-
GET是从服务器上获取资源。资源可以是文本,可以是视频,可以是任何东西。GET也可以传参。像搜索关键字等数据。
-
POST通常用来上传数据给服务器。数据是指那种像输账号密码一样的东西,不是文件
-
PUT通常是用来上传资源给服务器。
http状态码
3xx可以实现重定向。
简单来说就是,我输入一个网址,这个网址可以带我去另一个网址。后面写最简单的http服务器会写一下重定向。
HTTP常见Header(报头)
- Content-Type: 数据类型(text/html等),一般是respond有
- Content-Length: Body的长度,respond和request都可能有
- Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;
- User-Agent: 声明用户的操作系统和浏览器版本信息;
- referer: 当前页面是从哪个页面跳转过来的;
- location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问;
- Cookie: 用于在客户端存储少量信息. 通常用于**实现会话(session)**的功能;
- Accept:代表可以接收的数据类型
User-Agent
用程序去访问服务器并获取资源的行为叫爬虫。
wget命令就可以爬。
比如用wget www.baidu.com
本地就多了这么一个百度的首页信息文件
爬虫有可能会把服务器搞崩,因此很多网站会有反爬机制。服务器怎么识别你是正常用户还是爬虫用户呢?就是通过报头里面的User-Agent来判定的。
Content-type
常见的媒体格式类型如下:
- text/html : HTML格式
- text/plain :纯文本格式
- text/xml : XML格式
- image/gif :gif图片格式
- image/jpeg :jpg图片格式
- image/png:png图片格式
cookie
http本身是无状态的,但是如果是无状态的会给用户很差的体验,每一次访问一个网站都要重新登陆。
session
cookie有可能被窃取,为了减缓这个问题。引入了session。可以用session来为cookie加密。session是放在服务器的一个文件。存放着你的个人信息。
过程:
client发送request,server接收后set一个sid,并创建一个session文件。保存个人信息,然后返回respond的时候set-cookie = sid。当用户下次再访问的时候,request会自动发送cookie过去,server根据cookie里面的sid来识别个人信息。
但这并没有完全解决问题,盗取cookie还是有可能能上网的。但是已经很安全了,因为即使被盗取了cookie,账号密码等个人信息它还是拿不到的。
最简单的http服务器
和之前的socket编程是一样的。我总结一个流程模板,往上套即可。
流程模板
重点是怎么编写respond。先贴代码:(这里写的是短链接,因此连接完直接就close(sock)了)
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string>
using namespace std;
class httpServer
{
private:
int lsock;
int port;
public:
httpServer(int _port) :port(_port) {}
~httpServer(){close(lsock);}
void httpServerInit()
{
lsock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = INADDR_ANY;
socklen_t addrlen = sizeof local;
if(bind(lsock, (struct sockaddr*)&local, addrlen) < 0)
{
cerr << "bind error" << endl;
exit(2);
}
if(listen(lsock, 5) == -1)
{
cerr << "listen error" << endl;
exit(3);
}
}
void httpService(int sock)
{
char request[100];
ssize_t s = recv(sock, request, sizeof request - 1, 0);
if(s > 0)
{
cout << request << endl;
string respond = "http/1.0 200 OK\r\n";
respond += "Content-type: text/html\r\n";
respond += "\r\n";
respond += "\
<!DOCTYPE html>\
<html>\
<head>\
<title>most simplfied network</title>\
</head>\
<body>\
<h1>my first network</h1>\
<p>2022.3.17.13:38</p>\
</body>\
</html>\
";
send(sock, respond.c_str(), respond.size(), 0);
}
close(sock);
}
void start()
{
while(1)
{
struct sockaddr_in peer;
socklen_t addrlen = sizeof peer;
int sock = accept(lsock, (struct sockaddr*)&peer, &addrlen);
if(sock == -1)
{
cerr << "accept error" << endl;
continue;
}
cout << "get a new link" << endl;
if(fork() == 0)
{
close(lsock);
httpService(sock);
exit(0);
}
close(sock);
}
}
};
抓包结果
访问结果
用telnet访问:可以得到我们的respond
用网页访问:显示出来的就是响应正文的html
respond编写
模板可以对应一下代码,我这里讲一下respond
string respond = "http/1.0 200 OK\r\n";
respond += "Content-type: text/html\r\n";
respond += "\r\n";
respond += "\
<!DOCTYPE html>\
<html>\
<head>\
<title>most simplfied network</title>\
</head>\
<body>\
<h1>my first network</h1>\
<p>2022.3.17.13:38</p>\
</body>\
</html>\
";
这一行是响应行,version + code + code描述
string respond = "http/1.0 200 OK\r\n";
这一行分别是响应报头和空行。
respond += "Content-type: text/html\r\n";
respond += "\r\n";
这是响应正文:这是html语法,代表的是网站的具体内容。
下面是一个可视化的 HTML 页面结构(只有< body >标签中的内容会显示在浏览器中):
respond += "\
<!DOCTYPE html>\
<html>\
<head>\
<title>most simplfied network</title>\
</head>\
<body>\
<h1>my first network</h1>\
<p>2022.3.17.13:38</p>\
</body>\
</html>\
";
重点讲响应报头
响应报头里面可以写上面讲的header.像Content-types基本是一定要有的。(注意:写这些响应报头的名字的时候,最好格式完全一致。
可以实现一下重定向,通过访问服务器的ip来百度首页。
需要修改的就是code和code描述。并在响应报头那里加上location: https://www.baidu.com
即可。
效果图就不放了。开了fiddler之后访问有可能会失败,因为安全问题可能不允许我访问。但是关闭fiddler之后是可以访问的。
用telnet抓一下respond:
http vs https
http的端口好是80,https的端口号是443.
htt是直接把数据交给传输层的tcp。https并不是这样的,而是先经过一层软件层叫SSL/TLS(这两个东西没什么区别,TLS是标准版,SSL不是标准版)**加密,**然后再把数据给传输层。
对称加密
用对称加密有一个问题,密钥也是数据,这个密钥怎么让对方知道,如果直接发密钥就相当于发一个没有加密的数据了,很危险。
但实际上,https大部分都是用对称加密的,那它是怎么保证传输密钥的时候的安全的呢?
在发送密钥的时候,使用不对称加密。
不对称加密
不对称加密就是我用A密钥加密,你用B密钥解密(RSA,基于因式分解的加密解密算法)。
注:对称的称为密钥。非对称才有公钥私钥之分。
不对称加密对应了两种密钥,一种是公钥,一种是私钥。
通常情况下:
公钥是用来进行加密的,私钥是用来解密的。(只能是)
https对称密钥加密过程(重要)
其实简单来说,就是把对称加密的密钥再加密一次防止被盗取。
怎么加密呢?用非对称加密,先用服务器的公钥为自己的对称密钥加密,再发送给服务器,这样别人就窃取不了了。
服务器得到加密后的对称密钥,可以用自己的私钥来解开公钥的加密。因此双方都得到了对称的密钥。
证书(*)
本地会存放一些证书,可以win + R后输入certmgr.msc去看一下
这些证书可以防止客户端和服务端之间有中间人窃取信息。(具体细节有点难)
防止数据被中间篡改
有可能出现这种情况。
黑客通过更换公钥破解了客户端的信息,并且不断地获取客户端发送地信息。更有可能出现篡改数据的情况。
怎么防止黑客中间篡改数据呢?
我们知道有一种数据结构叫字符串哈希。它可以给一个文本生成对应的哈希值(这个值叫数据摘要)。如果主机发现数据摘要发生变化,那么就证明数据被篡改了。
问题又来了,数据摘要也可能被篡改,因此数据摘要也要用密钥加密起来。加密的摘要被称为数据签名(指纹)
总结来说:知道用哈希来加密文本数据即可。