TCP
TCP首部
- 20字节
16bit源端口号 16bit目的端口号
32bit序列号
32bit确认序号
4bit首部长度 保留6bit 6bit标识位 16bit窗口大小
16bit校验和 16bit紧急指针
特点
- 可靠,面向__连接__,字节流,__传输层__的服务(三次握手建立一个连接,四次挥手关闭一个连接)
- “连接”:客户端和服务器端的内存各自保存关于对方ip,port等信息
- 使用__校验和__、__确认__和__重传机制__保证__可靠__传输
- 给数据__分节排序__,使用__累计确认__保证数据的__顺序__和__非重复__
- 使用__滑动窗口__进行流量控制,通过__动态__改变__窗口大小__进行__拥塞控制__
- 连接4元组(源IP,源port,目的IP,目的端口)此外,同一个连接应有相同的__协议号__
- 三个阶段:启动,数据传输,退出(关闭)
- 双工
TCP字段
- **ACK ** 确认号用于确认__已成功接受__的N的字节(不包括N),后续ACK可以确认前面丢失ACK的报文
- **SYN ** 序列号可用于接收端__丢弃重复__的报文段,__记录__以杂乱次序到达的报文段
- RST 重置连接
- FIN 结束向对方发送数据
滑动窗口
TCP可靠传输
加权平均往返时间__RTTs__
TCP超时时间RTO
流量控制
- 控制发送方发送速率,保证接收方及时接受
- 接受方通过发送确认报文中窗口字段控制发送方发送窗口大小。将窗口字段置为0,则发送方不发送数据。
拥塞控制
- 降低整个网络的拥塞程度
- 慢开始、拥塞避免、快重传、快恢复
- 发送方维护__状态变量__拥塞窗口(cwnd)
拥塞控制时拥塞窗口(cwnd)变化
-
慢开始、拥塞避免
发送最初慢开始,令cwnd=1,发送方只能发送1个报文段;收到确认后,将cwnd翻倍
设置慢开始门限(ssthresh), cwnd>=ssthresh时进入拥塞避免每次cwnd加1
出现超时,ssthresh=cwnd/2
-
快重传、快恢复
收到3个重复确认,下一个报文段丢失,执行快重传,立即重传下一个报文段
只是丢失个别报文段而非网络拥塞,执行快恢复,令ssthresh=cwnd/2,cwnd=sstresh
TCP状态转换图
主动、被动 与 服务器、客户端没有明确的对应关系。
三次握手
- 第一次握手:客户端发送SYN,并指明客户端的初始序列号,即ISN(c),服务端接受消息,服务端确认的客户端发送能力和服务端的接受能力
- 第二次握手:服务端发送ACK+SYN,指明自己的ISN(s)。为了确认客户端的SYN,将ISN(c)+1作为ACK数值。这样,每发送一个SYN,序列号就会加1. 如果有丢失的情况,则会重传。客户端接受,客户端确认客户端接受和发送能力和服务端的接受发送能力
- 第三次握手:客户端发送ACK,客户端将ISN(s)+1作为返回的ACK数值,服务端接受,服务端确认服务端的发送能力
四次挥手
- 客户端发送一个FIN段,并包含一个希望接收者看到的自己当前的序列号K. 同时还包含一个ACK表示确认对方最近一次发过来的数据。
- 服务端将K值加1作为ACK序号值,表明收到了上一个包。这时上层的应用程序会被告知另一端发起了关闭操作,通常这将引起应用程序发起自己的关闭操作。
- 服务端发起自己的FIN段,ACK=K+1, seq=L
- 客户端确认。ACK=L+1
ISN(Initial Sequence Number)
ISN = M + F(localhost, localport, remotehost, remoteport)
- M是一个计时器,每隔4微秒加1。
- F是一个Hash算法,根据源IP、目的IP、源端口、目的端口生成一个随机数值。要保证hash算法不能被外部轻易推算得出。
TIME_WAIT状态
也称2MSL等待状态. 等待两倍于最大段生存期(Maximum Segment Lifetime, MSL)的时间, 加倍等待. 必须为最大段生存期选择一个数值, 代表任何报文段在被丢弃前在网络中被允许存在的最长时间.
- 保证发送的ACK能到达. 若未成功到达, 则服务器超时重传FIN+ACK报文段, 客户端再重传ACK, 并重新计时
- 防止已失效的连接请求报文段出现在本连接中, 可使本连接持续时间内所产生的报文段从网络中消失, 使下次连接中不会出现旧的报文段
安全协议与分层
x | x |
---|---|
链路层 | 一跳通信中的信息 |
网络层 | 两个主机之间传输的信息 |
传输层 | 进程与进程之间的通信 |
应用层 | 应用程序操纵的信息 |
粘包
- 产生原因
- 连续发送数据时,由于使用nagle算法,将较小的内容拼接为较大的内容,一次性发送,造成粘包
- 发送较大内容时,接收方读取缓存较小,不能一次读完全部内容,在下次读取内容时读取到上次发送的内容
syn flood
tcp_synack_retries=0 发送第二次握手包之后,如果收不到第三次握手包,不进行重试,加快回收"半连接"
# vi /etc/sysctl.conf
#最关键参数,默认为5,修改为0 表示不要重发
net.ipv4.tcp_synack_retries = 0
#半连接队列长度
net.ipv4.tcp_max_syn_backlog = 200000
#系统允许的文件句柄的最大数目,因为连接需要占用文件句柄
fs.file-max = 819200
#用来应对突发的大并发connect 请求
net.core.somaxconn = 65536
#最大的TCP 数据接收缓冲(字节)
net.core.rmem_max = 1024123000
#最大的TCP 数据发送缓冲(字节)
net.core.wmem_max = 16777216
#网络设备接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目
net.core.netdev_max_backlog = 165536
#本机主动连接其他机器时的端口分配范围
net.ipv4.ip_local_port_range = 10000 65535
注意,以下参数面对外网时,不要打开。因为副作用很明显,具体原因请google,如果已打开请显式改为0,然后执行sysctl -p关闭。因为经过试验,大量TIME_WAIT状态的连接对系统没太大影响:
#当出现 半连接 队列溢出时向对方发送syncookies,调大 半连接 队列后没必要
net.ipv4.tcp_syncookies = 0
#TIME_WAIT状态的连接重用功能
net.ipv4.tcp_tw_reuse = 0
#时间戳选项,与前面net.ipv4.tcp_tw_reuse参数配合
net.ipv4.tcp_timestamps = 0
#TIME_WAIT状态的连接回收功能
net.ipv4.tcp_tw_recycle = 0
为了处理大量连接,还需改大另一个参数:
# vi /etc/security/limits.conf
在底下添加一行表示允许每个用户都最大可打开409600个文件句柄(包括连接):
- nofile 409600