network

cookie、session和Token

cookie 以key-value的形式存储在浏览器的一种数据。拥有domain、path、maxAge等属性,可以实现父子域名之间的数据传输。此外cookie是有大小限制,并且value只能使用string字符串。
cookie应用流程:检查浏览器是否存在用户登录的cookie,未存在则转到登录界面。用户登录成功后,存储自身的cookie,然后在其他交易中验证用户是否合法。

session 也是以key-value的形式存储数据,其中key存储在用户的浏览器,而value值存储在服务器。
相较于cookie,session更加安全,生成、检验、验证都是在服务器完成。传输方面更加便捷,只需要传输对应的sessionId即可。不足之处就是在分布式环境下,session之间不能共享。但是也有解决办法使用redis或者其他的缓存集群,但这也引入了其他的问题。

token 令牌则是通过加密的用户信息和签名来验证用户。这样解除了服务器需要存储sessionId的负担,以后在需要时只需要对token 解密即可。
token流程:用户登录。用户登录成功,将用户信息依靠加密算法生成token,返回给客户端。客户端接收到token之后存储在本地,然后再每次调用后端请求时在http的header中携带token。后端在接收到请求后解析http的header中的token校验有效性。

TCP

由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。

三次握手:
第一步首先客户端的应用程序将会使tcp进程发送SYN,MSS(最大报文段长度),此时客户端处于SYN_SENT状态,报文传输到服务器端后。
第二步服务器端tcp接收到后服务器端将会由LISTEN状态变为SYN_RCVD状态,然后服务器端也会发送一个SYN,MSS还有一个ACK,注意这个ACK是客户端发送的SYN值加1。
第三步客户端在接收到服务器端的SYN,MSS,ACK核对无误后将会由SYN_SENT状态变为ESTABLILSHED,此时客户端的connect()函数将会返回不再处于阻塞状态,同时客户端向服务端发送ACK,此ACK是服务器端发送的SYN值加1,服务器端在接收到客户端的ACK核对无误后,accept()将从阻塞状态返回,同时read()处于阻塞状态。此时连接已经建立,服务端处于ESTABLILSHED状态。

1、客户端发送一个带SYN标志的TCP报文到服务器。
2、服务器端回应客户端的这个报文同时带ACK标志和SYN标志。因此ACK标志表示对刚才客户端SYN报文的回应;同时又标志SYN给客户端,询问客户端是否准备好进行数据通讯。
3、客户在接收到服务端的SYN报文后必须再次回应服务段一个ACK报文。

四次挥手:
第一步:当一端数据已经发送完了,就会将本端的tcp断开掉,但通常是客户端主动断开,这种情况同时是客户端应用程序调用close(fd)关闭套接字,这将触发tcp进程发送 FIN,此时客户端将会处于FIN_WAIT_1。
第二步:服务器端在接收到这个FIN后将会处于close_wait()状态,同时read()return 0,然后服务器端将会发送ACK 值为客户端发送的FIN值加1,此时服务端处于CLOSE_WAIT 状态。
第三步:客户端在接收到服务器端发送给它的ACK后将会处于FIN_WAIT2状态,然后服务器端将客户端的文件描述符读端关闭。此时服务器端可能还会有未发送的数据,通常会悄悄丢弃掉,然后 关闭客户端描述符close(),然后服务器端tcp进程将会发送FIN到客户端,此时服务器端将会处于LAST_ACK状态。
第四步:客户端在接收到服务器端发送的FIN后将会由FIN_WAIT2状态变TIME_WAIT状态,同时发送ACK值为客户端发送的FIN值加1,服务器端在接收到客户端发送的ACK后核对无误后将由LAST_ACK状态变为 CLOSED状态。注意客户端在处于TIME_WAIT状态时要经历 2个MSL 时间才会将状态变为CLOSED,通常这个等待的时间为60秒。

1、TCP客户端发送一个FIN,用来关闭客户到服务器的数据传送。
2、服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。
3、服务器关闭客户端的连接,发送一个FIN给客户端。
4、客户段收到这个FIN然后发回ACK报文确认,并将确认序号设置为收到序号加1。

