原创作品,允许转载,转载时请务必以超链接形式标明文章
原始出处 、作者信息和本声明。否则将追究法律责任。
http://bluefish.blog.51cto.com/214870/158416
2009-05-12
LWIP之TCP层接收相关
既然定了这么个标题,当然是要从
socket
的
recv
来讲了。这里主要涉及到
lwip_recvfrom
这个函数。它的大致过程是,先通过
netconn_recv(sock->conn);
从
netconn
的
recvmbox
中收取数据,在这里有个
do_recv
的调用,而
do_recv
又调用了
tcp_recved
,关于这个函数的注释如下:
* This function should be called by the application when it has
* processed the data. The purpose is to advertise a larger window
* when the data has been processed.
知道了,这里的
do_recv
只是起到一个知会的作用,可能的话会对接收条件做一些调整。
回到主题,
lwip_recvfrom
从
netconn
接收完数据就是要
copy the contents of the received buffer into the supplied memory pointer mem
,这一步是通过
netbuf_copy_partial
函数来完成的。
接着往下走,我们发现,数据的来源是在
netconn
的
recvmbox
,刚才提到过的。好了,那么是在什么地方,什么时候这个
recvmbox
被填充的呢?
在工程中搜索
recvmbox
,发现在
recv_tcp
函数有这样一句:
sys_mbox_trypost(conn->recvmbox, p)
ok
,就是它了。看看函数的原型:
* Receive callback function for TCP netconns.
* Posts the packet to conn->recvmbox, but doesn't delete it on errors.
static err_t recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
好的,
p
是个输入参数,并且这是个静态函数,是作为
tcp
的接收回调用的。
嗯接着看
recv_tcp
的
caller
:
* Setup a tcp_pcb with the correct callback function pointers
* and their arguments.
* @param conn the TCP netconn to setup
static void setup_tcp(struct netconn *conn)
{
struct tcp_pcb *pcb;
pcb = conn->pcb.tcp;
tcp_arg(pcb, conn);
tcp_recv(pcb, recv_tcp);
tcp_sent(pcb, sent_tcp);
tcp_poll(pcb, poll_tcp, 4);
tcp_err(pcb, err_tcp);
}
哈哈,可谓是一网打尽啊。对于这个函数中的几个调用,我们看一个就好了,别的实现也差不多,就是个赋值的过程
Void tcp_recv(struct tcp_pcb *pcb,
err_t (* recv)(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err))
{
pcb->recv = recv;
}
setup_tcp
上面也有讲过,是在
newconn
的时候被调用的,创建完
pcb
,就是调用的这个函数,以配置连接的各个回调函数。
然而到这里似乎走了一个死胡同里了,貌似没有什么地方对
pcb->recv
有调用的,而唯一有的就是接收
TCP
事件
TCP_EVENT_RECV
的宏定义中。同时,其他的几个函数也是类似的情况,例如
send_tcp
函数。
真是“山穷水尽疑无路,柳暗花明又一村”啊。原来上面的也不只是死胡同。这里就要从
tcp
的三大接收处理函数说起了。
最底层的(在
tcp
层)就是
tcp_input
,它是直接被
ip
层调用的,该函数的定义注释是这么写的:
* The initial input processing of TCP. It verifies the TCP header, demultiplexes
* the segment between the PCBs and passes it on to tcp_process(), which implements
* the TCP finite state machine. This function is called by the IP layer (in ip_input()).
Tcp_input
又调用了
tcp_process
函数做进一步的处理,它的定义注释如下:
* Implements the TCP state machine. Called by tcp_input. In some
* states tcp_receive() is called to receive data. The tcp_seg
* argument will be freed by the caller (tcp_input()) unless the
* recv_data pointer in the pcb is set.
是的,下面是
tcp_receive
函数,被
tcp_process
调用
* Called by tcp_process. Checks if the given segment is an ACK for outstanding
* data, and if so frees the memory of the buffered data. Next, is places the
* segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment
* is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until
* i it has been removed from the buffer.
然而光有这些调用顺序是不行的,最重要的是下面两个变量【都在
tcp_in.c
中】
static u8_t recv_flags;
static struct pbuf *recv_data;
在
tcp_receive
中有以下主要几句
if (inseg.p->tot_len > 0)
{
recv_data = inseg.p;
}
if (cseg->p->tot_len > 0)
{
/* Chain this pbuf onto the pbuf that we will pass to
the application. */
if (recv_data)
{
pbuf_cat(recv_data, cseg->p);
}
else
{
recv_data = cseg->p;
}
cseg->p = NULL;
}
下面的这个是
tcp_input
中的,是在
tcp_process
处理完之后的
if (recv_data != NULL)
{
if(flags & TCP_PSH)
{
recv_data->flags |= PBUF_FLAG_PUSH;
}
/* Notify application that data has been received. */
TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err);
}
看最后一条语句就好了,很熟悉是吧,对了,就是上面传说中的死胡同,到此也解开了。