网络编程发展史
- http/0.9 1991年 只有get命令,服务端直接返回html格式字符,响应完毕既断开。
- http/1.0 1996 可以发送任何格式内容,包括文字图像视频二进制,也丰富了Get Post Head请求和响应格式加入头信息,每个TCP只能发送一个请求,新建TCP链接的成本很高,导致效率很差。
- http/1.1 1997 引入持久连接,TCP默认不关闭,可复用。
- http/2.0 2015 采用二进制而非文本格式,解析更高效,数据更紧凑,错误更少,服务器可以将响应主动推送到客户端。
- 自2017年起http3协议已发布了29个Draft,推出在即,Chrome、Nginx等软件都在跟进实现最新的草案。
Http协议
超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上,是应用层协议。
请求报文
请求报文由请求行,请求头,和请求数据组成。
- 请求行:抓包第一行,包括请求方法,url和http版本
- 请求报头:header的内容
- 请求数据:指post方式提交的表单数据
响应报文
响应报文由状态行,响应头,响应正文组成。
- 状态行:状态码
- 响应报头:同请求报头
- 响应正文:服务器返回的资源数据
网络分层
请求发起和断开
三次握手
第一次握手:客户端将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给服务端,客户端进入SYN_SENT状态,等待服务端确认。
第二次握手:服务端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务器端将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给客户端以确认连接请求,服务端进入SYN_RCVD状态。
第三次握手:客户端收到确认后,检查ack是否为J+1,ACK是否为1,如果正确,则将ACK置为1,ack=K+1,并将该数据包发送给服务器,服务器检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,客户端和服务端进入ESTABLISHED状态,完成三次握手,随后可以开始传输数据了。
为什么握手需要3次?
为了实现可靠数据传输,TCP协议的通信双方,都必须维护一个序列号,以标识发送出去的数据包中,哪些是已经被对方收到的。
例
:发送方在发送数据包(假设是10byte),同时送上一个序号(假设为500),那么接收方收到这个数据包以后,就可以回复一个确认号(510 = 500 + 10)告诉发送方"我已经收到了你的数据包,你可以发送下一个数据包,序号从511开始"
三次握手的过程就是通信双方相互告知序列号起始值,并确认对方已经收到序列号起始值的必经步骤,如果只有两次握手,那么最多只有连接发起方的起始序列号能被确认。
通过wireshark观察三次握手
第一次握手:
第二次握手:
第三次握手:
3次握手漏洞-洪泛攻击:三次握手中的第二次握手,服务端向客户端应答请求,应答请求是需要客户端ip的,攻击者伪造这个ip,往服务端疯狂发送第一次握手的内容,因为第一次握手的客户端ip地址是伪造的,从而服务端忙于进行第二次握手,但是第二次握手没有响应,服务端被拖累至死机。
解决方案: 1.无效连接监视释放。监视所有连接,达到一定阈值后,拆除连接释放资源。这种方式不太推荐。 2.延缓TCB分配。第一次握手后,一般服务器会为该请求分配TCB(连接控制资源),这个资源通常会占用200多个字节,延迟TCB分配,当连接建立起来之后再分配TCB就可以有效减低服务器资源的消耗 3.使用防火墙。防火墙确认了连接的有效性后,再向内部服务器发起SYN请求。(推荐)
四次挥手
四次挥手终止TCP连接,当断开一个连接时,需要客户端和服务端总共发送四个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任意端来执行close触发。
由于TCP是全双工的,因此每个方向都需要单独进行关闭,这一原则是当甲方完成数据发送任务后,发送一个FIN给乙方来终止连接,乙方收到FIN只是意味着不会再收到甲方的数据了,但是乙方依然可以给甲方发送数据,直到乙方也发送了FIN给甲方,首先进行关闭的一方执行主动关闭,而另一方执行被动关闭。
由此可见,TCP建立连接需要三个分节,断开连接却需要四个分节
- 某个应用调用close(主动关闭),该端的TCP发送一个FIN分节,表示数据发送完毕,应用进入FIN-WAIT-1状态。
- 接收到这个FIN的对端执行被动关闭,发送确认报文,这个FIN由TCP确认。因为收到FIN意为着接受端应用进程在相应连接上再无数据可接收,接收端进入了CLOSE_WAIT状态,这时候处于半关闭状态,即此时主动关闭端已经没有数据需要发送了,但是被动关闭端要发送数据,主动关闭端依然可以接收,这个状态还要维持一段时间,也就是整个CLOSE_WAIT状态需要维持的时间。主动关闭端收到确认报文后进入FIN_WAIT_2状态。
- 过一段时间后,被动关闭的应用端通过调用close关闭他的套接字,这导致他的TCP也发送一个FIN。
- 接收这个FIN的原发端TCP确认这个FIN发出一个确认ACK报文,并进入TIME_WAIT状态,注意此时TCP还没有释放,必须经过2*MSL的时间后,当主动关闭端撤销相应的TCB(传输控制块)后,才进入CLOSED状态。
- 被动关闭端只要收到了主动关闭端发出的确认,立即进入CLOSED状态,同样撤销TCB后,就结束了这次TCP连接。可以看到被动关闭端结束TCP的时间要比主动关闭端早一些。
为什么挥手需要四次?
TCP是全双工模式,这就意味着,主机一发出一个FIN报文,只是意味着主机一已经没有数据要发送了,主机一告诉主机二,我数据发送完了。但是这个时候主机一还是可以接收来自主机二的数据,当主机二返回ACK报文段时,表示他已经知道主机一没有数据要发送了,但是主机二还是可以发送数据到主机一的。当主机二也发送了FIN报文段时,这个时候也表示主机二也没有数据要发送了,就会告诉主机一,我也没有数据要发送了,之后彼此就会愉快的中断这次TCP连接。这就是为什么全双工模式为了彻底中断,需要双方通信四次的原因。
为什么需要TIME_WAIT状态
第一,为了可靠的终止TCP连接,如果最后一个ACK报文因为网络原因被丢弃,此时server因为没有收到ACK超时报文而重发FIN报文,处于TIME_WAIT状态的client可以继续对FIN报文做回复,向server发送ACK报文。第二,让迟来的TCP报文段有足够的时间被识别和丢弃,连接结束了,网络中的延迟报文也应该被丢弃,以免影立刻建立的新连接。
TCP连接的基本特性
面向连接
TCP是面向连接的运输层协议
。应用程序在使用 TCP 协议之前,必须先建立 TCP 连接。在传送数据完毕后,必须释放已经建立的 TCP 连接。
可靠性
TCP提供可靠交付
的服务。通过 TCP 连接传送的数据,无差错、不丢失、不重复,并且按序到达。确认应答是TCP实现可靠传输的最核心机制,可靠传输不是说把消息100%发送给对方(由于网络物理等因素干扰,不可能实现),而是,当我们成功把数据发送给对方,我们知道发送成功了,当我们没有把数据发送给对方,我们能知道发送失败了。
RTT(Round-trip time)往返时延
RTO(Retransmission time out)重传超时
数据排序
TCP 发送数据时 , 会将数据拆分成不同的片段 , 并对这些片段进行排序 ; 顺序发送 : 将排序好的数据片段顺序发送 ; 顺序组装 : 在接收端按照顺序将数据片段组装成原数据 ;
全双工
TCP 允许通信双方的应用进程在任何时候都能发送数据。TCP 连接的两端都设有发送缓存和接受缓存,用 来临时存放双向通信的数据。
滑动窗口
滑动窗口,批量传输叫做滑动窗口,批量不是无限发送,是发送到一定程度就等待 ack,不等待直接发送的数据量是有上限的,而且是回来一个 ack 就立即发下一条,相当于总的要批量等待的数据是一致的。把批量等待数据的数量,就称为"窗口大小",
流量控制
流量控制,本质上就是让接收方来限制一下发送方的速度。发的太快,会把接收方接收缓冲区占满,接下来继续发数据就会丢包,所以接收方拿接收缓存区剩余空间作为窗口大小告诉发送方来限制发送速度。
拥塞控制
流量控制是作用于接收者的,它是控制发送者的发送速度从而使接收者来得及接收,防止分组丢失的。 拥塞控制是作用于网络的,它是防止过多的数据注入到网络中,避免出现网络负载过大的情况。