TCP/IP相关
1. 定义
-
IP协议
-
IP 协议实际上是用来查找地址的,而它对应的层级也是网络层,也可以称之为网际互联层,区别不大。
-
-
TCP协议
-
TCP 协议是用来规范传输规则的,和IP 协议是不同的,而它对应的层级是传输层,也就是IP去寻找地址,把所有的传输任务都交给TCP,而TCP相当于一个快递员的角色。
-
2. 三次握手
-
序号
-
seq:sequence number 的缩写,即序号。seq表示的则是自己传递的序号,TCP在传输的时候,其中每一个字节,都会有一个序号,发送数据的时候,会把第一个数据的第一个序号发送给对方,就是看到的第一步,而接收的这一方面,会按照这个序号来检查是否是一个连接完整的数据,是完整的话就继续,不完整的话就重新发送。保证数据完整性不被破坏。
-
ack: 注意是小写的 acknoledgement number的缩写 表示确认号 ,要和ACK(确认位)区分 ,接收端用它来给发送端返回接受消息的数据信息,而这时候,它的值就是表明我想接收下一个数据包了。而这个值就是下一个数据包的开始的序号,而这个ack所代表的值的序号前面的数据都已经接受成功了。
-
ACK确认位: 只有当ACK=1的时候ack才会起到作用,而在我们第一次请求的时候,是没有需要确认的接受的数据的,这个时候ACK=0,正常通信下ACK=1.
-
SYN: 同步位 ,作用是用于建立连接时同步序号,刚建立连接的时候,ACK=0这时ack就不起作用,当接收端收到SYN=1的报文的时候,会将ack设置为接收到的seq+1的值,这时候的ack的值就是根据SYN来设置的。
-
FIN:终止位,在本图没有体现,在四次挥手的时候能完全体现出来。而它是用来在数据传输都完成之后来释放连接的。
-
-
上图解析:
-
第一次握手(SYN=1,seq=x)
-
客户端发送一个TCP的SYN标志位置1的包,指明客户端打算连接的服务器的端口,以及初始序号X,保存在包头的序列号(seq)中。发送完毕后,客户端进入SYN_SEND状态
-
-
第二次握手(ack=y+1,ACK=1,seq=y,SYN=1)
-
服务器发回确认包(ACK)应答。即SYN和ACK标志位都为1。服务器端选择自己ISN序列号,放到seq域中,同时将确认序号ack设置为客户的ISN加1,即y+1。发送完毕后,服务端进入SYN_RCVD状态
-
-
第三次握手(ack=y+1,ACK=1)
-
客户端再次发送确认包(ACK),SYN标志位为0,ACK标志位为1,并且把服务器发来的ACK序号字段+1,放在ack中返回。发送完毕后,客户端进入ESTABLISHED状态,当服务端接收到这个包时也会进入ESTABLISHED状态,TCP握手结束。
-
-
3. 四次挥手
-
流程解读:
-
第一次挥手(FIN=1,seq=x)
-
假如客户端想要关闭连接,客户端发送一个FIN标志位置为1的包,表示自己已经没有数据可以发送了,但是任然可以接受数据,发送完毕后,客户端进入FIN_WAIT_1状态
-
-
第二次挥手(ACK=1,ack=x+1)
-
服务器端接收到客户端发送的FIN包,发送一个确认包,表明自己接受到了客户端关闭连接的请求,但是服务端还没有准备好关闭连接。发送完毕后,服务器端进入CLOSE_WAIT状态,客户端收到这个确认包后,进入FIN_WAIT_2状态,等待服务器关闭连接。
-
-
第三次挥手(FIN=1,seq=y)
-
服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN值为1。发送完毕后,服务器端进入LAST_ACK状态 ,等待来自客户端的最后一个ACK。
-
-
第四次挥手(ACK=1,ack=y+1)
-
客户端收到服务端的关闭请求时,发送一个确认包,并且进入TIME_WAIT状态,等待可能出现的要求重传的ACK包。
-
服务器端接收到这个包后,关闭连接,进入CLOSED状态
-
客户端等待了某个固定时间(两个最大段生命周期,2MSL,2Maximum Segment Lifetime)之后,没有收到服务器端的ACK,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入CLOSED状态。两次后会重传直到超时。如果多了会有大量半链接阻塞队列。
-
-
4. 为什么连接三次握手,而断开是四次挥手呢
-
这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示客户端不再发送数据,但是还能接受数据,服务端是否现在关闭数据发送通道需要上层应用决定,因此ACK和FIN一般分开发送。
5. 服务器保持了大量TIME_WAIT状态
-
原因
-
TIME_WAIT是主动关闭连接的一方保持的状态,对于爬虫服务器来说他本身就是“客户端”,在完成一个爬取任务之后,他就 会发起主动关闭连接,从而进入TIME_WAIT的状态,然后在保持这个状态2MSL(max segment lifetime)时间之后,彻底关闭回收资源。
-
查看服务器所有连接状态的命令
netstat -n|awk '/^tcp/{++S[$NF]}END{for (key in S) print key,S[key]}'
-
-
为什么?
-
防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)
-
可靠的关闭TCP连接。在主动关闭方发送的最后一个 ack(fin) ,有可能丢失,这时被动方会重新发fin, 如果这时主动方处于 CLOSED 状态 ,就会响应 rst 而不是 ack。所以主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。另外这么设计TIME_WAIT 会定时的回收资源,并不会占用很大资源的,除非短时间内接受大量请求或者受到攻击。
-
-
解决方案
-
编辑文件/etc/sysctl.conf,加入以下内容:
net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_fin_timeout = 30
-
6. 服务器保持了大量CLOSE_WAIT状态
-
原因
-
就是在对方关闭连接之后服务器程序自己没有进一步发出ack信号。换句话说,就是在对方连接关闭之后,程序里没有检测到,或者程序压根就忘记了这个时候需要关闭连接,于是这个资源就一直 被程序占着。
-
比如说
服务器A是一台爬虫服务器,它使用简单的HttpClient去请求资源服务器B上面的apache获取文件资源,正常情况下,如果请求成功,那么在抓取完 资源后,服务器A会主动发出关闭连接的请求,这个时候就是主动关闭连接,服务器A的连接状态我们可以看到是TIME_WAIT。如果一旦发生异常呢?假设 请求的资源服务器B上并不存在,那么这个时候就会由服务器B发出关闭连接的请求,服务器A就是被动的关闭了连接,如果服务器A被动关闭连接之后程序员忘了 让HttpClient释放连接,那就会造成CLOSE_WAIT的状态了。
-
-
解决方案
-
此问题一般是由于程序代码编写问题导致的,查代码吧
-