首页 > 其他 > 详细

获取网络包到达网卡的时间

时间:2014-02-10 17:11:37      阅读:517      评论:0      收藏:0      [点我收藏+]
       为啥要获取数据包到达网卡的时间?
       在回答这个问题之前,我们先看一下网卡消息队列。如下图所示,对端发送的网络数据包被网卡设备接收到之后,会存放到网卡消息队列中,由应用程序调用recv系列函数从网卡队列中获取网络消息。
bubuko.com,布布扣

       现在假设:消息处理服务处理网络消息包的平均时间为1s,请求方等待回包的时间为1s,网卡消息队列的长度为10,在应用程序正常处理的情况下,所有的请求均能够在规定的时间内接收到回包。但是消息处理服务(即recv调用者)在处理某个数据包时发生异常,导致处理耗时3s,这时网卡消息队列中保存了2个数据包。此时消息处理服务恢复正常,再次从网卡消息队列中获取到的数据包已将是3s之前的请求包,而且请求方已经等待超时,继续处理这个请求已经没有任何的实际意义,正确的处理方式为:从网卡消息队列获取消息时,同时获取消息到达网卡的时间,检查消息是否超时,超时的消息直接丢弃或者记录日志等操作,然后继续处理消息队列中剩余的消息。

       下面介绍几种获取网络包到达网卡时间的方法:
       方法一:搭建接口机
       搭建接口机调用recv系列函数,从网卡消息队列中获取消息,打上时间戳再存放到消息队列中,由消息处理服务来从应用消息队列中取出消息进行处理。整体架构图如下:
bubuko.com,布布扣

       因为接口机的服务功能比较简单,从网卡中获取时间的时间,就可以当作网络包到达网卡的时间。
      其实,接口服务与逻辑服务的架构比较常见,但是采用这种方法来解决获取时间的问题,显然成本比较高,非常不合适。

      方法二:ioctl获取网卡时间
      ioctl可以根据socket句柄来查询这个句柄获取传递给用户的最后一个包到达网卡的时间,即获取最后一次调用recv系列函数获取到的数据包到达网卡的时间戳。现在存在一个场景,如何获取这个句柄接收到的每个数据的网卡时间?每次调用recv系列函数之后, 均需要再调用一次ioctl。
     在调用ioctl的过程中,第一次调用ioctl返回的是gettimeofday的时间,后续获取到的时间是正常的数据包到达网卡的时。
 示例代码:
Server代码(C):
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <string.h> 
#include <stdio.h> 
#include <stdint.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#define PORT 31500

int main(){ 
     int sockfd,len; 
     struct sockaddr_in addr; 
     int addr_len = sizeof(struct sockaddr_in); 
     char buffer[256]; 

     if((sockfd=socket(AF_INET,SOCK_DGRAM,0))<0){ 
          perror ("socket"); 
          exit(1); 
     } 

     bzero ( &addr, sizeof(addr) ); 
     addr.sin_family=AF_INET; 
     addr.sin_port=htons(PORT); 
     addr.sin_addr.s_addr=htonl(INADDR_ANY) ; 
     if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr))<0){ 
          perror("connect"); 
          exit(1); 
     } 
     struct timeval tv, tvNow,tvRes;
     while(1){ 
          bzero(buffer,sizeof(buffer)); 
          len = recvfrom(sockfd,buffer,sizeof(buffer), 0 , (struct sockaddr *)&addr ,(socklen_t*)&addr_len); 
          printf("receive from %s\n" , inet_ntoa( addr.sin_addr)); 
          int iRet = ioctl(sockfd, SIOCGSTAMP, &tv);
          gettimeofday(&tvNow, NULL);
          uint64_t ddwNow = tvNow.tv_sec*1000000 + tvNow.tv_usec;
          uint64_t ddwTv = tv.tv_sec*1000000 + tv.tv_usec;

          printf("Now:%lu Tv:%lu dff:%lu\n",ddwNow,ddwTv,(ddwNow - ddwTv)/1000000);
          sleep(10);
     } 
     return 0;
}  


Client代码(Python):
import socket
address = (‘127.0.0.1‘,31500)
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while True:
     msg = raw_input()
     if not msg:
          break
     s.sendto(msg,address)

s.close()



使用该策略的缺点在于:为获取到时间戳需要产生额外的一次系统调用:ioctl,降低了系统的性能。方法三相对更加高效简单,在获取到网络数据包的同时获取到网络数据包到达网卡的时间。


方法三:recvmsg获取网卡时间
使用recvmsg函数,调整所需要获取的控制消息,即可获取到网络包到达网卡的时间戳
Server代码(C):
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#define SERVPORT 31500

int main(int argc, char **argv) 
{ 
     int sockfd; 
     struct sockaddr_in srvAddr; 
     sockfd = socket(AF_INET ,SOCK_DGRAM,0 ); 
     if(sockfd< 0 ) 
     { 
          printf("socket error\n"); 
          return 0; 
     }    

     bzero(&srvAddr, sizeof(srvAddr) ); 
     srvAddr.sin_family = AF_INET; 
     srvAddr.sin_port   = htons(SERVPORT); 
     srvAddr.sin_addr.s_addr = htonl(INADDR_ANY); 

     bind(sockfd, (struct sockaddr*)&srvAddr,sizeof(srvAddr)); 

     while(1) 
     {    
          struct msghdr msg; 

          struct sockaddr_in clientAddr; 
          msg.msg_name = &clientAddr; 
          msg.msg_namelen= sizeof(clientAddr); 

          struct iovec iov; 
          msg.msg_iov = &iov; 
          msg.msg_iovlen =1; 
          char text[1024] ; 
          iov.iov_base= text; 
          iov.iov_len = sizeof(text); 

          char ctrl[CMSG_SPACE(sizeof(struct timeval))]; 
          struct cmsghdr *cmsg=(struct cmsghdr*)&ctrl; 
          msg.msg_control = (caddr_t)ctrl; 
          msg.msg_controllen = sizeof(ctrl); 

          int rc = recvmsg(sockfd,&msg,0); 
          if(rc== -1) 
          { 
               printf("recvmsg error\n"); 
               return 0; 
          } 

          struct timeval tv, tvNow,tvRes;

          if(cmsg->cmsg_level ==SOL_SOCKET&& 
                    cmsg->cmsg_type  ==SCM_TIMESTAMP && 
                    cmsg->cmsg_len   ==CMSG_LEN(sizeof(tv)) 
            ) 
          memcpy(&tv,CMSG_DATA(cmsg),sizeof(tv)); 
          gettimeofday(&tvNow, NULL);
          uint64_t ddwNow = tvNow.tv_sec*1000000 + tvNow.tv_usec;
          uint64_t ddwTv = tv.tv_sec*1000000 + tv.tv_usec;

          printf("Now:%lu Tv:%lu dff:%lu\n",ddwNow,ddwTv,(ddwNow - ddwTv)/1000000);
          sleep(10);
     } 
}  


获取网络包到达网卡的时间

原文:http://blog.csdn.net/jiang1013nan/article/details/19041225

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!