tcp粘包和拆包问题:
应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包。
应用程序写入的数据大于套接字缓冲区大小,这将会发生拆包。
解决办法:
包头部添加报文长度:发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了。
报文封装成特定长度:发送端将每个数据包封装为固定长度(不够的可以通过补0填充),这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。
包末尾添加特殊分隔符:可以在数据包之间设置边界,如添加特殊符号,这样,接收端通过这个边界就可以将不同的数据包拆分开。

为什么为三次握手而不是两次握手?
为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤 如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认。

为什么三次握手四次挥手:
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。
但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了; 但未必你所有的数据都全部发送给对方了,所以你可以未 必可以马上关闭SOCKET,也需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文 和FIN报文多数情况下都是分开发送的。

TCP状态
相关TCP状态解释: LISTEN: 侦听来自远方的TCP端口的连接请求;
SYN-SENT: 在发送连接请求后等待匹配的连接请求;
SYN-RECEIVED: 在收到和发送一个连接请求后等待对方对连接请求的确认;
ESTABLISHED: 代表一个打开的连接;
FIN-WAIT-1: 等待远程TCP连接中断请求, 或先前的连接中断请求的确认;
FIN-WAIT-2: 从远程TCP等待连接中断请求;
CLOSE-WAIT: 等待从本地用户发来的连接中断请求;
CLOSING: 等待远程TCP对连接中断的确认;
LAST-ACK: 等待原来的发向远程TCP的连接中断请求的确认;
TIME-WAIT: 等待足够的时间以确保远程TCP接收到连接中断请求的确认;
CLOSE: 没有任何连接状态;

服务器的状态可以用如下的流程来表示:
CLOSED -> LISTEN -> SYN收到 -> ESTABLISHED -> CLOSE_WAIT -> LAST_ACK -> CLOSED

客户端的状态可以用如下的流程来表示:
CLOSED -> SYN_SENT -> ESTABLISHED -> FIN_WAIT_1 -> FIN_WAIT_2 -> TIME_WAIT -> CLOSED

TCP连接的 TIME_WAIT 和 CLOSE_WAIT 状态

终端输入命令 "netstat -anp | grep TIME_WAIT | wc -l" 查看一下, 接着发现有几百几千甚至几万个TIME_WAIT 连接数。
打开 sysctl.conf 文件,修改以下几个参数: vim /etc/sysctl.conf
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_fin_timeout = 30
相关查看命令 # " netstat -n | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key,"\t",state[key]}' "
会得到类似下面的结果,具体数字会有所不同:
LAST_ACK 1
SYN_RECV 14
ESTABLISHED 79
FIN_WAIT1 28
FIN_WAIT2 3
CLOSING 5
TIME_WAIT 1669
这里凭直觉看,TIME_WAIT 并不可怕,CLOSE_WAIT 才可怕(因为如果 CLOSE_WAIT 如果大量出现则说明代码出现问题,不能合适的关闭socket连接。)
TIME_WAIT有什么用?
1、防止前一个连接上延迟的数据包或者丢失重传的数据包,被后面复用的连接接收。(TIME_WAIT 很短或者没有,在新的连接建立后,上一个连接丢失的包因为没有收到ACK,会对当前的连接进行重传。)
2、确保连接方能在时间范围内,关闭自己的连接。(客户端在 TIME_WAIT 阶段的ACK包丢失,会导致服务端一直处于 LAST_ACK 状态,而客户端在处于CLOSED状态后,有新的连接进来发送SYN包,此时服务端就会恢复RST包,无法建立连接。)
3、这个 TIME_WAIT 定义,更多的是一种保障确保最后即便是丢失了ACK,被动关闭的一方再次重发FIN并等待回复的ACK,一来一去两个来回。

TCP拥塞控制

为了防止网络的拥塞现象,TCP提出了一系列的拥塞控制机制。最初的TCP的拥塞控制由 慢启动(Slow start)、拥塞避免(Congestion avoidance)、快重传(Fast retransmit)、快恢复(Fast Recovery) 算法。

