最近在研究Lwip协议栈,对协议栈的整体理解记录一下:
想记录的原因源于下面的问题-->
Lwip怎么把数据包交给上层?
Tcp_input中调用tcp_receive中,如果发现了数据包,将recv_data指向数据包结构pbuf。之后再Tcp_input剩余的处理中,如果发现recv_data不为空,则调用TCP_EVENT_RECV通知应用层有数据包。这个宏对应着(pcb)->recv,也就是pcb的recv成员。因此需要用户把编写好的接收函数在connected的callback中设置好。同理,还有ACK响应,查询等callback函数,需要在connect后的callback函数中设置与pcb关联上。而connect的callback函数是在tcp_connect函数中设置的(如果是客户端)。如果是服务端,是在accept的回调函数中设置ACK响应,查询,接收等callback函数,以供协议栈使用。
综上所述,用户程序会先设置好pcb,然后在连接建立以后,回调建立的callback。在这里,设置好发送,接收函数。然后当协议栈收到或者发送这个pcb的包时,会调用这些函数,这些函数是用户写的,也就链接了用户的buffer数据到协议栈中。
tcp_recv()的回调函数:收数据包到应用层
把传入的地址的数据包取出来放到应用层buffer,并通知协议栈。
tcp_poll的回调函数:查看是否要发送数据(主动发送),由tcp_timer定时触发
需要发送的话会调用tcp_write+tcp_output,把数据压入协议栈发送
tcp_sent的回调函数:收到接收方确认ACK后发送数据
发送原理同上
在以上的例子中,应用层数据是直接与协议栈联系在一起的。而使用api_lib或者socket编程的话,需要调用相应接口,把上层数据,PCB信息打包成msg,与TcpIp thread交互。TcpIp thread再与协议栈交互,完成数据的收发。
最后说下网卡的初始化。需要在初始化时候指明网卡的输入,输出回调函数。这里的回调函数的目的,是让底层数据和协议栈联系起来。
现在看来,这个链路的数据,从上到下三层通过回调函数连接在了一起。
(1)底层收到一包数据,会一层层上报到应用层。
(2)应用层收到ACK,也会把剩下的未发送数据一层层发送出去。
(3)应用层想要发送一包数据,就设置好相应的标志。TCP定时器定期查询用户定义的poll函数,如果需要把数据发送出去。当然不设置标志,定时直接发送也是可以的。反正都是用户自己定义的。
(4)应用层想要发送一包数据,也可以直接调用tcp_write,tcp_output把数据发送出去。
(5)应用层收取数据包,一般使用查询方式而不是直接接收,因为上层是不知道什么时候回收到包的。
只有查询到底层收取到包的时候,才触发应用层接口接收。
对于使用API编程,就看下每个函数的注释,调用接口即可。不用分析连接的细节过程。这也是它的意义所在。