什么是tcp协议
传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
四层负载均衡:传输层(transmission),tcp和udp。
TCP/IP模型与OSI模型的比较
模型 | 相同点 | 不同点 |
TCP/IP | 两者都是以协议栈的概念为基础。 | OSI适用于各种协议栈;TCP/IP只适用于TCP/IP网络。 |
OSI | 协议栈中的协议彼此相互独立。 | OSI是先有模型;TCP/IP是先有协议,后有模型。 |
下层对上层提供服务。 | 层次数量不同。 |
传输层的作用
IP层提供点到点的连接,传输层提供端到端的连接。
传输层的协议
TCP(Transmission Control Protocol)
- 传输控制协议
- 可靠的、面向连接的协议
- 传输效率低
UDP(User Datagram Protocol)
- 用户数据报协议
- 不可靠的、无连接的服务
- 传输效率高
通过不同的端口号来区别
不同的应用程序监听不同的端口。
nginx:80
mysql:3306
ssh:22
dns:53
端口号范围:0-65535。
TCP的封装格式
16位源端口号:告知主机该报文段是来自哪里。
16位目标端口号:告知主机该报文段是去往哪里。
32位序列号:一次TCP通信(从TCP连接建立到断开)过程中某一个传输方向上的字节流的每个字节的编号。
32位确认号:对另一方发送来的TCP报文段的响应。其值是收到的TCP报文段的序列号值加1。
4位首部长度(header length):标识该TCP头部有多少个32bit字(4字节)。因为4位最大能标识15,所以TCP头部最长是60字节。
6个标志位
标志位 | 意义 |
URG | urgent 紧急 |
ACK | acknowledgement 确认 |
PSH | push 通知应用程序尽快处理数据,不要让数据在缓存里停留。 |
RST | reset 重置,重新连接 |
SYN | sync 同步,建立连接 |
FIN | finish 请求断开连接 |
16位窗口大小(window size):是TCP流量控制的一个手段。这里说的窗口,指的是接收通告窗口(Receiver Window,RWND)。它告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度。
16位校验和(TCP check sum):由发送端填充,接收端对TCP报文段执行CRC算法以检验TCP报文段在传输过程中是否损坏。
16位紧急指针(urgent pointer):是一个正的偏移量。它和序号字段的值相加表示最后一个紧急数据的下一字节的序号。
三次握手
第一次握手:Host A 给Host B 发一个 SYN 报文,此时HostA处于 SYN_SEND 状态。
第二次握手:Host B 收到Host A 的 SYN 报文之后,会以自己的 SYN 报文作为应答,同时会把Host A的 ISN + 1 作为ACK 的值,表示自己已经收到了Host A 的 SYN,此时Host B 处于 SYN_REVD 的状态。
第三次握手:Host A 收到 SYN 报文之后,会发送一个 ACK 报文,把Host B 的 ISN + 1 作为 ACK 的值,表示已经收到了Host B 的 SYN 报文,此时Host A 处于 ESTABLISHED 状态。Host B 收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方已建立起了连接。
四次挥手
第一次挥手:Host A 发送一个 FIN 报文,报文中会指定一个序列号。此时Host A 处于 FIN_WAIT1 状态。
第二次挥手:Host B 收到 FIN 之后,会发送 ACK 报文,且把Host A 的序列号值 +1 作为 ACK 报文的序列号值,表明已经收到Host A 的报文了,此时Host B处于 CLOSE_WAIT 状态。
第三次挥手:如果Host B也想断开连接了,和Host A 的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时Host B处于 LAST_ACK 的状态。
第四次挥手:Host A 收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把Host B的序列号值 +1 作为自己 ACK 报文的序列号值,此时Host A 处于 TIME_WAIT 状态。需要过一阵子以确保Host B收到自己的 ACK 报文之后才会进入 CLOSED 状态,Host B收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。
32位序列号
32位的序号:单调递增
A 向 B 发送第一个包 生成初始序号,随机第一个包30给字节,发送第二个包 ISN + 第二个包的第一个字节偏移量,序列号:ISN + 30。ISN = M + Md5(四元组)
序列号随机的。为了安全性,黑客不会那么容易猜到序列号,不会伪造一些确认序列包,从而对服务器发起攻击。
序列号回绕,序列号32位保存,总会到尽头,到达尽头,序列号从0开始:
- 使用序列号无法判断出那个是以前的包,那个是之后的包。
- 在选项里加上 timestamp 时间戳 发送方的时间戳 接收方的时间戳。
- 选项:timestamp -- 计算往返时间,确认数据包的先后顺序。
ack包携带序列号,但是一般不占用,下一个包还可以从ack序列号开始,一般来说你的ack都不占用序列号 ,ack报文不携带数据就不占用序列号,不需要回复的包 基本不占用序列号。
什么是 TCP 半连接队列和全连接队列?
在 TCP 三次握手的时候,Linux 内核会维护两个队列,分别是:
- 半连接队列, syn queue
- 全连接队列, accept queue
服务端收到客户端发起的 SYN 请求后,内核会把该连接存储到半连接队列,并向客户端响应 SYN+ACK,接着客户端会返回 ACK,服务端收到第三次握手的 ACK 后,内核会把连接从半连接队列移除,然后创建新的完全的连接,并将其添加到 accept 队列,等待进程调用 accept 函数时把连接取出来。
计时器
体现了tcp的可靠性。
超时重传计时器:ack包迟迟未收到,服务器端检测超时,服务器重新发送数据包 SYN+ACK
/proc/sys/net/ipv4/tcp_synack_retries
centos系统,默认发送syn+ack包 5次,内核参数修改:永久生效。
vim /etc/sysctl.conf
sysctl -p
sysctl -a
坚持计时器:防止零窗口死锁。
保活计时器:防止两个TCP之间的连接长时间的空闲。
时间等待计时器:连接终止期间使用的,在发送了最后一个ACK后,不立即关闭连接,而是等待一段时间,保证能接收到重复的FIN数据段。
SYN攻击
SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。
如何检测?
检测 SYN 攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。在 Linux/Unix 上可以使用系统自带的 netstats 命令来检测 SYN 攻击。
netstat -n -p TCP | grep SYN_RECV
防范措施
syn flood:dmesg日志
1.出现这个日志,要么就是由于半连接队列设置小,要么就是遭受到ddos攻击
修改参数:扩大半连接队列
/proc/sys/net/ipv4/tcp_max_syn_backlog
net.ipv4.tcp_max_syn_backlog
2.启用
[root@localhost ipv4]# cat tcp_syncookies
1
syn backlog 是 TCP 协议栈中的半连接队列长度。
mss:tcp 最大报文长度。传输层一次最大可以传输多少数据,不包括头部字段。
1500 - ip头(20字节)tcp头部字(最少20)==1460
因为有了MSS tcp包一般来说在网络层是不需要分片的。
netstat 命令用于显示网络状态,可以得知 Linux 系统的网络情况。
[root@localhost ~]# netstat -anplut
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1744/sshd
tcp 0 36 192.168.102.129:22 192.168.102.1:64105 ESTABLISHED 10694/sshd: root [p
选项
-a all
-n number 以数字的形式显示
-p program 程序的名字
-l listening
-t tcp
-u udp
状态(state)
sent 发送
received 接受
establish 建立连接
Recv-Q
Established: The count of bytes not copied by the user program connected to this socket.
内核空间里的socket队列里还有多少数据没有被用户空间里的进程复制(取)走
说明应用程序非常忙,处理不过来了
Listening: Since Kernel 2.6.18 this column contains the current syn backlog.
Send-Q
Established: The count of bytes not acknowledged by the remote host.
还有多少字节的数据没有被远程主机确认--》发送出去的数据包还没有收到确认
Listening: Since Kernel 2.6.18 this column contains the maximum size of the syn backlog.
socket 套接字: ip:port 192.168.0.1:80 ,一个程序占用一个ip地址的一个端口号,访问这个套接字就是访问一个程序。
流量控制
TCP协议通过滑动窗口来进行流量控制,它是控制发送方的发送速度从而使接受者来得及接收并处理。
阻塞控制
拥塞控制作用于整体网络,它是防止过多的包被发送到网络中,避免出现网络负载过大,网络拥塞的情况。
拥塞控制状态机的状态有五种
1、Open状态
Open状态是拥塞控制状态机的默认状态。这种状态下,当ACK到达时,发送方根据拥塞窗口cwnd(Congestion Window)是小于还是大于慢启动阈值ssthresh(slow start threshold),来按照慢启动或者拥塞避免算法来调整拥塞窗口。
2、Disorder状态
当发送方检测到DACK(重复确认)或者SACK(选择性确认)时,状态机将转变为Disorder状态。在此状态下,发送方遵循飞行(in-flight)包守恒原则,即一个新包只有在一个老包离开网络后才发送,也就是发送方收到老包的ACK后,才会再发送一个新包。
3、CWR状态
发送方接收到一个显示拥塞通知时,并不会立刻减少拥塞窗口cwnd,而是每收到两个ACK就减少一个段,直到窗口的大小减半为止。当cwnd正在减小并且网络中有没有重传包时,这个状态就叫CWR(Congestion Window Reduced,拥塞窗口减少)状态。CWR状态可以转变成Recovery或者Loss状态。
4、Recovery状态
当发送方接收到足够(推荐为三个)的DACK(重复确认)后,进入该状态。在该状态下,拥塞窗口cnwd每收到两个ACK就减少一个段(segment),直到cwnd等于慢启动阈值ssthresh,也就是刚进入Recover状态时cwnd的一半大小。 发送方保持 Recovery 状态直到所有进入 Recovery状态时正在发送的数据段都成功地被确认,然后发送方恢复成Open状态,重传超时有可能中断 Recovery 状态,进入Loss状态。
5、Loss状态
当一个RTO(重传超时时间)到期后,发送方进入Loss状态。所有正在发送的数据标记为丢失,拥塞窗口cwnd设置为一个段(segment),发送方再次以慢启动算法增大拥塞窗口cwnd。
Loss 和 Recovery 状态的区别是:Loss状态下,拥塞窗口在发送方设置为一个段后增大,而 Recovery 状态下,拥塞窗口只能被减小。Loss 状态不能被其他的状态中断,因此,发送方只有在所有 Loss 开始时正在传输的数据都得到成功确认后,才能退到 Open 状态。
拥塞控制主要是四个算法:
1. 慢启动
2. 拥塞避免
3. 拥塞发生(快重传)
4. 快速恢复
为什么需要三次握手,而不是两次?
三次握手才能确立两方都已经准备好了,如果两次握手,客户端没有收到来自服务器端的syn+ack包,而服务端又已经发送了,那服务端认为我发了,客户端认为服务器没有发送,最后一直等着,形成死锁。
等待2MSL的意义?
1.保证客户端最后发送的ACK能够到达服务器,帮助其正常关闭。
2.防止已失效的连接请求报文段出现在本连接中。
TIME_WAIT状态过多有什么危害?
1.占用系统资源
2.socket的TIME_WAIT状态结束之前,该socket占用的端口号将一直无法释放。
如何解决TIME_WAIT状态过多?
1,最好的办法是尽量让客户端主动断开连接,除非遇到一些异常情况,如客户端协议错误、客户端超时等。
2,打开系统的TIME_WAIT重用和快速回收。
3,在Linux系统可以修改以下参数:
①打开TCP对时间戳的支持,保持服务器与客户端时间同步
net.ipv4.tcp_timestamps=1(默认即为 1)
②修改net.ipv4.tcp_tw_reuse = 1,允许对处于TIME_WAIT的socket用于建立新的连接
为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
建立连接时,ACK和SYN可以放在一个报文里来发送。而关闭连接时,被动关闭方可能还需要发送一些数据后,再发送FIN报文表示同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。
为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?
两个存在的理由:
1、无法保证最后发送的ACK报文会一定被对方收到,所以需要重发可能丢失的ACK报文。
2、关闭链接一段时间后可能会在相同的IP地址和端口建立新的连接,为了防止旧连接的重复分组在新连接已经终止后再现。2MSL足以让分组最多存活msl秒被丢弃。