拥塞窗口cwnd:发送方为一个动态变化的窗口叫做拥塞窗口,拥塞窗口的大小取决于网络的拥塞程度。拥塞窗口会随着发送的进行变化。

慢启动的思路:慢启动算法就是在主机刚开始发送数据报的时候先探测一下网络的状况,如果网络状况良好,发送方每发送一次文段都能正确的接受确认报文段。那么就从小到大的增加拥塞窗口的大小,即增加发送窗口的大小,让cwnd 变为上一次的两倍。

拥塞避免思路:是让 cwnd 缓慢的增加而不是加倍的增长,每经历过一次往返时间就使 cwnd 增加1,而不是加倍,这样使cwnd缓慢的增长,比慢启动要慢的多。
为了防止cwnd增加过快而导致网络拥塞,所以需要设置一个慢开始门限 ssthresh 状态变量(我认为他是一个拥塞控制的标识),它的用法:
1. 当cwnd < ssthresh,使用慢启动算法,
2. 当cwnd > ssthresh,使用拥塞控制算法,停用慢启动算法。
3. 当cwnd = ssthresh,这两个算法都可以。

AIMD(加法增大乘法减小)
1. 乘法减小:无论在慢启动阶段还是在拥塞控制阶段,只要网络出现超时,就将 cwnd 置为1,ssthresh 置为 cwnd 的一半,然后开始执行慢启动算法(cwnd小于ssthresh)。
2. 加法增大:当网络频发出现超时情况时,ssthresh 就下降的很快,为了减少注入到网络中的分组数,而加法增大是指执行拥塞避免算法后,是拥塞窗口缓慢的增大,以防止网络过早出现拥塞。
这两个结合起来就是AIMD算法,是使用最广泛的算法。拥塞避免算法不能够完全的避免网络拥塞,通过控制拥塞窗口的大小只能使网络不易出现拥塞。

快重传:
快重传算法首先要求接收方每收到一个失序的报文段就立即发出重复确认(为的是使发送方及早的知道有报文段没有到达对方)而不要等到自己发送数据时才捎带确认。
快重传算法规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待为其设置的重传计时器到期。

快恢复:
1. 当发送发连续接收到三个确认时,就执行乘法减小算法,把慢启动开始门限(ssthresh)减半,但是接下来并不执行慢开始算法。
2. 此时不执行慢启动算法,而是把 ssthresh 设置为 cwnd 的一半, 然后执行拥塞避免算法,使拥塞窗口缓慢增大。

HTTP、HTTPS

HTTP:超文本传输协议,是一个基于请求与响应,无状态的,应用层的协议,常基于TCP/IP协议传输数据。

HTTP 协议特点:

无状态:客户端与服务端之间不保存状态。 无连接:在HTTP1.1 之前由于无状态特点,每次请求都需要完成TCP三次握手四次挥手,和服务器重新建立连接。 基于请求和响应:基本特性,客户端请求,服务端响应。 简单快速、灵活。 通信使用明文,请求和响应不会对通信方进行确认,无法保护数据的正确性。

HTTPS: 是一种通过计算机网络进行安全通信的传输协议,经由HTTP进行通信,利用SSL/TLS建立全信道,加密数据包。HTTPS使用的主要目的是提供对网站服务器的身份认证,同时保护交换数据的隐私与完整性。
TLS是传输层加密协议,前身是SSL协议。

HTTP1.2 的多路复用:通过单一的HTTP/2连接请求发起多重的请求-响应消息,多个请求流 stream 共享一个TCP连接,实现多个流(stream)并行而不是依赖建立多个TCP连接。

HTTPS 协议特点:

内容加密:采用混合加密技术,中间者无法查看明文内容。 验证身份:通过证书认证客户端访问的是自己的服务器。 保护数据完整性:防止传输中间被中间人修改或者顶替。

HTTPS 实现步骤:

