• 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){…}中的循环体!

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

  • 相关阅读:
    查询SystemFeature的方法
    【HTML5游戏开发小技巧】RPG情景对话中,令文本逐字输出
    BFS寻路的AS3实现
    超级坑人的Couchbase数据库问题!!!
    java--函数练习
    CentOS 6.2 二进制安装apache2.4.3出现configure: error: APR-util not found. Please read the documentation的解决方
    2017第27周六努力与积累
    2017第27周五
    丢掉生活中的90%,你会收获更多
    《时间简史》笔记摘录
  • 原文地址:https://www.cnblogs.com/helloweworld/p/2699814.html
Copyright © 2020-2023  润新知