vtun 虚拟网卡的读写非阻塞研究

在文件linkfd.c文件中,有从虚拟网卡读出数据然后发送,将接收到的数据写入网卡过程。

注意,在client和server端,上面的两个过程都有,意思可以说是两端对等,看下图。

image

下面分析对虚拟网卡的读写非阻塞问题,在linkfd.c的lfd_linker函数中,(client和server都是用该函数完成对虚拟网卡的读写)。

主要是下面代码:

while( !linker_term )//while循环内做了两件事:从虚拟网卡读数据后发送;将接收的数据写入虚拟网卡。
    {
        errno = 0;

        /* Wait for data */
        FD_ZERO(&fdset);
        FD_SET(fd1, &fdset);
        FD_SET(fd2, &fdset);
        /* 非阻塞超时时间 */
        tv.tv_sec  = lfd_host->ka_interval;
        tv.tv_usec = 0;
        /* select非阻塞监控 */
        if( (len = select(maxfd, &fdset, NULL, NULL, &tv)) < 0 )
        {
           if( errno != EAGAIN && errno != EINTR )
              break;
           else
              continue;
        }
        /* 重连用的和信号处理函数等有关 */
        if( ka_need_verify )        //ka_need_verify和信号处理函数有关
        {
            if( idle > lfd_host->ka_maxfail )
            {
                vtun_syslog(LOG_INFO,"Session %s network timeout", lfd_host->host);
                break;
            }
            if (idle++ > 0)
            {  /* No input frames, check connection with ECHO */
                /* 这里应该是连接失败后,发送请求帧检查连接的 */
                if( proto_write(fd1, uf, VTUN_ECHO_REQ) < 0 )
                {
                    vtun_syslog(LOG_ERR,"Failed to send ECHO_REQ");
                    break;
                }
            }
            ka_need_verify = 0;
        }
        /* 这段代码可忽略,加密用的,而且在linkfd.c中定义来该send_a_packet默认为0.
         * 但注意还有个全局变量send_a_packet在linkfd.h中定义
         */
        if (send_a_packet)
        {
           send_a_packet = 0;
           tmplen = 1;
           lfd_host->stat.byte_out += tmplen;
           if( (tmplen=lfd_run_down(tmplen,buf,&out)) == -1 )
               break;
           if( tmplen && proto_write(fd1, out, tmplen) < 0 )
               break;
           lfd_host->stat.comp_out += tmplen;
        }
        /* Read frames from network(fd1), decode and pass them to the local device (fd2) */
        /* 将接收到的数据写入虚拟网卡 */
        if( FD_ISSET(fd1, &fdset) && lfd_check_up() )//FD_ISSET网络中是否有数据到达。
        {
            idle = 0;  ka_need_verify = 0;
            if( (len=proto_read(fd1, buf)) <= 0 ) //接收网络中数据村到buf
                break;

           /* Handle frame flags */
            /* 处理帧标志 */
            /* fl即frame flags帧标志,该帧标志说明接收到的数据是何种数据——请求、应答、坏帧、关闭 */
           fl = len & ~VTUN_FSIZE_MASK;
           len = len & VTUN_FSIZE_MASK;
           if( fl )
           {
              if( fl==VTUN_BAD_FRAME )
              {
                  vtun_syslog(LOG_ERR, "Received bad frame");
                  continue;
              }
              if( fl==VTUN_ECHO_REQ )
              {
                  /* Send ECHO reply */
                  if( proto_write(fd1, buf, VTUN_ECHO_REP) < 0 )
                      break;
                  continue;
              }
              if( fl==VTUN_ECHO_REP )
              {
                  /* Just ignore ECHO reply, ka_need_verify==0 already */
                  continue;
              }
              if( fl==VTUN_CONN_CLOSE )
              {
                  vtun_syslog(LOG_INFO,"Connection closed by other side");
                  break;
              }
           }//end if(fl)

           lfd_host->stat.comp_in += len;
           /*lfd_run_up解密用的*/
           if( (len=lfd_run_up(len,buf,&out)) == -1 )
               break;
           /* 将数据写入虚拟网卡 */
           if( len && dev_write(fd2,out,len) < 0 )
           {
               if( errno != EAGAIN && errno != EINTR )
                   break;
               else
                   continue;
           }
           lfd_host->stat.byte_in += len;
        }

        /* Read data from the local device(fd2), encode and pass it to the network (fd1) */
        /* 将虚拟网卡中的数据加密后发送 */
        if( FD_ISSET(fd2, &fdset) && lfd_check_down() )
        {
           if( (len = dev_read(fd2, buf, VTUN_FRAME_SIZE)) < 0 )
           {
              if( errno != EAGAIN && errno != EINTR )
                 break;
              else
                  continue;
           }
           if( !len ) break;
           lfd_host->stat.byte_out += len;
           if( (len=lfd_run_down(len,buf,&out)) == -1 )
               break;
           if( len && proto_write(fd1, out, len) < 0 )
               break;
           lfd_host->stat.comp_out += len;
        }
    }//end while
    if( !linker_term && errno )
        vtun_syslog(LOG_INFO,"%s (%d)", strerror(errno), errno);

    if (linker_term == VTUN_SIG_TERM)
    {
        lfd_host->persist = 0;
    }

    /* Notify other end about our close */
    proto_write(fd1, buf, VTUN_CONN_CLOSE);
    lfd_free(buf);

    return 0;
}//end lfd_linker

看黑体字部分,就是利用select以及FD_ISSET进行非阻塞操作的。

linker_term变量在上面代码中始终为0,

但是linker_term在信号处理函数中被改变了,

也就是说不考虑信号处理函数,

程序最后一直在执行的操作就是while(!linker_term){…}中的循环体!

那么下面就考虑信号处理函数在干嘛,下一篇继续介绍信号处理函数。

原文地址:https://www.cnblogs.com/helloweworld/p/2699814.html