TCP/IP握手和分手
2020-08-10 11:35:54 2 举报
AI智能生成
网络编程之TCPIP分手和握手的过程
作者其他创作
大纲/内容
Four Layers of TCP/IP Protocol
TCP
关键词
可靠
四次分手
主动关闭连接一端(active close),<br>才有TIME_WAIT状态<br>
半关闭状态/half-close
为啥挥手需要四次?
FIN报文,只表示发送端不再发送数据了,但还能接收数据
服务器收到客户端的FIN报文时,先回一个ACK应答报文,<br>而服务器端还可能有数据需要处理和发送,等服务端不再<br>发送数据是,才发送FIN报文给客户端,表示现在同意关闭连接。<br>
因为客户端和服务器端的ACK和FIN需要分开发送,所以需要四次
为什么TIME_WAIT等待时间是2MSL?
MSL:Maxmum Segment Lifetime报文最大生存时间。<br>任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。<br>
IP头有一个TTL字段,表示IP数据包可以经过的最大路由数,每经过<br>一个路由器此值减一,当TTL=0时,此数据包被丢弃。<br><br>MSL是时间,TTL是经过路由跳数。所以理论上MSL应该大于等于TTL.
极端情况下,网络中可能存在来自发送方的数据包,当这些发送方的<br>数据包被接收方处理后又被对方发送响应,所以一来一回要等待2倍的时间。<br>
如果TIME_WAIT时间内,因为客户端的ACK还没有传输到服务端,<br>客户端又接收到了服务端重发的FIN报文,那么2MSL时间将重新计时。<br>
Linux系统TIME_WAIT的时间为固定的60秒<br>#define TCP_TIMEWAIT_LEN (60*HZ)
为什么需要TIME_WAIT状态?
防止旧连接的数据包
足以让两个方向的数据包都被丢弃,<br>使得原来连接的数据包在网络中都<br>自认消失,再出现的数据包一定都<br>是新建立连接产生的。<br>
保证连接正确关闭
等待足够的时间以确保最后的ACK能让被动关闭方接收,从而帮助其正常关闭
如果服务器端没有收到四次握手中的最后一个ACK报文,则会重发 FIN 关闭连接报文并等待新的 ACK 报文
客户端在TIME_WAIT状态等待2MSL后,就可以保证双方的连接都可以正常的关闭
TIME_WAIT过多的危害
内存资源占用
对端口资源的占用,一个TCP连接至少消耗一个本地端口<br>可以通过以下参数设定:net.ipv4.ip_local_port_range
如果占满了所有端口,则会导致无法创建新连接。
TIME_WAIT优化
方法一:net.ipv4.tcp_tw_reuse 和 tcp_timestamps
可以复用处于TIME_WAIT 的 socket 为新的连接所用
如果引入时间戳,则2MSL问题就不复存在了。<br>因为重复的数据包会因为时间戳过期被自然丢弃。
方法二:net.ipv4.tcp_max_tw_buckets<br>默认值为18000
当系统中处于TIME_WAIT的连接一旦超过这个值时,<br>系统就会将所有的TIME_WAIT 连接状态重置。<br>
不推荐
方法三:程序中使用 SO_LINGER
struct linger so_linger;<br>so_linger.l_onoff = 1;<br>so_linger.l_linger = 0;<br>setsockopt(s, SOL_SOCKET, SO_LINGER, &so_linger,sizeof(so_linger));<br><br>如果l_onoff为非 0, 且l_linger值为 0,那么调用close后,会立该发送<br>一个RST标志给对端,该 TCP 连接将跳过四次挥手,也就跳过了<br>TIME_WAIT状态,直接关闭。<br>
不推荐
如果已经建立了连接,<br>但是客户端突然发生故障,怎么办?<br>
原理:如果没有任何连接相关的活动,TCP keepalive每隔一个时间间隔,<br>发送给一个探测报文,如果联系几个探测报文都没有得到响应,则认为当<br>前的TCP连接已经死亡,系统内核将错误信息通知给上层应用程序。<br>
内核参数,默认值:<br>net.ipv4.tcp_keepalive_time=7200<br>net.ipv4.tcp_keepalive_intvl=75<br>net.ipv4.tcp_keepalive_probes=9
<b>tcp_keepalive + (tcp_keepalive_intvl * tcp_keepalive_probes)</b><br>Linux系统经过2小时11分15秒<br>
需要考虑的情况
正常工作,TCP keepalive被重置
对端程序崩溃并重启,对端可以响应,但由于没有改连接的有效信息,<br>会产生RST报文,这样TCP连接被重置<br>
对端崩溃,或不可到达。达到keepalive的次数后,TCP会报告该TCP连接已经死亡
三次握手
为啥需要三次握手?
目的:初始化<b>Socket, 序列号</b>和<b>窗口大小</b>并建立连接
三次握手才可以<br>阻止重复历史连接的初始化<br>造成混乱。
历史连接情况图例
如果是两次握手,不能判断当前连接是否是历史连接。<br>三次握手的时候,客户端在准备发出第三次报文时,有足够的上下文信息来判断当前连接是否是历史连接:<br>1. 如果是历史连接,则发送RST报文,中止历史连接;<br>2. 如果不是历史连接,则发生ACK报文,通信双方成功建立连接。<br>
三次握手才可以同步双方的初始序列号
序列号是可靠传输的关键因素。<br>握手时,一来一回,才能确保双方的初始序列号能被可靠的同步。
三次握手才可以避免资源浪费
如果只是两次握手,当客户端发送SYN请求在网络中阻塞,<br>客户端没有收到ACK,就会重发SYN,由于没有第三次握<br>手,服务器不清楚客户端是否收到了自己发送的建立连接<br>的ACK确认报文,所以每个SYN就只能主动建立一个连接,<br><b>建立多个冗余的无效链接,造成资源浪费</b>。<br>
可靠
TCP头
重要控制位
SYC
"1"<b>表示希望建立连接</b>,<br>并初始化"序列号"(ISN)的字段<br>
第一个发出SYN:active open
第二个回复SYN:passive open
ACK
"1"表示“确认应答”字段有效
RST
1时,表示TCP连接中出现异常,必须强制断开连接
FIN
1时表示今后不再有数据发送,希望断开连接
包称之为 <b>Segment</b>
TCP的负载数据的长度
TCP数据长度 = IP总长度 - IP首部长度 - TCP首部长度
定义:TCP是<b>面向连接的、可靠的、<br>基于字节流</b>的<b>传输层</b>通信协议<br>TCP provides a <b><font color="#c41230">reliable flow</font></b> of <br>data between two hosts<br>
面向连接/Connection-oriented
必须一对一
可靠
无损坏
无间隔
非冗余
按序
字节流
消息没有边界,无论多大都可以进行传输
消息是有序的
即使先收到后面的字节,<br>也不会交给应用层处理,<br>同时对“重复”的报文<br>会自动丢弃<br>
可靠性和流量控制的信息
sockets
IP + Port
序列号<br>sequence numbers
解决乱序问题
可靠连接的关键因素
去除重复的数据
按序接收
可以标识发送出去的数据包中,<br>哪些是已经被对方收到的<br>
窗口大小<br>window size
用来做流量控制
By default 4096, 存在于每个包中
TCP四元组
源地址
32bit, 在IP头
源端口
目的地址
32bit, 在IP头
目的端口
最大TCP连接数
连接数 = 客户端IP数 x 客户端的端口数
限制条件
文件描述符限制
Unix中所有都是文件:socket也是文件
内存限制
客户端和服务端初始序列号是<b><font color="#c41230">不相同</font></b>的。
网络中的报文传输会延迟,会复制重发,也可能丢失。所以各自维护不同的随机初始序列号。
初始序列号产生算法
ISN = M + F(localhost, localport, remotehost, remoteport)<br>M: 计时器,每4ms递增<br>F : hash算法,生成一个随机数值。
既然IP层会分片,为什么TCP层还需要MSS呢?
基础知识
IP层分片原理:<br>当IP层发现整个报文超过MTU,则IP层自动进行分片,而保证么一个分片小于MTU。<br>分片后,有目标主机的IP层来进行重新组装,再交给上一层TCP传输层。<br><br>问题:<br>当如歌一个IP分片丢失,整个IP报文的所有分片都必须重传。因为IP层本身没有超时<br>重传机制,它有传输层的TCP来负责超时和重传。<br>当接收方发现TCP报文的某一片丢失后,则不会响应ACK给对方,那么发送方的TCP<br>在超时后,就会重发整个TCP报文(头部+数据)。<br>
解决方案:建立连接时,协商双方的MSS值,当TCP发现数据超过MSS(以太网时为1460Bytes)<br>时,则先会进行分片。如果TCP分片丢失,进行重发也是以MSS为单位。<br>
SYN攻击
攻击者短时间伪造不同IP地址的SYN报文,服务端每接收到一个SYN报文,<br>就进入SYN_REVD状态,但服务端发送出去的ACK+SYN报文,无法得到<br>未知IP主机的ACK应答,久而久之就会<b>占满服务端的SYN接收队列</b>(未连接队列)<br>
正常流程
避免方法一
修改Linux内核参数,控制队列大小和当队列满是应做什么处理。
当网卡接收数据包的速度大于内核处理的速度是,会有一个队列保存这些数据包。控制对垒的最大值的参数:<br>net.core.netdev_max_backlog
SYN_RCVD状态连接的最大个数:<br>net.ipv4.tcp_max_sync_backlog
超出处理能力是,对新的SYN直接回报RST,丢弃连接:<br>net.ipv4.tcp_abort_on_overflow
避免方法二
tcp_syncookies方式应对SYN攻击:<br>net.ipv4.tcp_syncookies = 1<br>
1. 当"SYNC队列"满之后,后续服务器收到的SYNC包,不进入"SYNC队列"<br>2. 计算出一个cookie值,再以SYN+ACK中的“序列号”返回客户端<br>3. 服务端接收到客户端的应答报文时,服务器会检查这个ACK包的合法性。如果合法,直接放到"Accept队列"<br>4. 最后应用通过accept() socket接口,从"Accept队列"取出连接
IP
不可靠
不保证网络包的<b>交付</b>
不保证网络包的<b>按序</b>交付
不保证网络包中数据的<b>完整</b>性
UDP头
不需要连接/Connectionless
TCP VS UDP
连接
服务对象
可靠性
拥塞控制、流量控制
子主题
性质区别
连接
服务对象
TCP 1 to 1
UDP 1 to 1/ 1 to Multi / Multi to Multi
可靠性
拥塞控制、流量控制
即使网络非常拥堵了,仍不会<br>影响UDP的发送速率<br>
头部开销
TCP>= 20 Bytes 在不考虑"选项”字段的时候
UDP = 8 Bytes
应用场景
TCP面向可靠传输
FTP/HTTP/HTTPS
UDP
包总量较少的通信
DNS/SNMP
视频、音频等多媒体通信
广播通信
三次握手
第一个报文
SYN
<ol><li>客户端 OUT 流打开,等待服务端确认</li></ol>
不可带数据
第二个报文
SYN + ACK
<ol><li>服务端确认输入流正常,</li><li>打开输出流,等待客户端确认</li></ol>
不可带数据
第三个报文
ACK
<ol><li>客户端确认输入流正常。</li></ol>至此,双工建立。都进入连接建立状态
可以带数据
TCP Socket编程
模型
握手和连接
关闭图解
要点
服务器端调用accept()时,返回一个已经完成三次握手,建立连接的socket,所有后续通信通过此socket
监听socket <font color="#c41230">!=</font> 传输数据的socket
0 条评论
下一页