1、client向server发送请求https://baidu.com,然后连接到server的443端口,发送的信息主要是随机值1和客户端支持的加密算法。
2、server接收到信息之后给予client响应握手信息,包括随机值2和匹配好的协商加密算法,这个加密算法一定是client发送给server加密算法的子集。
3、随即server给client发送第二个响应报文是数字证书。服务端必须要有一套数字证书,可以自己制作,也可以向组织申请。区别就是自己颁发的证书需要客户端验证通过,才可以继续访问,而使用受信任的公司申请的证书则不会弹出提示页面,这套证书其实就是一对公钥和私钥。传送证书,这个证书其实就是公钥,只是包含了很多信息,如证书的颁发机构,过期时间、服务端的公钥,第三方证书认证机构(CA)的签名,服务端的域名信息等内容。
4、客户端解析证书,这部分工作是由客户端的TLS来完成的,首先会验证公钥是否有效,比如颁发机构,过期时间等等,如果发现异常,则会弹出一个警告框,提示证书存在问题。如果证书没有问题,那么就生成一个随即值(预主秘钥)。
5、客户端认证证书通过之后,接下来是通过随机值1、随机值2和预主秘钥组装会话秘钥。然后通过证书的公钥加密会话秘钥。
6、传送加密信息,这部分传送的是用证书加密后的会话秘钥,目的就是让服务端使用秘钥解密得到随机值1、随机值2和预主秘钥。
7、服务端解密得到随机值1、随机值2和预主秘钥,然后组装会话秘钥,跟客户端会话秘钥相同。
8、客户端通过会话秘钥加密一条消息发送给服务端,主要验证服务端是否正常接受客户端加密的消息。
9、同样服务端也会通过会话秘钥加密一条消息回传给客户端,如果客户端能够正常接受的话表明SSL层连接建立完成。

HTTP TRACE 请求: trace 请求方法用于http 请求的测试和诊断。根据协议,服务器在收到trace 请求后,应回显所收到的数据,即服务器返回自己所收到的数据。

OSI七层模型和TCP/IP四层模型

OSI七层模型:
1、物理层。比特流传输。常见协议:IEEE802.1A。
2、数据链路层。控制网络层与数据链路层的通信。常见协议:PPP。
3、网络层。IP寻址和路由选择。常见协议:IP、ARP(地址解析协议,基于IP与MAC之间的映射关系,询问目标IP的MAC地址)。
4、传输层。建立、维护、管理端到端的链接。常见协议:TCP和UDP协议。
5、会话层。建立、管理、维护会话连接。常见协议:SMTP、DNS协议。
6、表示层。数据格式化、加密、解密。常见协议:SNMP。
7、应用层。为应用程序提供网络服务。常见协议:HTTP、HTTPS。

TCP/IP四层模型:
1、数据链路层。
2、网络层。
3、传输层。
4、应用层。

DNS的解析过程

DNS解析分为两种:正向解析和反向解析。
正向解析:将域名转化为IP,应用于浏览器输入网址域名时。
反向解析:根据IP查询对应的域名,经常在后台程序用到。

DNS解析过程:
1、浏览器会检查缓存中是否存在该域名对应的IP地址,如果存在就会解析结束。
2、如果浏览器的缓存中未找到,就会找到本机的hosts文件检查是否存在这个IP的映射关系,如果存在就调用完成解析过程。
3、如果hosts中没有配置,则在本地的DNS解析器中查询是否存在对应的IP域名映射,如果存在返回浏览器,完成解析。
4、本地hosts和本地DNS解析器都没有查找到时,就会查找本地的DNS服务器,DNS服务器接收到对应的请求后,返回结果给客户机完成解析。大部分的DNS解析在这里就会完成,如果还没有解析出来就会去到根DNS服务器。
5、本地的DNS服务器未查询到映射,就会去到根DNS服务器,而根DNS服务器则会根据收到的请求发完对应的域名DNS服务器来完成解析。

BIO、NIO、AIO、select、poll、epoll

同步与阻塞更多的是关注应用程序,关注的是程序中间的协作关系。
阻塞与非阻塞更关注的单个进程的执行状态。
IO处理过程:数据通过网关达到内,内核准备好数据。数据再从内核缓存写入用户缓存。

