TCP协议栈与posix api
TCP协议栈主要关注三个方面
1、建立连接:三次握手
2、传输过程
3、断开连接:四次挥手
建立连接
过程解析:
服务端:tcp建立连接时,server端需要保持客户端的信息,会在半连接队列中存储;当client收到server的syn和ack包后,返回一个信息给server,server收到后检测ip/port信息,若在半连接队列(syn队列)中找到,就将节点移动到全连接队列(accpet队列)中(三次握手的最后一步完成后)。只有进入到全连接队列中,accept才能处理。
accpet函数作用:
1、从全连接队列中拿出一个节点;
2、将节点分配个fd,返回fd;
若没有节点,就会进入阻塞状态,等待全连接队列有节点;
设置fd为非阻塞的,fcntl(fd,noblock);
判断全连接队列为空,直接返回-1,不会再等待。
三次握手是客户端发生connect中,服务端是被动实现的,是在进入Listen状态之后,三次握手完成后调用accpet函数。三次握手是发生在协议栈之间的操作。
listen函数就是将fd置为一个listen状态,可以进行三次握手。
小疑问:
1、如何在半连接队列中如何找到对应客户端的节点?
通过五元组找到(sip,dip,sport,dport,proto),五元组信息在tcp头,ip头可以得到;
2、节点在什么时候释放,生命周期是多长?
周期在close()结束;
tcp状态机的11个状态存在哪里?存储在半连接队列的节点中,
send/recvbuffer存储在哪里?
怎么通过五元组找到fd,也存储在半连接队列的节点中,该节点也叫tcp控制块,或者叫tcb;
数据传输:通信过程
tcp协议头:
tcp(是流式套接字:先发的先到)如何保证顺序的?
背景:A发送100个包给B:A发送一个包给B,B返回一个确认信息,A收到后才发第二个包,以此类推… 但是这样效率低,通常是A一次发送1,2,3…等包,边等待接收确认信息边发送数据包给B。但是多个包传输时,在公网上很难保证先发的先到。
保证收到的是顺序的:
tcp协议栈的超时重传机制:每接收到一个数据包时,协议栈会启动重置200ms定时器,一旦超时就会检测收到包的顺序,检测到3号包没收到,就将其之后的包重传,比如4好包(ACK包数值的意思是多少号包之前的都收到了)。
tcp缺点:
ack确认时间长,
重发的次数多
如何做到一次性发多个包?多个包时如何计算的?
慢启动,一开始发送第一个包,收到确认信后;第二次发送两个包,收到确认信息后,第三次就发4个包,第四次就发8个包。
梅县指以后的线性增长方式叫拥塞控制,告诉发多少包。之前的叫慢启动。
send函数成功只能代表是将应用程序的数据拷贝到了协议栈中,并不能代表数据已经成功发送到对端。(后期搜索拥塞控制、滑动窗口和慢启动)
断开连接:
对应用程序而言两个程序实现:
close
shutdown
四次挥手:
接受端收到close的第一个fin包,接收端fd触发epollrbhub,读关闭了;
两端都关闭了,换句话说接收端也发送fin包出去,并接收到返回的ack包,fd还在epoll中就会epollhup,读写都关闭了
问题:
1、大量的timewait原因是什么,大量的closewait怎么处理
主动调用close的一端才会出现这个问题,原因的查找:
(1)主动调用close的业务场景是否合理,查自己的逻辑;
(2)若合理,调用setsockopt(), 设置为reuse,重用
大量close_Wait:
接收端收到第一个fin包时,可以通过recv()=0检测到,再调用close。
出现大量close_wait是close调用过程延迟了。已经知道客户端主动关闭了,要释放客户端的相关业务信息,逻辑处理时间有点长,所以close有些延迟滞后。
代码检查就是:recv=0时,有没有及时的调用close。正确时机是:recv=0后立刻调用close,相关的clientfd释放应该开启个线程进行处理;
2、大量的fin_wait1,fin_wait2怎么处理,有没有方法中止?
fin_wait2状态没有方法终止;逻辑代码没法处理,想强行处理就是kill操作;
面试常问点:
1、TCP三次握手过程?
2、TCP四次挥手过程
3、建立连接为什么要3次,断开连接要四次握手
4、time_Wait状态时间和造成的原因
5、超时重传和快速重传
6、TCP在listen时的参数backlog的意义
7、tcp首部长度,有哪些字段?
8、Accept发生在三次握手的哪一步
9、三次握手过程中有哪些不安全性
10、tcp与udp的区别