分类: 嵌入式
2017-02-06 15:00:37
tcp的运行过程是怎样的?用户如何使用它?
tcp协议栈移植完成后,就像一个不停运转的摩天轮。用户要使用它,只需在它的每个舱体内装上游客即可。
每个舱体相当于一个pcb。新建一个pcb就相当于新建一个舱体。让男女游客进仓,相当于为tcp->recv,tcp->poll等注册对应的回调函数。
下面讨论一个pcb从创建到销毁,在它身上发生了什么?
tcp_input(),负责查询pcb所属的状态链表,并分流
tcp_input(struct pbuf *p, struct netif *inp)详解。
1) 参数p:指向ip报文头;参数inp:接收的网口
2) 处理流程
a. 数据包有效性检测,丢弃过短的数据包(ip报文、tcp段都有最小的长度);丢弃广播、多播包;丢弃校验出错的数据包。
b. 遍历tcp_active_pcbs链表,检查是否包含接收段所对应的pcb。若包含,继续后续处理。
c. 遍历tcp_tw_pcbs链表,检查是否包含接收段所对应的pcb。若包含,则调用tcp_timewait_input()重发ACK。后退出。
d. 遍历tcp_listen_pcbs链表,检查是否包含接收段所对应的pcb。若包含,则调用tcp_listen_input()。先检查下flag,看是否是连接请求(SYN置位)。若是,创建一个新的pcb,设置相关参数,然后将其放入tcp_active_pcbs链表。
另外原有的tcp_listen_pcbs链表仍有lpcb。服务器时刻处于侦听状态,以接收来自多个ip的连接请求。后退出。
e. 若是处于tcp_bound_pcbs、tcp_active_pcbs链表的pcb收到了数据,则会调用tcp_process()进一步处理接收的数据。
f. 检查该pcb接收段的recv_flags(在tcp_process()中设置,根据pcb的当前状态和接收tcp段头的相关标志进行)。
TF_RESET: 收到的tcp段头TCP_RST被置位。该连接被远端强行断开(远端调用tcp_abort())。所以此时也只能将该pcb从tcp_active_pcbs链表中删除,并释放该pcb相关资源。
TF_CLOSED: pcb处于LAST_ACK状态,并收到ack确认时。说明连接正常断开,将该pcb从tcp_active_pcbs链表中删除,并释放该pcb相关资源。
TF_GOT_FIN: 收到的tcp段头TCP_FIN被置位。即收到远端断开的请求。在e步骤的tcp_process()中,已经立即返了一个ACK。此处调用TCP_EVENT_RECV()以通知应用层收到了FIN请求,要进行断开连接处理。
(通知应用层时,数据为NULL,即表示收到FIN请求。
tcp_process()处理client和server的pcb的tcp状态机的迁移。
tcp_receive()处理接收报文中相关字段,主要做两件事:
(1)a. 根据ackno确定是否重传,更新拥塞窗口。
b. 分析ackno与发送窗口的关系,更新unacked、unsent链表。
(2)a. 根据seqno+len与接收窗口的关系,看接收报文中哪些数据该保留,哪些该丢弃。
b. 该保留的先放入乱序链表ooseq,看能否凑成更大的连续段;然后将连续段放入recv_data中,等待应用程序处理。
tcp_out主要有两个函数:
1. tcp_enqueue():将应用层的整个数据包拆分成段。按序放入pcb->unsent队列。
2. tcp_out() :从pcb->unsent队列取出要发送的段,调用tcp_out_segment()将特定的段发送出去。