漫谈TCP(一)

分两次谈谈TCP协议栈中的一些细节。

三次握手的实现

这里不讲三次握手的过程,而是讲一下其他细节。
1.状态机
服务器会与多个客户端建立连接,与每个客户端保持连接的状态,会保存在TCB当中。
2.标识客户端
在三次握手之前,还没有fd的概念,所以不是用fd来标识不同的客户端连接,而是使用的五元组。
3.握手数据保存
首先分析应该使用什么数据结构。因为服务器建立的连接不止一个,所以不能简简单单地用一个对象来存储。这里使用队列,因为数据量不会太大,没有必要使用树结构。
第三次握手的数据到达后,就把数据(TCB)移动到下一个队列。其实,这两个队列分别是半连接队列和全连接队列。
4.第三次握手包丢失
服务端不会重发第二次握手包,因为此时超时重传定时器的时间不知道该设为多少。解决办法是在半连接队列中设置超时机制,如果超时,则删除该节点。
三次握手完成后,调用accept()函数,从全连接队列中取出一个节点并为其分配一个fd与相应的TCB对应。这个fd和TCB的生命周期伴随整个连接。

ddos攻击

简单介绍一下,ddos攻击就是客户端不断地只发第一次握手的数据包,不发送后续的数据包。这样会使得服务器的半连接队列不断变大,最终队列会满,无法再接受新连接。
解决方法有两个,对于开发人员来说,我们可以将listen()函数中的backlog参数调小,并设置超时。这样的方法在攻击较多时意义不大,只能用第二种方法,就是设置防火墙。

TCP的长度字段

TCP包头中没有长度字段,是因为IP包头有包的总长字段。所以,包的完整性由IP来控制而不是TCP控制。这一点表明,TCP是依赖IP的。再与UDP对比一下,UDP包头中的长度就是sendto()的长度,UDP加上长度字段是因为UDP包相对独立,而TCP是流式传输,数据包本身并不独立。当然,其实UDP不用这个字段也可以。

慢启动与拥塞控制

画个图就可以。
漫谈TCP(一)_第1张图片
这里画的是对端接收窗口不受限制的情况。注意在实际情况中,对端接收窗口大小有限制,所以发包长度不会超过对端窗口大小。这个窗口大小值可以通过sysctl.conf中rmem修改。

延迟确认

这里主要再提示几点。一是接收到的包顺序不对也会重置延迟确认定时器。
二是接收方不会因为一直接收到数据而一直不回ACK包,因为发送方还有发送的超时重传定时器,如果接收方一直不回ACK,发送方就会重发数据。
这里也可以看出TCP传输的两个缺点,重传数据多和实时性不强。

TCP定时器

TCP协议中有很多定时器,这里简单列举几个。
1.超时重传定时器
从发包开始计时,到接收到对端回的ACK包所经历的时间。
这里有个消抖的机制。

本次RTT=上次RTT用时*0.1+老RTT值*0.9

2.延迟确认定时器
3.探测定时器
也叫坚持定时器。对端接收窗口为0时,还想发送数据,就会向对端发送探测包。
4.keep-alive定时器
不建议使用,因为一旦判断超时,就会直接回收TCB,导致fd没法用。而且,判断超时的机制也非常愚笨,定时发包,没有回复就判为超时。建议自己在应用层实现一个定时器,每30秒发包一次,如果没有收到回复,就在60秒再发一次,如果还没收到,再在3分钟时发送一次,再没有收到才回收。
5.time_wait定时器

客户端关闭后服务端接收的数据

客户端关闭是指客户端向服务端发送了FIN包,而此时服务端对应的接收缓冲区中可能还有之前没有接收完的数据,所以服务端recv()的是缓冲区中的数据。
大概先讲这么多。对于TCP的理解,关键在于把握三点:一是建立连接时的三次握手,二是数据传输时的滑动窗口和拥塞控制,三是断开连接时的四次挥手。

你可能感兴趣的:(笔记,tcp/ip)