LWIP之TCP层接收相关 tcp_recv的实现(转)

2019-07-14 10:17发布

标签: 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://bluefish.blog.51cto.com/214870/158416 既然定了这么个标题,当然是要从socketrecv来讲了。这里主要涉及到lwip_recvfrom这个函数。它的大致过程是,先通过netconn_recv(sock->conn);netconnrecvmbox中收取数据,在这里有个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_recvfromnetconn接收完数据就是要copy the contents of the received buffer into the supplied memory pointer mem,这一步是通过netbuf_copy_partial函数来完成的。               接着往下走,我们发现,数据的来源是在netconnrecvmbox,刚才提到过的。好了,那么是在什么地方,什么时候这个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_tcpcaller * 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); } 看最后一条语句就好了,很熟悉是吧,对了,就是上面传说中的死胡同,到此也解开了。   本文出自 “bluefish” 博客,请务必保留此出处http://bluefish.blog.51cto.com/214870/158416