Linux(socket网络编程)I/O缓冲、三次握手、四次挥手
- TCP套接字的I/O缓冲
- I/O缓冲的特性
- 滑动窗口协议Sliding Window
- TCP三次握手
- 提要
- TCP四次挥手
TCP套接字的I/O缓冲
(1)write函数调用后,并非立即传输数据。
read函数调用后,也不是马上接收数据。
write函数调用时,数据是先被移至输出缓冲;
read函数调用时,数据从输入缓冲读取数据。
(2)write返回的值,是成功写到输出缓冲的字节数。
read返回的值,是成功从输入缓冲读到的字节数。
(3)不同socket所拥有的I/O缓冲是不同的,即使是相同IP、相同端口号。
I/O缓冲的特性
(1)I/O缓冲在每个socket(TCP套接字)中单独存在。
(2)I/O缓冲在创建套接字时自动生成。
(3)关闭套接字后,输出缓冲中的剩余数据会继续传递。
(4)关闭套接字后,会丢弃输入缓冲中的数据。
滑动窗口协议Sliding Window
缓冲大小100,对方却一次发来100字节的数据怎么办?
TCP的滑动窗口协议就是为了解决这样的问题。
TCP中不会因为缓冲溢出而丢失数据。
但会因为缓冲而影响传输效率。
TCP三次握手
提要
TCP连接建立时,分客户端和服务端;
服务端建立步骤:
1.建立套接字、套接字结构体
2.bind函数给套接字绑定地址族、IP(监听的IP)、端口号
3.调用listen函数进行监听
4.accept函数接受连接,完成三次握手
5.write、read 进行通信
客户端建立步骤:
1.建立套接字,套接字结构体
2.设置结构体的地址族、IP(服务端IP),端口号(服务端端口号)
3.调用connect向服务端发起连接,客户端套接字自身的IP和端口号,是通过connect函数自动赋予。
未建立连接时的状态:
服务端:listen
客户端:close
注:下面说的SYN,SYN+ACK表示的是报文,不是参数、、、
第一次握手:
客户端发送SYN报文
SYN报文包含SEQ,假设这里SEQ的初始值为1000;
状态:
服务端:listen
客户端:SYN_SENT
第二次握手:
服务端收到SYN,同意连接,发出SYN+ACK
这里ACK=1001,用来和上述的SEQ=1000对应;
假设这里的SEQ=2000;没错,各自发送的SEQ值是不一样的
状态:
服务端:SYN_RECV
客户端:SYN_SENT
第三次握手:
客户端收到SYN+ACK后,向服务端发送ACK------状态置为ESTABLISHED
服务端收到ACK------状态置为ESTABLISHED
这里回复的SEQ=1001,因为上一次是1000;
回复的ACK=2001,因为上一次接收到的SEQ=2000;
状态:
服务端:ESTABLISHED
客户端:ESTABLISHED
说明:
(1)上述的SEQ是一个序列号,32位无符号整数类型。而我假设的SEQ=1000、SEQ=2000,这里假设的1000、2000其实就是初始序列号(ISN)。
(2)建立完成后,在后续的数据传递过程中,SEQ也会继续跟随数据段发送,继续递增,用来记录数据段的顺序。
(3)SEQ超过其最大值2^32-1后又回到0继续递增。
(4)上述的状态变化,是基于一个正常完成的TCP三次握手,那要是中间出现什么问题了,比如服务端回复了SYN+ACK,客户端迟迟没有回复ACK;那服务端就会进行重发,一直都不回复就一种重发,直到超时;那么在这个重发的过程中,服务端的相应资源也是被占用的;
(4.1)这样一个机制,就使得有一种针对服务端的攻击:SYN Flood攻击,即客户端伪造很多SYN向服务端请求连接,但刻意不完成第三次握手,占用服务端的连接表、内存等资源。严重时会导致服务端无法正常接受其它请求。
(4.2)防御方法:
》增加资源,治标不治本
》使用防火墙过滤掉恶意的SYN请求
》启用SYN Cookie:服务端接受SYN时不立刻分配内存资源,而是收到客户端的ACK后,验证完成后,才分配内存资源。
》部署入侵检测系统(IDS)和入侵防御系统(IPS):当短时间内接收到大量来自同一源IP的SYN请求时,系统应能自动识别并采取措施。
》部署负载均衡器:负载均衡器可以将流量分散到多台服务器上,从而降低单台服务器的压力。在遭受SYN Flood攻击时,负载均衡器可以快速将流量切换到其他未受影响的服务器上,保证服务的连续性。
TCP的重要机制:
序列号回绕机制、确认应答机制、超时重传机制、窗口控制机制、处理序列号冲突等;
TCP四次挥手
报文:FIN、ACK
角色:主机A、主机B
注意了,挥手的时候,谁主动都可以。
第一次挥手:
A发:FIN------A的状态置为fin_wait_1
状态:
A:fin_wait_1
B:established
第二次挥手:
B发:ACK-------B的状态置为close_wait
A收:ACK------A的状态置为fin_wait_2
状态
A:fin_wait_2
B:close_wait
第三次挥手:
B发:FIN-------B的状态置为last_ack
第四次挥手:
A收:FIN
A发:ACK-------A的状态置为time_wait
B收:ACK-------B关闭连接进入closed状态
而A在time_wait状态滞留一段时间后,没有再收到B的报文,也把状态置为closed
注:FIN和ACK报文中,也包含了seq和ack;
第二次挥手和第三次挥手都是由被动方发报文;
原因在于:
第二次挥手是告诉主动方:我已经收到你的请求了,但请再等等,我要把我的业务处理完。
第三次挥手是告诉主动方:我这边也完事了,该发的也发完了,咱结束吧。
而最后第四次挥手中,A发送ACK后,还要在time_wait状态等待的原因是:
确保对方确实没有报文要发了,不然要是对方有发出的报文因为延迟而姗姗来迟。A却早早的结束了连接,释放了资源。就无法收到这迟到的报文了。
而如果A已经close了,B的连接却依然存在。那么后续新建立的连接也会受B的旧链接报文的影响。
四次挥手确保了全双工连接的可靠关闭,防止了旧链接数据包对新连接产生干扰。