为什么要三次握手不能两次
为了防止已失效的连接请求报文突然又传送到服务端,从而产生错误。
例如client发出的第一个SYN没有丢失,而是在某个网络节点长时间滞留了,以致于延误到连接释放以后的某个时间点到达server。此时server收到SYN后认为这是client发出的一个新的连接请求,会向client发送ACK,同意建立连接。此时如果是两次握手,server就会以为新的连接已经建立,浪费了系统资源;如果使用三次握手,server由于收不到确认,就会知道client并没有要求建立连接。
TCP的三次握手与accept的关系
- 服务端调用listen进行监听
- 客户端调用connect发送SYN,发起连接
- 服务端协议栈负责三次握手过程,握手成功后往listen队列中添加一个成功连接,直到队列满
- 服务端调用accept会从listen队列中取出一条成功的TCP连接,listen队列中的连接个数减少一个
所以accept发生在三次握手成功之后,如果服务端因为某种原因没有调用accept,只要listen队列未满,也会正常进行三次握手。
bind函数
服务端与客户端都可以bind,并不是只有服务端才可以bind;
bind端口
需要在建立连接前就知道端口的话,需要bind;
需要通过指定的端口来通迅的话,需要bind;
客户端一般不需要bind,由系统自动分配端口号;
bind IP地址
客户端:为发送出去的数据分配源IP地址,但这个IP地址必须是主机的一个接口,不能分配一个不存在的IP;
服务端:限制socket只接受目的地为此IP地址的客户链接;
什么情况下会出现RST包
- 防火墙
- 试图连接对方未打开的端口时(如果对方是backlog满了的话SYN会被丢弃,表现为超时而不是RST)
- close时recv buffer不为空,此时TCP会认为数据没有正确提交到应用,发送RST释放连接
- 收到非正常包,比如连接已经关闭,SEQ不正确等
- 一方关闭了连接,但另一方没有收到结束报文还维持着连接,此时如果往该连接写数据会收到RST,原因同上一条
- 某方长期未收到对方的确认报文,会在超出重传次数或时间后,主动向对方发送RST释放连接
- 程序崩溃或异常退出,会由操作系统向对方发送RST,让对方释放连接
- 应用程序主动使用RST以达到快速释放连接的目的(例如SO_LINGER),不属于正常的4次挥手流程,可能会造成新建立的连接与旧连接的数据混乱
socket什么时候是可读的,什么时候是可写的
可读
- 缓冲区内有数据时,对socket进行读操作不会阻塞,而是成功返回一个>0的值,即可读数据的大小
- 该连接的读半部关闭(接受了FIN的TCP连接)。对该socket进行读操作不会阻塞,而会返回0(EOF)
- 该socket为监听socket,且backlog队列不为空;正常情况下此时对该socket进行accept操作不会阻塞
- 该socket有异常错误条件待处理,此时对其进行读操作不会阻塞并且返回错误
可写
- 缓冲区有可用空间,且socket已连接(如TCP)或socket不要求连接(如UDP),写操作将不会阻塞并返回一个>0的值(由传输层接收的字节数)
- 该socket写这一半已经关闭(主动发送过FIN),此时写操作会产生SIGPIPE
- 非阻塞的connect已创建连接,或connect已经失败。即connect有结果了
- 该socket有错误等待处理,此时写操作不阻塞并返回错误
单机最大支持同时多少个TCP连接
系统用一个四元组唯一标识一个TCP连接:源IP,源端口,目的IP,目的端口,因此只考虑用作server时最大支持的TCP连接数应为不同IP地址数量*端口号数量,IPv4下为2^32 * 2^16 = 2^48;考虑到IP地址分类等,实际上略少;全部用作client时最大连接数为65535,可以连接到不同的server
但是实际环境中,连接数会受到机器资源、操作系统的限制;linux下限制连接的主要是内存与文件标识符的个数。通过增加内存、修改最大文件描述符个数等参数,单机最大并发TCP连接数超过10万,甚至上百万是没有 问题的。
TCP如何确保可靠传输
- 三次握手,保证建立连接
- 对数据合理分片,编号保证有序传输
- 数据校验和,如果校验和有误丢弃该数据包
- 确认、重传机制
- 流量控制,防止接收方处理数据较慢时数据溢出、丢包
- 拥塞控制,网络拥塞时减少数据发送
UDP应用场景
- 非常在乎延迟,不能忍受重传 && 不在乎可靠性,丢一些包也可以不重传(eg:实时音视频)
- NAT穿透
- 组播、多播、广播
TIME_WAIT
主动关闭TCP连接一方会进入TIME_WAIT状态,需要等待2MSL(报文最大生存时间)后才进入CLOSED状态,这段时间内该连接不可重用。
作用
保证上一个连接的数据包不再残留于网络中,因为可能会影响到新连接
避免由于最后发送的ACK丢失导致另一方无法正常关闭连接
如何避免
尽量使用长连接或复用连接;
尽量让对方主动关闭连接;
设置tcp_tw_recycle与tcp_timestamps: 开启TIME_WAIT连接快速回收;(可能导致NAT后的设备连接成功率降低,因为同一NAT后的不同设备在服务端看来IP是一样的,无法保证timestamp严格递增)
设置tcp_tw_reuse与tcp_timestamps: 复用TIME_WAIT连接;
配置tcp_max_tw_buckets: 强制控制TIME_WAIT总数;
使用RST强行关闭连接(SO_LINGER),可能造成新连接与旧连接数据混乱;
网友评论