posix api(TCP)与网络协议栈的联系

linux 下 posix api有哪些

服务端

1、socket
2、bind
3、listen
4、accept
5、recv
6、send
7、close

客户端

1、socket
2、bind(可有可无)
3、connect
4、send
5、recv
6、close

设置socket参数

setsocketopt
getsocketopt

socket是什么

  直译过来是插座,fd(文件描述符)是我们可以操作的,与之对应的有tcb(tcp control block),fd与tcb的生命周期相同,fd与tcb是一一对应的。创建出来的fd是从3开始的,0是stdin,1是stdout,2是stderr.

socket函数

  创建listenfd,用于监听接收或者连接

bind函数

  绑定本地的ip+prot,如果不确定ip是哪个的时候用0.0.0.0

五元组

remoteip/sourceip     远端(源)ip
remoteprot/sourceport   远端(源)端口
localip/destip       本地(目的)ip
localprot/destport     本地(目的)端口
proto          协议类型(TCP/UDP)

五元组与tcb与fd一一对应
一台服务器只有65525个端口,为什么能做到百万连接?
  因为五元组与端口复用,一个五元组对应一个tcb对应一个fd,五元组的不同可以区分出不同的tcb和fd,并且端口复用,让同一个端口可以被多个tcb/fd使用

五元组何时建立

客户端:
  调用connect时
服务端:
  收到第一次握手时

三次握手

  是在内核协议栈中发生的,posix api感受不到三次握手

图解

在这里插入图片描述
1、将服务端置于listen状态
2、客户端调用connect
3、客户端的内核协议栈发送第一次握手(syn和seqnum)
  服务端的内核协议栈接收到第一次握手,建立五元组与tcb,将tcb加入半连接队列,此时处于syn_recv状态
4、服务端的内核协议栈发送第二次握手(syn和acknum)
  客户端的内核协议栈收到后让connect返回(若fd创建为阻塞的话,如果是非阻塞的就在2中返回了)
5、客户端的内核协议栈发送第三次握手(acknum)
  服务端的内核协议栈接收到后,根据五元组去半连接队列中查找是否有对应的tcb,找到的话就放入到全连接队列,然后停止listen,accept取出全连接队列中的tcb并返回一个clientfd

listen函数

  将服务端置于监听状态,当服务器接收到第一次握手时,clientfd对应的tcb的状态置为syn_recv(此时的clientfd用户没有获得也无法操作),并将对应的五元组加入到半连接队列
  listen第二个参数是半连接队列和全连接队列之和(unix)或全连接队列(linux)的最大长度

accept函数

  三次握手结束后,从全连接队列中取出并返回一个clientfd,只有accept返回之后,我们才能得到了clientfd,才能操作对应的tcb。

数据传输

图解

在这里插入图片描述
1、本端调用send命令,将send的buffer拷贝到内核sendbuffer中,若sendbuffer满了,send返回-1。
2、本端内核协议栈将sendbuffer中的数据发送给对端内核中的recvbuff。
3、对端调用recv取出recvbuffer中的数据。

sendbuffer为什么会满

当对端recv不及时,导致recvbuffer满了,再导致sendbuffer发不出去,所以sendbuffer就满了

粘包

上述1、 2、 3阶段都为异步的,因此就可能出现一下情况:
  本端调用n次send,但是内核协议栈发送了m次sendbuffer(m<n),然后对端recv到m次,此时就会出现所谓的粘包的问题。因此需要分包(将接收到m次数据分成n份)

分包

分包的方式:
1、每次send的时候都加入一个唯一标志(如http的/r/n/r/n,可参考之前的文章
2、每次发送数据的时候,都在最前面定义一个长度,即数据格式为:buffer_size+buffer

TCP如何实现顺序发送

  之前提到的粘包问题和分包处理都是基于TCP顺序的前提下,若TCP发送数据不顺序,接收到的数据就会错乱,也无法进行分包处理。

顺序发送和传输可靠性

图解

在这里插入图片描述

TCP协议栈中有一个延迟ACK(定时器,可关闭)。
对端每次接受到一个包(recvbuffer)定时器重置,当定时器超时时,会返回ACK给本端。如上图中对端接收到1、2、4号包(不管接受顺序是怎么样),返回ack=3给本端,本端会重新发送3、4号包,当对端返回ack=5的时候说明对端接受到所有数据,再根据包号就能做到顺序接受。

udp使用场景

因为每次丢包都要重传,所以在网络较弱或者带宽小的情况下,使用udp会比较合适。
1、游戏,要求实时性

断开连接(四次挥手)

图解

在这里插入图片描述

1、本端调用close,此时本端将不能在send和recv(调用close的时候,会先将之前sendbuffer中的数据发完,再单独发一个空包),若本端与对端都调用close,则会进入closeing状态
2、本端协议栈将fin位置1,sendbuffer为空,大小为0
3、对端调用recv,返回0,对端用户处理相关业务
4、对端处理完业务之后,调用close
5、对端协议栈将fin位置1,sendbuffer为空,大小为0
4、本端协议栈接收到之后,返回ack,断开成功

time_wait存在的意义

若对端没有接收到第四次挥手的数据(即ack),则对端会进行重传,若本端发送ack就进入close状态的话,就没有办法接收到对端的重传要求和发送ack了

断开连接出现的问题

服务端有大量的close_wait状态怎么解决?
出现原因:
  从recv返回0到close中有大量耗时的操作。
解决方法:
1、close放在耗时操作前面
2、将耗时的业务放到任务队列中或者开一个线程池去处理

TCP状态转换图

在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值