BIO:同步阻塞IO,一个客户端连接对应一个服务端连接。
NIO:同步非阻塞IO。(java中的实现:Buffer(缓冲区)、Channel(通道)、Selector(多路复用器))
AIO:异步非阻塞IO。
select:知道存在IO事件发生,但是不知道是哪一个流,所以需要轮询所有的流,找出其中可以读取数据或者写入数据的流,对其进行操作。
poll:与select 类似,他将用户传入的数组拷贝到内存空间,然后查询每个fd对应的状态,但是它没有最大连接数限制,原因是它基于链表来存储的。
epoll(Linux 内核特有):可以理解为event poll,不同于忙轮询和无差别轮询,epoll会将哪个流发生IO事件会通知。所以epoll实际上是基于事件驱动的。
表面上epoll 的性能最好,但是在连接数较少并且连接都不活跃的情况,select和poll的性能可能比epoll,毕竟epoll的通知机制需要很多的回调。

kcp协议

KCP 是一个快速可靠协议,能以比 TCP 浪费10%-20%的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降低三倍的传输效果。
其中使用纯算法实现,并不负责底层协议(如UDP)的收发,需要使用者自己定义下层数据的发送方式,以 callback的方式提供给 KCP。连时钟都需要外部传递进来,内部不会有任何一次系统调用。
TCP是为流量设计的(每秒内可以传输多少KB的数据),讲究的是充分利用带宽。而 KCP是为流速设计的(单个数据从一端发送到一端需要多少时间),以10%-20%带宽浪费的代价换取了比 TCP快30%-40%的传输速度。

KCP协议的数据段格式:

补充:传输层的数据叫作段(segment),网络层的数据叫作包(packet),数据链路层的数据叫作帧(frame),物理层的数据叫作流(stream)。

KCP协议特征:
1. RTO不翻倍
RTO(Retransmission-TimeOut)即重传超时时间,TCP是基于ARQ协议实现的可靠性,KCP也是基于ARQ协议实现的可靠性,但TCP的超时计算是RTO*2,而KCP的超时计算是RTO*1.5,也就是说假如连续丢包3次,TCP是RTO*8,而KCP则是RTO*3.375,意味着可以更快地重新传输数据。通过4字节ts计算RTT(Round-Trip-Time)即往返时延,再通过RTT计算RTO,ts(timestamp)即当前segment发送时的时间戳。
2. 选择性重传
TCP中实现的是连续ARQ协议,再配合累计确认重传数据,只不过重传时需要将最小序号丢失的以后所有的数据都要重传,而KCP则只重传真正丢失的数据。
3. 快速重传
与TCP相同,都是通过累计确认实现的,发送端发送了1,2,3,4,5几个包,然后收到远端的ACK:1,3,4,5,当收到ACK = 3时,KCP知道2被跳过1次,收到ACK = 4时,知道2被跳过了2次,此时可以认为2号丢失,不用等超时,直接重传2号包,大大改善了丢包时的传输速度。1字节cmd = 81时,sn相当于TCP中的seq,cmd = 82 时,sn相当于TCP中的ack。cmd相当于WebSocket协议中的openCode,即操作码。
4. 非延迟ACK
TCP在连续ARQ协议中,不会将一连串的每个数据都响应一次,而是延迟发送ACK,即上文所说的UNA模式,目的是为了充分利用带宽,但是这样会计算出较大的RTT时间,延长了丢包时的判断过程,而KCP的ACK是否延迟发送可以调节。
5. UNA + ACK
UNA模式参考特征2和特征4,ACK模式可以参考特征3。4字节una表示cmd = 81时,当前已经收到了小于una的所有数据。
6. 非退让流控
在传输及时性要求很高的小数据时,可以通过配置忽略上文所说的窗口协议中的拥塞窗口机制,而仅仅依赖于滑动窗口。2字节wnd与TCP协议中的16位窗口大小意义相同,值得一提的是,KCP协议的窗口控制还有其它途径,当cmd = 83时,表示询问远端窗口大小,当cmd = 84时,表示告知远端窗口大小。
4字节conv表示会话匹配数字,为了在KCP基于UDP实现时,让无连接的协议知道哪个是哪个,相当于WEB系统HTTP协议中的SessionID。
1字节frg表示拆数据时的编号,4字节len表示整个数据的长度,相当于WebSocket协议中的len。