vtun 虚拟网卡的读写非阻塞研究
在文件linkfd.c文件中,有从虚拟网卡读出数据然后发送,将接收到的数据写入网卡过程。
注意,在client和server端,上面的两个过程都有,意思可以说是两端对等,看下图。
下面分析对虚拟网卡的读写非阻塞问题,在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){…}中的循环体!
那么下面就考虑信号处理函数在干嘛,下一篇继续介绍信号处理函数。