Socket-TCP
在TCP/ip协议中,用源IP、源端口号、目的IP、目的端口号、协议号这样一个五元组来标识一个通信!
端口号范围划分
- 0 - 1023: 知名端口号,HTTP,FTP,SSH 等这些广为使用的应用层协议,他们的端口号都是固定的。
- 1024 - 65535: 操作系统动态分配的端口号。客户端程序的端口号,就是由操作系统从这个范围分配的。
认识知名端口号 (Well-Know Port Number)
有些服务器是非常常用的,为了使用方便,人们约定一些常用的服务器,都是用以下这些固定的端口号:
- ssh 服务器,使用 22 端口
- ftp 服务器,使用 21 端口
- telnet 服务器,使用 23 端口
- http 服务器,使用 80 端口
- https 服务器,使用 443
执行下面的命令,可以看到知名端口号
cat /etc/services
一个进程可以绑定多个端口号一个用来收数据一个用来传数据;一个端口号不可以被多个进程绑定。
TCP(传输控制协议,Transmission Control Protocol )是一种位于传输层的通信协议。
应用场景
适用于对数据准确性、完整性、顺序性要求高的场景 ,如文件传输(FTP 基于 TCP ) 、网页浏览(HTTP 基于 TCP ) 、电子邮件(SMTP 基于 TCP ) 等。
TCP协议格式首部
- 源端口号(16 位):标识数据发送方应用程序端口,便于接收方后续回传数据能找到对应端口 。比如客户端浏览器发起 HTTP 请求,会用一个随机的源端口号。
- 目的端口号(16 位):指明数据要抵达的目标应用程序端口,像 HTTP 协议默认目的端口号 80 ,用于定位 Web 服务器应用 。
- 序号(32 位):给 TCP 传输的每个字节编号,在建立连接和数据传输时,保证字节按序传输 。例如发送一篇长文档,每个字节都有序号。
- 确认序号(32 位):接收方告知发送方下一个期望接收的字节序号,发送方依此判断哪些数据已成功接收,哪些需重传 。
- 首部长度(4 位):以 4 字节为单位指示首部长度,因首部中选项字段可变长,取值范围对应 0 - 60 字节 ,无选项时通常为 20 字节 。
- 保留(6 位):预留字段,当前未使用,默认置 0 。
- 标志位(6 位):
- 窗口大小(16 位):这个窗口大小其实代表的是缓冲区里面剩余空间把它放在报头里面目的是,想让接收方得知目前传送过去的数据,对方的接受速度是多少,来调整一下自己的发送速度(流量控制)。
- 检验和(16 位):对 TCP 首部、数据及伪首部计算校验和,检测传输过程中数据有无错误 。
- 紧急指针(16 位):当 URG 标志位为 1 时有效,指向紧急数据末尾,方便接收方优先处理紧急数据 。
选项与数据
- 选项:可选字段,长度可变,用于实现如最大段长度声明、窗口扩大等扩展功能 。
- 数据:即有效载荷,承载应用层传来的数据,TCP 负责可靠传输这部分数据 。
超时重传机制:
如果你的数据发送过去后没有得到应答,等到了一定时间就会重新补发,这个机制叫做超时重传机制,这个时间是动态的,会根据你的网络环境进行调整的,当你面临的情况是应答丢失的时候,此时它主机b会得到两个相同的数据,此时的主机b会进行去重操作,会根据你的报头里面的32位序号进行查重去重!当你面临的情况是始终重传不成功,系统会认为你的网络出现了异常,会自动断开两个主机的连接!
面对主机B收到重复的信息,此时操作系统可以根据报文里面的序号,进行去重操作!
TCP 连接管理机制详解
TCP 连接管理主要包括建立连接和断开连接两个过程,分别通过三次握手和四次挥手实现。
TCP的三次握手:刚开始客户端进行了connect在客户端状态没有变为ESTABLISHED之前一直处于阻塞状态,第一次握手是客户端给服务器发送的报文是SYN类型的(申请建立连接)此时的客户端状态为SYN_SENT,当你服务器收到发来的申请后状态变为SYN_RCVD,然后向客户端发送SYN+ACK(申请建立连接和确认收到了客户端发来的SYN)这是第二次握手,当客户端收到了来自服务器的应答后状态变为ESTABLISHED,随后向服务器发送ACK(确认自己收到了应答),收到信息后的服务器状态变为ESTABLISHED,此时的服务器会把刚刚完成三次握手后分配到的新的连接fd,交给accept,第三次握手的时候其实是已经可以携带数据了,做出捎带应答!
建立连接以后的通信:所用到的write和read其实都是面向发送缓冲区和接受缓冲区进行操作的!
TCP四次挥手:第一次挥手是客户端给服务器发FIN,客户端的状态变为FIN_WAIT_1,服务器接受到了以后状态变为CLOSE_WAIT,第二次挥手然后给客户端发送ACK应答,接收到信息的客户端状态变为FIN_WAIT_2,第三次挥手紧接着服务器会向客户端发送FIN,状态变为LAST_ACK,客户端收到断开连接请求以后状态变为TIME_WAIT,第四次挥手就是客户端给服务器的应答,状态变为CLOSED!
解决疑问:
至少三次握手是因为:1.客户端和服务端各自都要至少进行一次收报文和发报文,这样其实可靠验证了全双工通路是否通畅!!!2.奇数次握手,在一般情况,握手失败的连接成本嫁接给了客户端!
为什么两次不行是因为:当你服务器再发回给客户端的信息,你并不能知道客户端到底有没有收到!
为什么一次不行是因为:会发生SYN洪水,直到服务器内存不够分配的时候会导致服务中断!
插入知识点:SYN洪水:
SYN洪水不止一次握手会出现:SYN 洪水(SYN Flood)是一种常见的 DoS(Denial of Service,拒绝服务)攻击手段,它利用了 TCP 协议在建立连接时三次握手的漏洞来实施攻击,以下是关于 SYN 洪水攻击的详细介绍:
攻击原理
正常情况下,TCP 建立连接需要经过三次握手。客户端发送 SYN 包请求建立连接,服务器收到后回复 SYN + ACK 包并分配资源等待客户端的 ACK 包完成连接建立。而 SYN 洪水攻击中,攻击者会伪造大量源 IP 地址,向目标服务器发送大量的 SYN 包。服务器收到这些请求后,会为每个请求分配资源并发送 SYN + ACK 包进行响应,然后等待客户端的 ACK 包。由于攻击者使用的是伪造的 IP 地址,服务器无法收到这些伪造客户端的 ACK 确认,会不断重传 SYN + ACK 包,并长时间保留这些半连接状态的资源,直到超时。当服务器的半连接队列被填满后,就无法再处理正常用户的连接请求,导致正常用户无法与服务器建立连接,从而实现拒绝服务的目的。
防范措施
- 防火墙配置:通过防火墙设置规则,限制来自同一 IP 地址的 SYN 请求速率,防止单个 IP 地址发送过多的 SYN 包。
- TCP SYN Cookie 技术:服务器在收到 SYN 包时,不立即分配资源,而是根据 SYN 包的信息生成一个特殊的 Cookie(一种加密的序列号),并将其包含在 SYN + ACK 包中发送给客户端。当客户端返回 ACK 包时,服务器根据 ACK 包中的信息验证 Cookie 的有效性,如果验证通过,则建立连接。这样可以避免服务器为大量伪造的 SYN 请求分配资源。
- 增加服务器资源:增加服务器的内存、CPU 等资源,提高服务器的处理能力和抗攻击能力。
- 入侵检测系统(IDS)/ 入侵防御系统(IPS):部署 IDS/IPS 系统,实时监测网络流量,检测并阻止 SYN 洪水攻击。
从这个攻击原理其实可以明白:在三次握手中最怕的一次握手是第三次客户端给服务器的应答ACK,假如你是只有两次握手,那你的第二次握手丢失以后整个压力就会给到服务端,导致服务崩溃,但是如果你是三次握手,就不是这样,因为如果丢失了第三次握手,整个压力就会给到客户端上,这样就能保证服务器的稳定性!
四次挥手原因:
它不进行捎带应答是因为服务器与客户端进行关闭通信的时候存在着“协商”!
- TCP 协议规定,主动关闭连接的一方要处于 TIME_WAIT 状态,等待两个 MSL (maximum segment lifetime) 的时间后才能回到 CLOSED 状态。
- 我们使用 Ctrl - C 终止了 server,所以 server 是主动关闭连接的一方,在 TIME_WAIT 期间仍然不能再次监听同样的 server 端口;
为了避免这种情况,所以可以去用setsockopt。
- MSL 在 RFC1122 中规定为两分钟,但是各操作系统的实现不同,在 Centos7 上默认配置的值是 60s;
- 可以通过
cat /proc/sys/net/ipv4/tcp_fin_timeout
查看 msl 的值; - 规定 TIME_WAIT 的时间请读者参考 UNP 2.7 节;
MSL:一个报文它在网络中存活的时间是有限的!
当你第三次挥手结束,如果第四次挥手丢失,导致服务端收不到最后的一个ACK,此时Time_wait的时间可以让服务端端有足够的时间去补发一个FIN!等待时间一般为两个MSL(在传输过程中最大存在时长,这个一般是不确定的,因为如果遇到一个阻塞,那存在时长就大了)
流量控制:
接收端处理数据的速度是有限的。如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包,继而引起丢包重传等一系列连锁反应。
因此 TCP 支持根据接收端的处理能力,来决定发送端的发送速度。这个机制就叫做流量控制 (Flow Control):
- 接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 “窗口大小” 字段,通过 ACK 端通知发送端;
- 窗口大小字段越大,说明网络的吞吐量越高;
- 接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端;
- 发送端接收到这个窗口之后,就会减慢自己的发送速度;
- 如果接收端缓冲区满了,就会将窗口置为 0;这时发送方不再发送数据,但是需要定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端。
流量控制属于可靠性还是效率?可靠性!
滑动窗口:
刚才我们讨论了确认应答策略,对每一个发送的数据段,都要给一个 ACK 确认应答。收到 ACK 后再发送下一个数据段。
这样做有一个比较大的缺点,就是性能较差,尤其是数据往返的时间较长的时候。
滑动窗口的大小,根据接受方缓冲区的剩余大小进行调整,这样可以保证滑动窗口里面的数据一次性传给接收方不会导致数据丢包!
关于缓冲区:
TCP 协议依靠发送缓冲区来保存已经发出但未收到应答的报文 。具体如下:
- 缓冲区划分:发送缓冲区通常分为三部分。已发送已确认的数据,这部分数据传输已完成,可被后续数据覆盖;已发送未确认的数据,即暂时保存等待应答的报文所在区域,这部分区域也属于滑动窗口范畴,其最大范围由对方接收窗口大小决定 ;未发送未确认的数据,是等待发送且未得到接收方接收许可的数据。通过设置指针(如指向窗口开始和结束的指针)或数字下标来区分这些区域,随着数据发送和确认,指针移动实现窗口滑动 。
- 保存作用:发送缓冲区保存已发送未确认报文,是为实现可靠传输。若发送方在一定时间(由超时重传时间 RTO 决定 )内未收到接收方对应答报文的确认,就会从发送缓冲区取出对应报文副本重传 。 比如网络拥塞导致报文丢失或延迟到达接收方,接收方无法及时返回确认,发送方超时后从缓冲区取出报文重发,保障数据最终能送达接收方。 同时,滑动窗口机制也基于发送缓冲区这些划分来控制数据发送速率,避免接收方处理不过来。
传输异常处理:
若窗口更新通知在传输途中丢失,发送主机 A 无法得知接收主机 B 窗口状态变化,会导致通信受阻。此时,发送主机 A 会定时发送窗口探测包,来主动获取窗口信息,以便恢复数据传输。
滑动窗口机制通过动态调整窗口大小,在保障数据可靠传输的同时,还能适应网络和接收端的不同状态,有效提升网络传输效率,避免网络拥塞和数据丢失。
注意:快重传并不是超时重传机制!超重传机制可以理解为是兜底的,而快重传是提高效率的!
快重传机制是 TCP 协议中用于快速检测并恢复丢失数据包的机制 。当接收方收到乱序数据包时,会立即向发送方发送重复确认 。当发送方累计收到三个相同的重复确认时,就会判定对应的数据包已丢失,不等重传超时,直接重传该数据包 。比如发送方发送数据包 1、2、3、4 ,若数据包 2 丢失,接收方收到 1、3、4 后发送重复确认,发送方收到三个重复确认,就快速重传数据包 2 。该机制减少了网络传输延迟,提升了 TCP 协议在网络传输中的效率和可靠性 。
延迟应答
如果接收数据的主机立刻返回 ACK 应答,这时候返回的窗口可能比较小。
- 假设接收端缓冲区为 1M,一次收到了 500K 的数据;如果立刻应答,返回的窗口就是 500K;
- 但实际上可能处理端处理的速度很快,10ms 之内就把 500K 数据从缓冲区消费掉了;
- 在这种情况下,接收端处理还远没有达到自己的极限,即使窗口再放大一些,也能处理过来;
- 如果接收端稍微等一会再应答,比如等待 200ms 再应答,那么这个时候返回的窗口大小就是 1M;
一定要记得,窗口越大,网络吞吐量就越大,传输效率就越高。我们的目标是在保证网络不拥塞的情况下尽量提高传输效率;
那么所有的包都可以延迟应答么?肯定也不是;
- 数量限制:每隔 N 个包就应答一次;
- 时间限制:超过最大延迟时间就应答一次;
拥塞控制
虽然 TCP 有了滑动窗口这个大杀器,能够高效可靠的发送大量的数据。但是如果在刚开始阶段就发送大量的数据,仍然可能引发问题。
因为网络上有很多的计算机,可能当前的网络状态就已经比较拥堵。在不清楚当前网络状态下,贸然发送大量的数据,是很有可能引起雪上加霜的。
TCP 引入慢启动机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据。
一个主机缓解不了网络拥塞,但是一群主机就可以缓解了!
看下图分析:
慢启动
TCP 启动时,拥塞窗口(cwnd)初始值通常为 1 ,慢启动阈值(ssthresh)等于窗口最大值(如初始值为 16 ) 。在慢开始阶段,cwnd 按指数规律增长。每收到一个确认应答,cwnd 就增加 1 。这使得发送方发送的数据量迅速增加,网络吞吐量逐渐上升。例如,从传输轮次 0 开始,cwnd 从 1 逐步增长,快速提升数据发送量。
拥塞避免
当 cwnd 增长到慢启动阈值(如达到 16 )后,进入拥塞避免阶段。此时,cwnd 不再按指数增长,而是采用 “加法增大” 策略,即每收到一个确认应答,cwnd 增加一个较小的固定值(如 1 ) 。这种方式让发送方数据发送量缓慢、平稳增加,防止网络因发送数据量增长过快而拥塞。
网络拥塞处理
若出现大量丢包,判定为网络拥塞。此时采取 “乘法减小” 策略,ssthresh 值变为原来的一半(如从 16 变为 12 ) ,同时 cwnd 置回 1 ,重新进入慢开始阶段。这能迅速降低发送方数据发送量,缓解网络拥塞。少量丢包时,仅触发超时重传,不调整 ssthresh 和 cwnd 。
TCP 拥塞控制机制在追求快速数据传输的同时,平衡网络负载,避免拥塞,就像热恋中把握节奏,既积极又不过度,保障网络通信顺畅。
TCP是面向字节流:
面向字节流:读和写不需要匹配。TCP是面向字节流,UDP面向数据报,udp写一次就得读一次!
粘包问题:说简单就是,不知道一段报文从哪开始到哪结束,从而导致读取报文错误!
TCP异常情况:
当你的进程终止的时候(无论是客户端还是服务端或者两个同时),此时我们应该知道linux下一切皆文件,包括通信。有因为文件的生命周期是随进程的,所以进程终止了,通信也要结束了,此时通信进行正常的四次挥手既可!