概述:
- TCP传输前先要建立连接
- TCP在传输层
- 点对点,一条TCP只能连接两个端点
- 可靠传输、无差错、不丢失、不重复、按顺序
- 全双工
- 字节流
TCP报文段
TCP报文段的报头前20字节是固定的,后面4n字节是根据需要而添加的。
20字节的固定部分:
- 源端口和目的端口:分别写入源端口号和目的端口号
- 序号:0-(2^32-1),本报文段数据的第一个字节的序号,用来解决乱序问题
- 确认序号:期望收到对方下一个报文段的第一个数据字节的序号,用来解决丢包问题
- 数据偏移:TCP报头长度,包括固定的20字节和选项字段
- 保留:均为0
- 控制位:说明报文的性质,操控TCP的状态机
- 窗口:指发送者自己的接收窗口大小,流量控制
- 校验和:
- 紧急指针:当URG=1时才有效,指出本报文段紧急数据的字节数
- 选项:
连接的建立与释放
三次握手:
(1)client发出请求连接报文段,SYN=1,Seq=x。client进入SYN-SENT状态。
(2)server收到请求报文后,向client发送确认报文段。确认报文段的首部中SYN =1,ack = x + 1,同时为自己选择一个初始序列号Seq = y.server进入SYN-RCVD(同步收到)状态。
(3)client收到server的确认报文后,还有给server发送一个确认报文。 确认号ack = y + 1,而自己的Seq = x + 1。这个报文段已经可以携带数据,如果不携带数据则不消耗序号,下一个报文段序号仍为Seq = x + 1。
为什么要三次握手呢?
假设握手只有两次,如果某次数据报由于网络延迟,从client到server时,已经断开连接了,而此时server会认为是client想建立一个新的连接,于是发给client一个确认报文(第二次握手)就建立了新的连接了,显然这是错误的。如果是三次握手,client接到这个确认报文时,不予理睬,两者没有完成第三次握手,所以不会建立错误的连接了。
网上有一个比较形象的说法:
第一次对话:
老婆让甲出去打酱油,半路碰到一个朋友乙,甲问了一句:哥们你吃饭了么?
结果乙带着耳机听歌呢,根本没听到,没反应。甲心里想:跟你说话也没个音,不跟你说了,沟通失败。说明乙接受不到甲传过来的信息的情况下沟通肯定是失败的。
如果乙听到了甲说的话,那么第一次对话成功,接下来进行第二次对话。
第二次对话:
乙听到了甲说的话,但是他是老外,中文不好,不知道甲说的啥意思也不知道怎样回答,于是随便回答了一句学过的中文 :我去厕所了。甲一听立刻笑喷了,“去厕所吃饭”?道不同不相为谋,离你远点吧,沟通失败。说明乙无法做出正确应答的情况下沟通失败。
如果乙听到了甲的话,做出了正确的应答,并且还进行了反问:我吃饭了,你呢?那么第二次握手成功。
通过前两次对话证明了乙能够听懂甲说的话,并且能做出正确的应答。接下来进行第三次对话。
第三次对话:
甲刚和乙打了个招呼,突然老婆喊他,“你个死鬼,打个酱油咋这么半天,看我回家咋收拾你”,甲是个妻管严,听完吓得二话不说就跑回家了,把乙自己晾那了。乙心想:这什么人啊,得,我也回家吧,沟通失败。说明甲无法做出应答的情况下沟通失败。
如果甲也做出了正确的应答:我也吃了。那么第三次对话成功,两人已经建立起了顺畅的沟通渠道,接下来开始持续的聊天。
通过第二次和第三次的对话证明了甲能够听懂乙说的话,并且能做出正确的应答。
可见,两个人进行有效的语言沟通,这三次对话的过程是必须的。
四次挥手
(1)此时TCP两端都还处于ESTABLISHED状态,client不再发送数据,并发送一个FIN报文段,seq = 1,进入FIN-WAIT-1状态。
(2)server收到后回复,ack = u + 1,seq = v(v等于服务器传输数据最后一字节的序号加1),然后进入CLOSE-WAIT状态,server如果继续发送数据,client依然接收。
(3)client收到确认报文后,进入FIN-WAIT-2状态。server不再发送数据后,发出FIN,ack = u + 1,进入LASK-ACK状态。
(4)client收到后回复,seq = u + 1, ack = w + 1(w 等于服务器后来继续发送的最后一个字节的序号加1,与v并不一定相等!),然后进入TIME-WAIT状态
(5)此时连接还没有释放,需要等待(4分钟)两端才会CLOSED。设置时间等待是因为有可能最后一个确认报文丢失而需要重传。
为什么要四次挥手呢?
因为当client发送FIN是,表示client没有要传的数据了,不代表server没有数据要传给client,所以server还需要发送FIN来表示它也没数据传输了。由于TCP是全双工的,所以并不存在谁先FIN的问题。
SYN超时问题:server如果在一定时间内没有收到client的确认报文,会重发。在Linux下,默认重试次数为5次,5次重试的时间分别为1、2、4、8、16、32s。第五次都没有收到,就会断开这个连接。
SYN Flood攻击问题:如果给服务器发送一个SYN后,就下线了,于是服务器就需要等63秒才会断开连接,这样攻击者就可以把服务器的SYN连接队列耗尽。Linux下有一个叫tcp_syncookies的参数来应对这个事。当SYN队列满了以后,TCP会通过源地址端口、目标地址端口和时间戳打造一个特别的Seq Number发回去(又叫cookie),如果是攻击者则不会有相应,如果是正常连接,则会把这个SYN Cookie发回来,然后服务端可以通过cookie建立连接。千万不要用tcp_syncookies来处理正常的大负载的连接的情况。对于正常的请求,应该调整TCP参数,第一个是:tcp_synack_retries可以用他来减少重试次数;第二个是:tcp_max_syn_backlog,可以增大SYN连接数;第三个是:tcp_abort_on_overflow处理不过来感觉直接拒绝连接!
TCP可靠传输的实现
- TCP报文段的长度可变,根据收发双发缓存、网络状态而调整。
- TCP收到另一端数据时,会回发确认
- TCP能够超时重传
- 数据校验
- 报文段中有序号,以保证顺序正确
- TCP提供流量控制
发送者再发送一个报文段后,暂时保存该文段的副本,收到确认后才删除;
确认报文段也需要序号,才能明白是发出去的那个数据得到了确认;
超时计时器比传输往返时间略长,但具体值不确定,根据网络情况而定(使用RTT算法)。
连续ARQ协议
实际中为了提高效率,采用流水线传输:发送方可以连续发送多个报文段(连续发送的数据长度叫做窗口)。接收方也不必对收到的每个报文都做回复,而是采用累积确认方式;
流量控制和拥塞控制
参考资料:《TCP那些事(上)》http://coolshell.cn/articles/11564.html
实验楼 TCP/IP网络协议基础 https://www.shiyanlou.com/courses/98