我是靠谱客的博主 真实小甜瓜,最近开发中收集的这篇文章主要介绍关于LWIP的一点记录(四),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

TCP编程本质是操作tcp_pcb,每个TCP连接对应一个tcp_pcb,
每个tcp_pcb维护3个链(缓冲队列):unset,unacked,ooseq ———— 记录了本连接的所有数据
TCP缓冲:
struct tcp_seg{

struct tcp_seg * next;	//单链
struct pbuf * p;	//数据链
len,flags;		//长度,选项
* tcphdr;		//TCP首部

}

unset,unacked,ooseq就是三个tcp_seg链
unsent链上的tcp_seg试图将缓冲数据组织成最佳发送长度(TCP_MSS),
————so,应该是每个pbuf加入缓冲的时候,看下当前unsent链尾的tcp_seg够不够TCP_MSS,
如果不够就继续挂在这个tcp_seg上,如果够了就新开一个tcp_seg
tcp_write负责申请tcp_seg和pbuf,将待发送数据按照每个tcp_seg TCP_MSS大小组织,设置首部字段,并挂接到unsent链上,
然后调整snd_lbb,snd_buf,snd_queuelen,
tcp_write只负责组织,不管发送 ———— 内核会定期发送,也可以使用tcp_output手动发送
———— 看描述是要正好填够TCP_MSS,所以用的pbuf是固定大小?还是说完整的数据可以被切到俩tcp_seg中?

tcp_output先检查是否符合发送条件,判断一下别跟tcp_output冲突,if pcb->flags中设置了TF_ACK_NOW,则需要赶紧发一个ACK,
这时if unsent是空的?或者发送窗口不够了,就先发一个空ACK,否则可以跟在正常报文中捎带确认,
其次是正常数据,前面tcp_write填装了一个个TCP_MSS大小的报文段(一个tcp_seg管理一个报文段),
此时tcp_output就可以在有空闲发送窗口的情况下调用tcp_output_segment(填写字段、校验和、启动重传定时器等)将报文段一个个发送,直到空闲窗口被用完
每个发送出去的报文段会按照序号大小加入unacked链

SYN攻击:同时向服务器发送大量SYN请求,服务器为每个请求(连接)申请空间,返回ACK+SYN,但无法获得回复,占用资源

TCP状态转换:

tcp_process是TCP的状态机函数
目前看来,状态机似乎不用处理TIME_WAIT?

tcp_receive:

  1. 用新的窗口通告、seqno,ackno同snd_wnd,snd_wl1,snd_wl2比较,看是否更新窗口
  2. lastack与ackno比较,if相同,则说明报文丢失,可执行快重传或者快恢复(包括计算RTT)
  3. 看确认号能否释放unacked空间,联合ooseq链看能否组出有序数据放入recv_data,无序则加入ooseq
  4. 对于需要重传的,会被挂到unsent上,此时可将unsent上序号小于ackno的都删掉(应该是之前重传的还没来得及发)

超时重传与RTT:
当rtime超过rto时,unacked队列中的所有报文段都会被重传(插到unsent前端),
if重发的报文超时,则开始指数退避(这里直接让rto每次翻倍直到上限),超限后停止发送,删除pcb

慢启动与拥塞避免是为了调节发送流量(超时是触发条件),与重传机制无关
而快启动与快恢复是3个重复ack后不等rtime溢出,直接将丢的这一个放到unsent中
快重传与快恢复侧重于解决偶尔丢包的问题,而前者是为了限流

糊涂窗口:小窗口通告和小报文段发送导致带宽浪费(头多数据少)
———— so,LWIP的tcp_seg会组织满长度(TCP_MSS)的报文段(unacked非空的情况下),而接收方使用推迟确认

所谓的nagle算法,貌似是判断一些条件成立时不发送而是缓冲起来去拼接最大长度(TCP_MSS)
而平时禁止nagle,是说定时发送,能拼多少是多少?

零窗口探查:发送方周期性查询窗口大小(通过发送unacked或unsent中的一字节数据),避免先收到0窗口,而之后的非0窗口丢失而死锁

保活机制:服务器在长时间没和client通信后(2h),会发送探查报文(只有TCP首部),看一下对面挂了没,根据结果保活或终止连接

7个定时器(其实只是变量和宏):

  1. 建立连接:server响应SYN后等75s,没响应就终止
  2. 重传
  3. 数据组装:用于清理ooseq
  4. 坚持:0窗口探查
  5. 保活
  6. FIN_WAIT2:超时后关闭连接(因为对方已经知道你关了,而LWIP不支持半打开)
  7. TIME_WAIT:(2MSL)超时关、删、重用,另外LAST_ACK也用这个超时关闭,而不必非等ACK
    每个定时器是通过比较当前tcp_ticks与本pcb的tmr来判断的,连接切换状态时会记录pcb->tmr = tcp_ticks,
    so定时器可以通过当前tcp_ticks - pcb->tmr知道pcb处于某种状态的时间
    tcp_tmr负责周期调用tcp_slowtmr(500MS)和tcp_fasttmr(250MS)
    定时器结构:

struct sys_timeo{

sys_timeo * next;//单链
u32_t time;		//表示当处于链表头结点时还需等待的时间
sys_timeout_handler h,void * arg;//超时回调和参数

}
sys_timeout负责插入定时事件到链表,注册回调等,通过超时时间在链中找到位置插入,注意需要修改后面那一位的等待时间,
另:在回调中重新使用sys_timeout注册就是循环定时了

使用OS的情况下:
网络接口收到数据包后会回调注册的tcpip_input,构造消息,投递邮箱
收数据包可使用中断或查询线程
之前的RAW_API中是收到数据后调ethernet_input(向上逐层递交),而使用OS之后,调tcpip_input投递邮箱,内核线程从邮箱取到之后再调ethernet_input,
多了中间的邮箱,慢了一点,好处是中断处理缩短了,网卡这边接收能力提高

用户进程使用的netbuf只是对内核pbuf的简单封装(共享了),so避免了数据copy

一个栗子:调用netconn_bind,则会发个TCPIP_MSG_API类型的消息,阻塞在邮箱上(信号量),
内核拿到这个消息,会调用do_bind ——> tcp_bind … 执行完后释放信号量,netconn_bind继续执行

最后

以上就是真实小甜瓜为你收集整理的关于LWIP的一点记录(四)的全部内容,希望文章能够帮你解决关于LWIP的一点记录(四)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(46)

评论列表共有 0 条评论

立即
投稿
返回
顶部