方法 | 说明 | HTTP协议版本 |
---|---|---|
GET | 获取资源 | 1.0、1.1 |
POST | 传输实体主体 | 1.0、1.1 |
PUT | 传输文件 | 1.0、1.1 |
HEAD | 获得报文首部 | 1.0、1.1 |
DELETE | 删除文件 | 1.0、1.1 |
OPTIONS | 询问支持的方法 | 1.1 |
TRACE | 追踪路径 | 1.1 |
CONNECT | 要求用⒆道协议连接代理 | 1.1 |
LINK | 建立和资源之间的联系 | 1.0 |
UNLINE | 断开连接关系 | 1.0 |
其中我们最常用的为GET和POST方法
GET方法是通过url传参的。
POST方法是通过正文传参的。
- | 类别 | 原因短语 |
---|---|---|
1XX | Informational(信息性状态码) | 接收的请求正在处理 |
2XX | Success(成功状态码) | 请求正常处理完毕 |
3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 |
4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 |
5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 |
HTTP常见的Header
Cookie和Session
什么是Cokkie?
简单来说,Cookie是用来保存个人信息的,它的保存方法有两种,分别为内存级别和文件级别
cookie就是在浏览器当中的一个小文件,文件里记录的就是用户的私有信息。cookie文件可以分为两种,一种是内存级别的cookie文件,另一种是文件级别的cookie文件。
因为单纯的使用Cokkie是不安全的,所以,我们又引入了Session
session的作用
当我们第一次登录某个网站输入账号和密码后,服务器认证成功后还会服务端生成一个对应的SessionID,这个SessionID与用户信息是不相关的。系统会将所有登录用户的SessionID值统一维护起来。
此时当认证通过后服务端在对浏览器进行HTTP响应时,就会将这个生成的SessionID值响应给浏览器。浏览器收到响应后会自动提取出SessionID的值,将其保存在浏览器的cookie文件当中。后续访问该服务器时,对应的HTTP请求当中就会自动携带上这个SessionID。
我们使用SessionID的好处,我们的客户端拿到的只有SessionID,而真正的密码账号在我们的服务器中,从此以后,保护用户信息的任务就落在了服务器上。即使这样在HTTP环境下也是不安全的,我们又引入了HTTPS
再使用http进行信号传输的时候,任然有可能会导致我们传输过程中被不法分子将我们的数据截取进行修改或者监听。
对此,我们HTTPS中又新增加了一层协议层SSL/TLS,主要功能就是进行数据加密、身份验证(安全证书)
加密方式
对称加密
采⽤单钥密码系统的加密⽅法,同⼀个密钥可以同时⽤作信息的加密和解密,这种加密⽅法称为对称加密,也称为单密钥加密,
特征:加密和解密所⽤的密钥是相同的(及时特点也是它的缺点)
使用对称加密的时候,很容易让不法分子从传输过程中进行截取,原因:因为密钥是公开的,所有人都是可以看到和使用的。如图:
非对称加密
非对称加密有两个密钥,一个是公钥,一个是私钥。
两个密钥是不同的,公钥可以公开给任何人使用,而私钥必须严格保密。
公钥和私钥有个特别的“单向”性,虽然都可以用来加密解密,但公钥加密后只能用私钥解密,反过来,私钥加密后也只能用公钥解密。
但即便如此,不法分子还是可以从中间进行窃取的,虽然他没有私钥,但是它有公钥,如图:
不法分子篡改过程:
非对称加密+对称加密
因为非对称加密算法强度复杂、安全性依赖于算法与密钥但是由于其算法复杂,⽽使得加密解密速度没有对称加密解密的速度快,所以如果使用两者结合的方法来进行传输,效率将会提高很多
服务端在使⽤HTTPS前,需要向CA机构申领⼀份数字证书,数字证书⾥含有证书申请者信息、公钥信息等。服务器把证书传输给浏览器,浏览器从证书⾥获取公钥就⾏了,证书就如⾝份证,证明服务端公钥的权威性
解决数据安全问题(引入证书)
CA证书是什么保证数据安全的
1.通过某种公开的散列函数将数据进行运算得到一个散列值,如果中间人修改数据内容,会导致最后算出来的结果与签名中通过加密保存的散列值不同,可以发现已经修改数据内容。
2.如果中间人要修改签名,客户端在进行使用CA公钥进行解密时,由于CA的密钥只有CA持有,所以中间人修改的签名不能通过CA的公钥进行解密,此时也会发现已经修改内容。
Socket 接口
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
struct sockaddr
结构的前16个位。如果是本地通信就设置为AF_UNIX
,如果是网络通信就设置为AF_INET(IPv4)
或AF_INET6(IPv6)
。UDP
的网络通信,我们采用的就是SOCK_DGRAM
,叫做用户数据报服务,如果是基于TCP
的网络通信,我们采用的就是SOCK_STREAM
,叫做流式套接字,提供的是流式服务。// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
现在套接字已经创建成功了,但作为一款服务器来讲,如果只是把套接字创建好了,那我们也只是在系统层面上打开了一个文件,操作系统将来并不知道是要将数据写入到磁盘还是刷到网卡,此时该文件还没有与网络关联起来。
// 开始监听socket (TCP, 服务器)
int listen(int sockfd, 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);
参数说明:
返回值说明:
接收和发送方式
ssize_t recvfrom(int sockfd,void *buf,size_t len,unsigned int flags, struct sockaddr *from,socklen_t *fromlen);
ssize_t 相当于 long int,socklen_t 相当于int
参数说明:
返回值说明:
int sendto (socket s, const void * msg, int len, unsigned int flags, const struct sockaddr * to , int tolen ) ;
参数说明:
返回值说明:
ssize_t read(int fd, void *buf, size_t count);
参数说明:
fd:特定的文件描述符,表示从该文件描述符中读取数据。
buf:数据的存储位置,表示将读取到的数据存储到该位置。
count:数据的个数,表示从该文件描述符中读取数据的字节数。
返回值说明:
如果返回值大于0,则表示本次实际读取到的字节个数。
如果返回值等于0,则表示对端已经把连接关闭了。
如果返回值小于0,则表示读取时遇到了错误。
ssize_t write(int fd, const void *buf, size_t count);
参数说明:
返回值说明:
端口号转换接口
#include
uint32_t htonl( uint32_t hostlong );
uint16_t htons ( uint16_t hostshort );
uint32_t ntohl(uint32_t netlong );
uint16_t ntohs( uint16_t netshort);
IP转换接口
字符串转整数
int inet_aton(const char *cp, struct in_addr *inp);
参数说明:
返回值说明:
in_addr_t inet_addr(const char *cp);
参数说明:
返回值说明:
整数转IP
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
参数说明:
返回值说明:
port端口问题
1、一个进程是否可以bind多个端口号?
可以( 因为一个进程可以打开多个文件描述符,而每个文件描述符都对应一个端口号,所以一个进程可以绑定多个端口号。)
2、一个端口号是否可以被多个进程bind?
不可以(如果一个进程先绑定一个端口号,然后再fork一个子进程,这样的话就实现了多个进程绑定一个端口号,但是不同的进程绑定同一个端口号是不可以的。)
特点
UDP协议
协议的工作只是用来保证发送数据的正确性和统一的
其中udp协议中的报头是定长的8个字节,16位udp长度就是整个udp的长度,两者相减就可以得到数据的大小。我们可以根据定长的报头长度来进行封装和解包。
UDP缓冲区
特点
TCP协议
TCP如何解包?
TCP运行过程
服务端的操作
1.调用socket,创建文件描述符
2.调用bind,将当前文件描述符和ip/port进行网络绑定
3.调用listen启动监听模式,时刻注意是否有客户端发来连接请求。
5.调用accept进行接受以及阻塞。
客户端的操作
1.调用socket,创建文件描述符
2.调用connect, 向服务器发起连接请求;、
每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你从哪里开始发。
发数据1-1000,回复数据1001代表着前1000个数据都收到了。
超时重传机制
发生超时重传的机制会有两种情况:
第一种为从A主机发送到B主机长时间没有收到答复时,此事件称为发生了数据丢包
第二种为确认应答丢失时,会进行超时重传(但是一般多数据发送时,应答丢失是可以忽略的)
这种情况是可以忽略的
流量控制
流量控制的本质就是每次客户端和服务端进行确认应答的时候,服务端会进行将自身的剩余缓冲区空间进行一个上报,服务端如何把窗口大小告诉客户端呢? 回忆我们的TCP首部中, 有一个16位窗口字段, 就是存放了窗口大小信息;
Linux中TCP的流量控制通过滑动窗口机制实现,确保发送方不会以过快的速度向接收方发送数据,从而保证数据传输的可靠性和接收方的正常运行。
滑动窗口
滑动窗口的由来是因为每次单支数据一应一答太慢且占用空间,所以,我们又推出了滑动窗口。
双方在进行TCP通信时可以一次向对方发送多条数据,这样可以将等待多个响应的时间重叠起来,进而提高数据通信的效率。
滑动窗口工作流程:
窗口大小指的是无需等待确认应答而可以继续发送数据的最大值. 下图的窗口大小就是4000个字节(四个段).
发送前四个段的时候, 不需要等待任何ACK, 直接发送;
收到第一个ACK后, 滑动窗口向后移动, 继续发送第五个段的数据; 依次类推;
操作系统内核为了维护这个滑动窗口, 需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答;
只有确认应答过的数据, 才能从缓冲区删掉;窗口越大, 则网络的吞吐率就越高
既然有优点也就会有一定的缺点,因为一次传输的数据多了也会有一定的丢包情况,面对这种情况,我们是这样解决的
情况一: 数据包已经抵达, ACK被丢了
这种情况下, 部分ACK丢了并不要紧, 因为可以通过后续的ACK进行确认。
情况二: 数据包就直接丢了
拥塞控制
它的引入主要原因是,虽然我们使用滑动窗口进行了数据包的管理,但是还是有一些不足之处的,例如:
此处引入一个叫做慢启动的阈值;
就好像谈恋爱一样,刚开始处于热恋期;感情温度升的非常快,之后感觉升温就慢下来了;最后可能还吵了一架,导致感情瞬间到达极小点,如何男生不断的示好,感情也重新开始升温,可是感情的最后点变低了。(当然也可能直接崩了)
TCP拥塞控制这样的过程, 就好像热恋的感觉。
小结:
延迟应答
如果接收数据的主机立刻返回ACK应答, 这时候返回的窗口可能比较小。
那么所有的包都可以延迟应答么? ??肯定也不是;
捎带应答
例如:我们在进行握手的时候,其实就可以捎带的将16位窗口进行设置。
在延迟应答的基础上, 我们发现, 很多情况下, 客户端服务器在应用层也是 “一发一收” 的. 意味着客户端给服务器说了 “How are you”, 服务器也会给客户端回一个 “Fine, thank you”;那么这个时候ACK就可以搭顺风车, 和服务器回应的 “Fine, thank you” 一起回给客户端。
面向字节流
创建一个TCP的socket, 同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区。
例如:
粘包问题
简单来说,粘包问题就是接收的报文多了后,容易搞混分析错,那么我们如何解决呢?
归根结底就是一句话, 明确两个包之间的边界。
对于UDP协议来说, 是否也存在 “粘包问题” 呢?
没有,报文是定长的
对于UDP, 如果还没有上层交付数据, UDP的报文长度仍然在. 同时, UDP是一个一个把数据交付给应用层,就有很明确的数据边界。
站在应用层的站在应用层的角度, 使用UDP的时候, 要么收到完整的UDP报文, 要么不收. 不会出现"半个"的情况。
三次握手
四次挥手
如果客户端没有更多的请求了, 就调用close()关闭连接, 客户端会向服务器发送FIN段(第一次);
此时服务器收到FIN后, 会回应一个ACK, 同时read会返回0 (第二次);
read返回之后, 服务器就知道客户端关闭了连接, 也调用close关闭连接, 这个时候服务器会向客户端发送一个FIN; (第三次)
进程终止: 进程终止会释放文件描述符, 仍然可以发送FIN,它和正常关闭没有什么区别。
机器重启: 它和进程终止的情况相同。
机器掉电/网线断开: 接收端认为连接还在, 一旦接收端有写入操作, 接收端发现连接已经不在了, 就会进行reset, 即使没有写入操作, TCP自己也内置了一个保活定时器, 会定期询问对方是否还在. 如果对方不在, 也会把连接释放。
另外, 应用层的某些协议, 也有一些这样的检测机制. 例如HTTP长连接中, 也会定期检测对方的状态. 例如QQ, 在QQ断线之后, 也会定期尝试重新连接。
TCP小总结
可靠性:
提高性能:
TCP 和 UDP 对比
可靠传输 vs 不可靠传输
有连接 vs 无连接
字节流 vs 数据报
TCP用于可靠传输的情况, 应用于文件传输, 重要状态更新等场景;
UDP用于对高速传输和实时性要求较高的通信领域, 例如, 早期的QQ, 视频传输等. 另外UDP可以用于广播。
TCP作为传输层控制协议,IP则提供了一种能力,即将数据从主机跨网络传送到另一台主机的能力。IP不提供任何可靠性机制。可以理解为IP是一个执行者,而TCP是一个决策者。
IP分片
那么如果TCP传给IP的数据太多,导致IP报文太大怎么处理呢?注意IP是面向数据报的,而不是面向字节流的。
这个时候就需要将IP报文进行分片操作了,分片之后由对端的IP层来进行组装。
比如IP的数据是3400个字节,此时就需要对此IP报文进行分片操作:
分片的过程就是将原有的报头提出,作为其中一个子报文的首部,然后将数据分为几个1480部分,并为它们添加报头。
这一过程是IP层做的,与传输层无关,传输层也不需要知道。
分片五大方向
数据传输过程
数据的分片和组装都是在IP层完成的,上层的传输层和下层的链路层并不关心。
传输层只负责为数据传送提供可靠性保证,比如当数据传送失败后,传输层的TCP协议可以组织进行数据重传。
而链路层的MAC帧只负责,将数据从一个节点传送到和自己相连的下一个节点。
因此,数据的分片和组装完全是由IP协议自己完成的,传输层和链路层不必关心也不需要关心。
小结:
IP网段划分
比如对于192.168.128.10这台主机,它所在的网段号是192.168.128,它的主机号是10。
网络号:保证相互链接的两个网段具有不同的标识。
主机号:同一个网段内,主机之间具有相同的网络号,但是必须有不同的主机号。
同一个网段的两个主机,网段号相同,主机号不同。
通信过程
当一台主机想像另一台主机发消息的时候,会首先查看自己的网络号与当前局域网的网络号是否相同,如果不相同,就会到路由器构成的网络中去查找哪个网络号符合条件,找到符合条件的网络号之后,再到该网络号对应的局域网中根据主机号找到对应的主机。
手动为子网内新增的主机分配节点IP地址时很复杂的事情,有一种技术称为DHCP,能够自动的给子网内新增的主机节点分配IP地址,一般路由器都带有DHCP功能·,因此路由器也可以看做一个DHCP服务器。
IP地址分五大
随着Internet的飞速发展,这种划分方案的局限性很快显现出来,大多数组织都申请B类网络地址, 导致B类地址很快就分配完了, 而A类却浪费了大量地址。
针对这种情况提出了新的划分方案,称为CIDR(Classless Interdomain Routing):
总结来说就是:人为定义一个子网掩码,并规定子网掩码从左到右必须是连续的1或者0(通常首位为1),从而根据网段中的主机量来控制网络号和主机号。
特殊的IP地址和私有IP
解决IP地址不足有以下几种方式:
IP地址(IPv4)是一个4字节32位的正整数. 那么一共只有 2的32次方 个IP地址, 大概是43亿左右. 而TCP/IP协议规定, 每个主机都需要有一个IP地址。这么就意味着IP地址就不够用。
解决方法:
在整个的大网络中,我们会发现,不同的局域网中可以有相同IP的主机。同时我们会发现一个路由器有两份IP地址。这是因为路由器是横跨两个网段的,它可以是该局域网的一部分(网络号和局域网一致),也可以作为一台主机和外面的其他路由器共同构成一个局域网(网络号与更大的局域网一致)。
路由
数据在路由的过程中,实际就是一跳一跳(Hop by Hop)“问路”的过程。所谓“一跳”就是数据链路层中的一个区间,具体在以太网中指从源MAC地址到目的MAC地址之间的帧传输区间。
IP数据包的传输过程也和问路一样
既让数据链路层解决的问题是局域网通信问题,那么局域网通信方式一共有三种,其中我们主要介绍以太网;三种方式分别为:以太网、令牌环网、无线LAN/WAN
也就是说,网络中的路由器会不断去掉数据旧的局域网报头,并添加上新的局域网报头,因此数据在进行跨网络传输时,就算所需跨越的网络采用的是不同的局域网技术,最终也能够正确实现跨越。
注意,在数据链路层和网络层中间也可能有一些协议,来完成不同的工作。因此数据链路层向上提交的协议不同,添加的报头就不同。
它的总体格式可以分为三种,分别是提交给IP,ARP,RARP。
源地址和目的地址是指网卡的硬件地址(也叫mac地址),长度是48位,是网卡出厂时固化的。
认识MAC和理解他和IP的区别
理解MTU
在IP中讲过,由于数据链路层有最大的数据承载量(即IP报文不能超过1500字节),该最大承载量称为MTU,因此需要对IP报文进行分片处理。同时也有最小的大小规定,即如果小于46字节要在后面添加补充位。
MTU对UDP影响
UDP是面向数据报的,它是没有缓冲区的:
一旦UDP携带超过1472(1500-20(IP首部)-8(UDP首部)),那么就会在网络层分为多个数据包。这样多个IP数据包有任意一个丢失,都会引起接收端网络层重组失败,那么意味着,如果UDP数据报在网络层被分片,整个数据被丢失的概率就大大增加了。
MTU对TCP协议的影响
TCP是面向字节流的,它是有缓冲区的:
这样就可以通过TCP来控制向网络层发送的数据量的多少了,但是其实TCP的数据长度也不能无限长,它也有一个限制,称为MSS。
TCP在建立链接的过程中,通信双方会进行MSS协商。
最理想的情况下,MSS的值正好是在IP不会分片处理的最长长度(这个长度仍然是受限于数据链路层)。
双方在发送SYN的时候会在TCP头部添加自己能支持的MSS值。
然后双反得知对方的MSS的值之后,选择最小的作为最终MSS。
MSS的值就是在TCP首部的40字节变长选项中(kind=2)。
MTU对IP协议的影响
由于数据链路层MTU的限制, 对于较大的IP数据包要进行分包
将较大的IP包分成多个小包, 并给每个小包打上标签
每个小包IP协议头的 16位标识(id) 都是相同的
每个小包的IP协议头的3位标志字段中, 第2位置为0, 表示允许分片, 第3位来表示结束标记(当前是否是最后一个小包, 是的话置为1, 否则置为0)
到达对端时再将这些小包, 会按顺序重组, 拼装到一起返回给传输层
一旦这些小包中任意一个小包丢失, 接收端的重组就会失败. 但是IP层不会负责重新传输数据
ARP协议(属于网络层的子层,在IP地址到MAC地址的映射过程中起到重要的作用。)
虽然我们在这里介绍ARP协议, 但是需要强调, ARP不是一个单纯的数据链路层的协议, 而是一个介于数据链路层和网络层之间的协议
ARP协议建立了主机 IP地址和 MAC地址的映射关系
当MAC协议层通过对有效载荷的解析发现是ARP的请求或者应答的时候,会将报文直接交给ARP协议,ARP协议对报文进行处理之后再返回给MAC协议,此时就不会经过网络层,如果MAC协议通过解析发现是一个IP协议的话,就会将数据发送给网络层,并一层一层向上处理。
发送返回报文
假设主机A向主机B发消息,其中MACA和MACB分代表两者的MAC地址,IPA和IPB分别代表两者的IP地址。
由于在请求的时候,还并不知道MACB的值,因此将目的MAC地址填为全F。同时数据链路层有要求,发送的数据不能小于46个字节,不能大于1500个字节,因此需要后面的PAD来补位18位。
在发送请求报文的时候,是以广播的形式发送的,这就意味着所有的主机都需要对该报文进行解包,解包之后会先看op(因为代表的是请求还是响应),然后再看IP地址。这是因为局域网中任何一台主机既可以向别人发起ARP,也可以被别人发起ARP。
ARP缓存
由于每次都进行ARP的效率太低了(每次有传输报文的时候都要发送一次广播一次ARP),因此系统中会将ARP做一些缓存,一定时间后再清理缓存。
RARP协议
RARP协议就是将ARP协议反过来,根据MAC地址找到IP地址,也是通过先广播后单向传输的方式。