目录
3、CA机构
一、预备知识
1、IP地址与端口号
ip地址:ip地址唯一的标识互联网中的一台主机。
端口号(port):唯一的标识一台机器上的一个进程。在一台机器上每个进程的标识都是独一无二的,但是在不同机器上的进程标识可以相同。
PID和端口号都是用来标识进程的,他们两个类似于身份证号和学号的区别。身份证号在全国都通用,学号只在学校适用,在学校里通常用学号而不是用身份证号进行管理,原因是学号能更好的反映出学生在学校中特定的信息,比如年纪班级等,管理起来更方便。同时学号还有一个作用,那就是形成了一套自己的管理体系,假如有一天因为特殊原因人们的身份证号需要变动,学号形成的管理体系也不会受影响,在一定程度上完成了解耦。端口号也是如此,只在特定的机器上适用。
总结:ip地址代表某一台主机,端口号代表主机上的一个进程,IP地址 + 端口号能够标识网络上的某一台主机的某一个进程,是互联网中的唯一标识。网络通信就是把数据从某台机器的某个进程交到另一台机器的某个进程,所以网络通信本质上是进程间通信。
注意:一个进程可以绑定多个端口号,但是一个端口号不能被多个进程绑定。
2、网路传输四层协议
应用层下面紧挨着的是传输层,传输层协议(TCP和UDP)的数据段中有两个端口号, 分别叫做源端口号和目的端口号,就是在描述“数据是谁发的,要发给谁"。TCP协议是一种可靠的传输,UDP是一种不可靠的传输。
3、网络字节序
我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?
- TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。
- 不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据。
- 如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可。
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。
其中h代表主机,n代表网络,l代表32位长整数,s代表16位短整数。如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回。如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回。
4、socket编程
socket(套接字)是网络通信的桥梁。
4.1 socket 常见API
这些与socket相关的API是传输层暴露出来的接口,通过调用这些接口可以实现网络通信,这个过程是在用户层完成的。
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address,socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
各接口解释:
- socket (),创建socket的过程,本质是打开文件,文件中仅仅有与系统相关的内容
- bind (),本质是将ip和port与文件信息进行关联
- listen (),本质是设置该socke的状态,允许别人来链接我
- accept (),获取新链接到应用层,是以fd为代表的,当有多个链接连上我们的服务器时,操作系统要对他们进行管理,在OS看来,每一个链接都相当于一个文件结构体。
- connect (),本质是发起连接。
- read ()和write (),本质就是进行网络通信,但在用户看来,就是进行普通的文件读写操作。
4.2 sockaddr结构
socket API是一层抽象的网络编程接口,适用于各种底层网络协议。然而, 各种网络协议的地址格式并不相同。
- IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16位端口号和32位IP地址。
- socket API可以都用struct sockaddr *类型表示(由于创建socket接口的时候还没有void*的概念), 在使用的时候需要强制转化成sockaddr_in。这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型sockaddr结构体指针做为参数。只需要在函数内部取前十六位即可判断是什么类型。
sockaddr_in 结构
虽然socket api的接口是sockaddr, 但是我们真正在基于IPv4编程时, 使用的数据结sockaddr_in。这个结构里主要有三部分信息: 地址类型, 端口号, IP地址。
in_addr结构
in_addr用来表示一个IPv4的IP地址。 其实就是一个32位的整数。
二、应用层协议
应用层主要由三部分组成,分别是网络通信,序列化和反序列化,应用层协议细节。上面提到的socket编程主要完成了网络通信,保证了两台主机可以通过网络交互,那么这些数据具体是怎样传输的呢?
网络计算器
我们可以通过一个小项目来理解序列化和反序列化以及什么是应用层协议。例如, 我们需要实现一个服务器版的加法器. 我们需要客户端把要计算的两个加数发过去, 然后由服务器进行计算, 最后再把结果返回给客户端。为了数据能够被正确的解析解析,我们需要进行一定的约定。
约定方案一:
- 客户端发送一个形如"1+1"的字符串;
- 这个字符串中有两个操作数, 都是整形;
- 两个数字之间会有一个字符是运算符, 运算符只能是 + ;
- 数字和运算符之间没有空格;
- ...
像这样的约定就是一个应用层协议。保证了一端发送时构造的数据, 在另一端能够正确的进行解析,这就是应用层协议。上面这种协议有一定缺陷,因为数据没有进行封装,用户过多时容易造成混淆。
约定方案二:
- 定义结构体来表示我们需要交互的信息;
- 发送数据时将这个结构体按照一个规则转换成字符串, 接收到数据的时候再按照相同的规则把字符串转化回结构体;
网络通信时数据交互都是以字符串的形式,如果直接以结构体的形式进行交互容易产生版本不兼容的问题,传输时需要对数据转化为字符串类型,等传送完毕后再转换回去。数据类型来回转化的过程叫做 "序列化" 和 "反序列化"。有了序列化和反序列化后,上层可以更好的使用内部成员,将应用和网络进行了解耦。
三、http协议
虽然我们说, 应用层协议是我们程序猿自己定的。但实际上, 已经有大佬们定义了一些现成的, 又非常好用的应用层协议, 供我们参考使用,HTTP(超文本传输协议)就是其中之一。
1、认识URL
URL就是我们平常说的“网址”。
我这篇博客的URL如下:
在这些信息中,登录信息现在已经很少呈现在URL中了,服务器端口号一般也是隐藏的,因为每种协议和端口号一般都是固定对应的,不需要特意指明。ip地址也就是服务器地址由于不方便记忆,一般以域名的形式呈现出来,是一一映射的关系。在域名后面跟的是路径。
下面我们来谈一个概念,方便更好的理解URL:我们请求的图片,视频,音频,文档等这些都称之为“资源”。linux或者传统的操作系统都是以文件的方式保存资源的。通过IP和路径就可以确认唯一一个网络资源。所以通过URL就可以访问某个特定的网络资源。
2、HTTP协议格式
HTTP请求:
http请求组成分为四部分:
- 请求行:请求方法 + url(去掉域名后的内容)+ 版本号
- Header: 请求的属性, 冒号分割的键值对
- 空行
- 请求正文(如果没有则不显示)
HTTP响应
http响应也分为四部分:
- 状态行: 版本号 + 状态码 + 状态码解释
- Header: 请求的属性, 冒号分割的键值对
- 空行
- 请求正文(如果没有则不显示)
前三部分为http报头,最后一部分为有效载荷。在传输过程中直接用一个长字符串将四部分全传过去,收到后通过空行解包,用空行将字符串一分为二,空行前为报头,空行后为正文。属性中的Content-Length表明了正文有多少个字节,通过Content-Length可以读取相应的字节数,保证了不会把下一个http数据误读。
看上面这个http请求,其中的/代表的并不是根目录,而是web根目录。我们发送请求时一般请求的是一个资源,如果请求的是/,意味着请求该网站的首页。index.html 或 index.htm。一般所有的网站都要有默认主页。
3、http协议各部分介绍
3.1 HTTP常见属性
- Content-Type: 数据类型(text/html等)
- Content-Length: Body的长度
- Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;
- User-Agent: 声明用户的操作系统和浏览器版本信息;
- referer: 当前页面是从哪个页面跳转过来的;
- Connection:表示支不支持长链接
- location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问;
- Cookie: 用于在客户端存储少量信息。通常用于实现会话(session)的功能;
Cookie介绍:
首先介绍两个结论:
(1)在网站中,网站是认识我们的。
相信大家都有这样的经历,在登陆一个网站输入密码后,如果一定时间内再次登陆时不需要输入密码会默认登上我们的账号。还有用爱奇艺看视频时,服务器需要判断我们是不是vip用户从而分配相应的资源权限,但是我们只需要输入一次账号,服务器以后在申请资源时就可以自己判断出我们是否是vip用户。这些样例都告诉我们一个结论:在网站中,网站是认识我们的。在进行各种界面跳转也就是进行各种http请求时网站都一直认识我们。
(2)http协议本质是一种无状态的协议。
http协议无状态指的是http协议并没有记忆功能。但是http可以提供一些技术支持,来保证网站具有“会话保持”的功能。这个功能是通过Cookie完成的。
cookie:
cookie是http协议中的一个属性,本质其实是浏览器中的一个文件,里面保存的是用户的私密信息(账号密码等)。http协议规定,如果浏览器中有cookie,那么无论发送任何请求,都会在请求时携带cookie信息。举一个例子:在第一次登陆时服务器识别到我们的账号密码,然后在发送响应时把我们的账号密码写到cookie属性中传给浏览器保存起来。之后浏览器的每次请求都会在包头属性中携带cookie信息,服务器就可以根据cookie信息判断用户身份。
我们通过在网址左侧的按键查看正在使用的Cookie,如下图所示。如果把其中我们的用户信息删除,那么再次访问该网站时就要重新输入账号。
我们要注意一点:单纯使用Cookie是存在安全隐患的,Cookie保存在浏览器中,既然我们能看到那别人也可以通过一些特殊手段盗取我们的Cookie。盗取后则可以通过我们的身份访问特定的资源,还可能识别到我们的用户和密码。所以实际情况一般是这样的:在服务器识别到我们的相关信息后,会在服务器中形成一个session文件保存这些信息,并对session进行唯一标识,然后把这个标识传回浏览器中保存到cookie中,之后只需要通过这个标识来确认身份即可。这样可以避免我们的信息被泄漏,但是依旧不能防止别人盗取session标识访问特定的资源,所以也相应的产生了一些防御手段,例如异地登陆检测。
3.2 HTTP的方法
短链接和长链接 :
1.0和1.1为http协议的版本号,1.0只支持短链接,1.1支持长连接。
一般而言一个网页是由多个元素构成的,多个元素就代表多个资源。再用http/1.0访问一个网页就要进行多个http请求,重复多次建立链接->传送数据->断开链接的过程,十分耗时。http/1.1支持长链接,只进行一次链接就能访问多个资源。可以通过属性中的Connection判断是否支持长链接。
方法和POST方法区别:
方法中最常用的是GET方法和POST方法。
概念区别:
- GET方法叫做获取,是最常用的方法,一般获取网页的方法都是默认使用GET方法。GET方法也可以提交参数,通过URL进行参数拼接从而提交给server端。
- POST方法叫做推送,是提交参数最常用的方法。
提交参数的区别:
- GET和POST提交参数的位置不同,POST比较私密,参数不回回显到浏览器的url输入框。GET方法不私密,参数会回显。
- GET通过url传参,url一般是有大小限制的,和具体的浏览器有关,而POST一般没有大小限制。
结论:
- 如果提交的参数非常小,不敏感,可以用GET,否则用POST。
3.3 HTTP的状态码
最常见的状态码, 比如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)。
由于参与用户层的人水平参差不齐,很多人并不清楚如何使用http的状态码,而且浏览器的种类也有很多,对http状态码的处理没有一个统一的标准,所以状态码一般并不能对浏览器有很好的指导意义。
3XX重定向概念解释:有时候网站的ip地址会更新,为了防止老用户找不到新网址,一般服务器会进行重定向操作。http请求属性中的Location中保存了要跳转的新网址,服务器要告诉浏览器要跳转到哪里。
- 永久重定向301:当访问一个网站时,会跳转到另一个。
- 临时重定向302,307:当访问某种资源时会跳转,等访问完毕后又会跳转回来。例如微信支付输密码的时候,美团下单的时候,都会自动跳转回来。
四、https协议
从上面的学习我们也可以看出,http协议的传输过程中,数据都是裸奔的,非常不安全。所以一般现在使用的都是https协议,对数据进行加密处理。https=http+TLS/SSL,TLS和SSL为数据的加密和解密层,位于TCP层和http层之间,在数据的传输过程中都会进行加密和解密操作。
1、加密方式
(1)对称加密
对称加密的密钥X只有一个,举一个最简单的用X加密解密的例子,通过简单的或运算就可以实现:
加密:data^X=result
解密:result^X=data
由此可见密钥本质就是一个算法。类似于求导和积分的关系。
(2)非对称加密:
非对称密钥有一对,分别是公有密钥和私有密钥。需要保证公有密钥加密只能用私有密钥解密,或者公钥加密只能用私钥解密。
一般而言公有密钥是全世界公开的,私有密钥必须自己保存。
2、加密过程
通过对称加密来保护数据是不可行的,因为对称加密的密钥只有一把,服务器和客户端想要进行加密和解密必须告知对方密钥是什么,也就是把密钥是什么传给对方,在这传送过程中密钥很有可能被别人窃取,加密也就失去了意义。
通过非对称加密理论上是可行的,服务器和客户端各自建立一对公钥和私钥,然后在开始时互换公钥,之后的数据都通过对方的公钥加密,这样发送的数据也就只能有对方才能解密,实现了双向安全。但是实际上这并不可行,因为非对称加密算法是及其浪费时间的。
实际中的加密是对称加密和非对称加密一起配合完成的,首先在服务器建立非对称密钥,然后把公钥给客户端,此时客户端创建一把对称密钥X,然后用公钥对X进行加密后传给服务器,服务器再通过私钥解密获得X。这时候对称密钥X就非常安全的进行了传递,之后通过对称密钥X进行加密解密就可以了。
3、CA机构
通过对称加密和非对称加密一起完成依旧可能出现安全问题,因为在刚开始交互密钥的时候总是没办法受到保护的,很容易被中间人使用狸猫换太子的手段截取。比如服务器建立非对称密钥私钥S',然后把公钥S传给客户端,在这个过程中是不受保护的,中间人可以很轻易的截取公钥然后掉包,比如自己建立一个公钥M替换S。客户端拿到M后,建立一个对称密钥X,通过M加密然后传回服务器,此时再次被中间人截取,通过手里的私钥M'就能解密拿到X,此时再用之前拿到的公钥S加密,然后传给服务器。服务器用S'解密时不会有任何问题。但对称密钥早已经被中间人神不知鬼不觉的窃取了。
为了解决这个问题,CA机构产生了。CA机构是一个权威的证书机构,只有通过CA机构的认证,才能证明一个服务商是合法的。CA机构会通过一个Hash算法对企业的信息(域名加公钥)进行处理创建出一个字符串序列,只要信息发生一丁点变化,根据Hash算法产生后的字符串序列就会有很大差别。然后CA机构创建私钥对字符串序列进行加密生成该公司的数字签名,再把数字签名和企业信息一起打包形成一张证书。因为证书信息中包含企业的公钥,所以企业和用户端对接的时候直接传证书就行了。用户拿到证书后再用CA机构的公钥对数字签名进行解密(CA机构的公钥是人尽皆知的),解密后再通过Hash算法处理企业信息,如果得到的值和数字签名有差距就说明信息已经被修改了。这样就避免了数据被中间人修改。中间人想要修改数据必须先修改数字签名,中间人当然能拿到并解密数字签名,因为CA机构的公钥人尽皆知。但是拿到了也没什么用,因为中间人没有私钥,如果不用特定的私钥生成数字签名用户在解密的时候就会察觉。所以CA机构用打名牌的方法就能避免中间人掉包数据。
总结
本文主要简单介绍了网络传输中的的用户层协议,希望能给大家带来帮助。江湖路远,来日方长,我们下次见。