TCP POSIX API网络协议栈实现原理

有哪些底层api接口

服务器端

  • socket
  • bind
  • listen
  • accept
  • recv
  • send
  • close

客户端

  • connect

epoll

  • epoll_create
  • epoll_ctl
  • epoll_wait

TCP的三个过程

建立连接

tcp和udp的connect函数的作用

tcp的connect 只有在服务器的状态是listen时,才能发送

udp的connect只是验证一下这条路径是否通畅,不会建立连接

三次握手发送在哪个函数

客户端:accept函数

服务器:listen和accept函数之间

半连接队列和全连接队列

当服务器绑定、监听了指定端口后,内核通常会为每一个LISTEN状态的socket维护两个队列:

  • SYN队列(半连接队列):由/proc/sys/net/ipv4/tcp_max_syn_backlog指定,表示处于 SYN_RECV 状态的队列
  • ACCEPT队列(全连接队列):由listen()函数的第二个参数 backlog 指定,内核硬限制由 net.core.somaxconn 限制,即实际的值由min(backlog,somaxconn) 来决定。表示已完成连接的队列,等待被 accept系统调用取走。

在这里插入图片描述

调用listen函数,会发生什么

服务器端将fd设置为listen状态,此时才能进行三次握手,第二个参数是syn半连接队列的长度,现在指的是三次握手完成后的队列,也就是全连接队列。

三次握手细节

在这里插入图片描述

客户端调用connect函数,就把IP地址端口号,copy到协议栈中,协议栈自身会准备一个包syn包,发给对端

服务器收到以后,将节点数据保存在syn半连接队列中,返回客户端一个ack syn包

客户端发送ack后,服务器将半连接队列中的节点移动到accept全连接队列中,该节点被称为TCB控制块

在这里插入图片描述

第一次握手时,服务端需要在TCB中保存客户端的连接

第三次握手时,判断客户端数据是否存在半连接队列中,若存在,移动到全连接队列中。

服务器调用accept函数,做了什么

  1. 从全连接队列中取出一个连接节点
  2. 把节点分配一个fd ,返回fd

服务器调用accept时,全连接队列中没有TCB控制块会怎么样

如果全连接队列中没有连接节点,就会进入阻塞等待

如果listenfd设置为非阻塞,判断全连接队列中是否有连接节点,没有则返回

SYN队列满会发生什么情况

如果SYN队列满,则会直接丢弃连接请求。
比如syn floods 攻击就是针对半连接队列的,攻击方不停地建连接,但是建连接的时候只做第一步,第二步中攻击方收到server的syn+ack后故意扔掉什么也不做,server需要一个超时时间把这个连接断开,否则大量这样的连接导致server上这个队列满其它正常请求无法进来。

ACCEPT队列满会发生什么情况

如果ACCEPT队列满了,server 通过 /proc/sys/net/ipv4/tcp_abort_on_overflow 来决定如何返回:

  • tcp_abort_on_overflow 为 0,不会把连接从SYN队列中移除,server过一段时间再次发送syn+ack给client(也就是重新走握手的第二步),这样来回重发几次,次数由 /proc/sys/net/ipv4/tcp_synack_retries(centos默认为 5 ) 指定,如果三次握手第三步的时候 ACCEPT 队列一直是满,那么server扔掉client 发过来的 ACK(在server端认为连接还没建立起来);
  • tcp_abort_on_overflow 为 1 ,表示第三步的时候如果 ACCEPT 队列满了,server发送一个RST包给client,表示废掉这个握手过程和这个连接(本来在server端这个连接就还没建立起来),客户端会出现 connection reset by peer 的异常。

如何找到半连接队列中的TCB控制块

通过五元组,源ip 目的ip 源端口 目的端口 协议 五个因素决定。

TCB控制块中需要存储哪些信息

tcp状态机的11个状态、发送缓冲区和接收缓冲区

传输过程

tcp是如何保证单个数据包的顺序的?这样做有什么缺点

每接收到一个包,协议栈就会启动一个200ms的定时器,每接收到一个包,就会重置这个定时器。如果哪个包超时,则会重发以后的包。

缺点:确认时间长,ack的意思是此号包之前的数据全部收到,后面的包没有收到,后面的包即使收到了,前面的包也会重发。

多个数据包的收发,慢启动,拥塞控制

在这里插入图片描述

  • 第一阶段(慢启动):第一次发1个包,第二次发2个包,第三次8个包,以2的指数方式增长发送数据包的个数,当达到门限值,进入第二阶段。
  • 第二阶段(拥塞控制):在第一阶段进入第二阶段后,发包数量按照固定的斜率增长,当发送数据包到达一定数据,发送数据包个数变为原来的一半,继续增长发送。主要是为了控制流量。

数据传输过程中调用send函数会发生什么

调用copy_from_user将数据从用户空间拷贝到协议栈的sendbuffer中,数据发送的过程是在协议栈中解决的

断开连接

四次挥手细节

在这里插入图片描述

在右端看来,对端调用close,读关闭

接收到了fin包,epoll中就能触发EPOLLRDHUP

自己也发送fin包,收到ack后,fd会触发epollhub

出现大量的time_wait?

主动方(主动先调用close的一端)才会产生time_wait 1. 这时查看自己的逻辑;2. 通过setsockopt()设置为reuse(重用),使tcb不被释放而重用,一定程度上减少time_wait

出现大量的close_wait?

接收到主动发送的fin包以后,recv返回0,调用close的过程延迟,因为此时需要处理业务,发生未发送的数据。

解决方法,收到返回的0以后立即调用close,资源的释放等行为交给线程处理

出现大量fin_wait_1或者2?

客户端没有接收到ack包,导致长期处于fin_wait_1的状态。解决方法:无解只能kill

UDP

UDP应用场景

  1. 大量数据传输,下载,会影响别人用网
  2. 游戏,实时性
  3. dns协议udp用在请求,回应的时候,采用tcp

UDP的并发怎么做

接收不同客户端的数据,只通过一个buffer接收,很难区分清楚是哪个客户端发的数据,可能产生脏数据。

在这里插入图片描述

用udp模拟tcp的方式

在客户端和服务器第一次发送数据的时候,服务器接收到数据以后为其分配一个新的fd以及一个对应的端口号,再从对应的端口号send出来。一个fd对应一个客户端。

  1. recvfrom(&addr)接受一个客户端的信息,拿出IP地址的端口
  2. 新建一个fd,调用sendto(fd,)发给对方的ip地址和端口号

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:技术工厂 设计师:CSDN官方博客 返回首页
评论

打赏作者

为成大道踏平坎坷

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值