1. 程式人生 > >獲取網路包到達網絡卡的時間

獲取網路包到達網絡卡的時間

       為啥要獲取資料包到達網絡卡的時間?        在回答這個問題之前,我們先看一下網絡卡訊息佇列。如下圖所示,對端傳送的網路資料包被網絡卡裝置接收到之後,會存放到網絡卡訊息佇列中,由應用程式呼叫recv系列函式從網絡卡佇列中獲取網路訊息。
       現在假設:訊息處理服務處理網路訊息包的平均時間為1s,請求方等待回包的時間為1s,網絡卡訊息佇列的長度為10,在應用程式正常處理的情況下,所有的請求均能夠在規定的時間內接收到回包。但是訊息處理服務(即recv呼叫者)在處理某個資料包時發生異常,導致處理耗時3s,這時網絡卡訊息佇列中儲存了2個數據包。此時訊息處理服務恢復正常,再次從網絡卡訊息佇列中獲取到的資料包已將是3s之前的請求包,而且請求方已經等待超時,繼續處理這個請求已經沒有任何的實際意義,正確的處理方式為:從網絡卡訊息佇列獲取訊息時,同時獲取訊息到達網絡卡的時間,檢查訊息是否超時,超時的訊息直接丟棄或者記錄日誌等操作,然後繼續處理訊息佇列中剩餘的訊息。        下面介紹幾種獲取網路包到達網絡卡時間的方法:        方法一:搭建介面機        搭建介面機呼叫recv系列函式,從網絡卡訊息佇列中獲取訊息,打上時間戳再存放到訊息佇列中,由訊息處理服務來從應用訊息佇列中取出訊息進行處理。整體架構圖如下:

       因為介面機的服務功能比較簡單,從網絡卡中獲取時間的時間,就可以當作網路包到達網絡卡的時間。       其實,介面服務與邏輯服務的架構比較常見,但是採用這種方法來解決獲取時間的問題,顯然成本比較高,非常不合適。       方法二: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);
     } 
}  


相關推薦

獲取網路到達時間

       為啥要獲取資料包到達網絡卡的時間?        在回答這個問題之前,我們先看一下網絡卡訊息佇列。如下圖所示,對端傳送的網路資料包被網絡卡裝置接收到之後,會存放到網絡卡訊息佇列中,由應用程式呼叫recv系列函式從網絡卡佇列中獲取網路訊息。        

網路資料分析 Offload

TCP減壓引擎,第一次聽說這個名詞,但是並不是一個新的概念了,若干年前聽說過裝置廠商在研究在FPGA之中實現TCP Stack,但是後來沒有聽到任何的產品出來,應該是路由裝置to host的traffic不多,而對於FW裝置,中間的TCP Proxy實現過於複雜,工程上

【Ubuntu14.04】網路模式與配置

VMware虛擬機器軟體為什麼提供有三種網路模式(橋接模式、NAT模式、僅主機模式),本章節會分別介紹著三種模式的區別,如何在Ubuntu14.04環境下設定網路連線模式與配置網絡卡資訊的方法。     關於三種網路模式   橋接模式:讓虛擬機器與物

網路簡介:跟路由器是如何讓你上網的——《碼農翻身》閱讀筆記

聯網是這樣的,首先,你的電腦上必須要有網絡卡,否則上不了網。這章是動態撥號上網 每個網絡卡都有一個固定的MAC(Media Access Control)地址。也稱為實體地址。MAC地址由產商決定,就像身份證號碼,一般不可更改。如:11:27:F5:8A:79:54   可

linux 獲取eth0(指定的IP地址(或MAC地址)

引言 使用場景: 我使用的Linux伺服器上有多個網絡卡,有時候就需要在指令碼中使用到指定網絡卡的IP地址或MAC地址,那麼這個時候就有兩種做法,一是預先讀取網絡卡的IP地址或MAC地址,然後寫入到指令碼中;另外一種方法就是通過shell命令實時獲取指定網絡卡上的當前

黑蘋果,Mac OS X如何使用USB資料線連線手機網路(沒有驅動時)

安裝黑蘋果之後,發現網絡卡驅動都不能使用,所以就想用手機幫助電腦連線網路; 開始用藍芽開始連線,因為我的黑蘋果可以使用藍芽,但手機連線之後總是間斷斷開。 所以就開始打算使用資料線使用手機的USB分享網路來使黑蘋果連線網路,非常簡單,下載一個基於Mac OS X 的USB t

debian下配置網路 安裝無線驅動 Broadcom BCMXX系列

解決方式來自於debian官網  1、新增源 deb http://http.debian.net/debian/ wheezy main contrib non-free deb http://http.debian.net/debian/ squeeze main

Python獲取本機所有的MAC地址

在拙作《Python可以這樣學》(清華大學出版社,2017.2)第297頁介紹了一種獲取本機網絡卡MAC地址的方法,不過程式碼顯得稍微有點囉嗦,並且只能獲得一塊網絡卡的MAC地址。本文對該內容稍加補充。 1、這樣更簡潔 >>> import uuid

c# 多 由【網路介面卡名】獲取資訊,IP

c# 多網絡卡 由【網路介面卡名】獲取網絡卡資訊,IP 多網絡卡電腦中,網路介面卡的名字 多樣化! 專案中需要,根據網路介面卡 名字 獲取 單個網絡卡的IP: using System.Net.NetworkInformation;

VC++獲取本機網路流量

使用GetAdaptersInfo 和 GetIfTable獲取本機網絡卡資訊以及流量等資訊。 #include "stdafx.h" #include <WinSock2.h> #include <Iphlpapi.h>

unix網路程式設計之根據主機名(hostname)或獲取IP地址(三種方法)

#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <netinet/

使用 混雜模式 編寫網路程式

都說在winsock2中支援抓包功能了,在網上找了個例子,修改一下,在windows xp + VC2003 下測試通過。不知道能不能做成QQ訊息嗅探工具,試驗中   #pragma comment(lib,"ws2_32.lib")        // sokect 2 # include "wi

linux 設定網路API --- 開/關、設定/獲取Mac地址

最近在做專案過程中,由於在多執行緒中使用system函式,有時候出現莫名程式異常終止,最後決定替換所有的system函式,但是對於設定mac地址這個函式試了很多次都沒有成功;今天在此總結下原因: 1. 編寫的關閉/開啟網絡卡函式沒有延時;(一般情況設定開關網絡卡可能需要初始

winPcap獲取網路地址和子掩碼

下面是獲取網路地址(不是IP地址)和子網掩碼的示例,沒時間接著往下做例子了,因為接下來需要在LINUX下面使用libPcap,當然我會貼出程式碼,會linux程式設計的大牛一般都會,所以準確的說是貼給自己的,喜歡玩資料包的朋友自己看官方例子就行 #include<pc

linux c/c++按規則獲取ip

linux c/c++按規則獲取網絡卡ip 輸出專案到雲或者輸出給外部客戶,會遇到伺服器多網絡卡多ip的情形,如果有多個應用都需要這個主機ip,而且多應用需要獲取相同的ip,此時可以約定一種規則來獲取相同的ip,比如: 獲得所有網絡卡名,然後對網絡卡名按從小到大排序,查詢最小

圖解幾個與Linux網路虛擬化相關的虛擬-VETH/MACVLAN/MACVTAP/IPVLAN

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Debian-Linux配置網路方法

Debian不同於centos系統,網絡卡配置不是在/etc/sysconfig/network-scrip裡面,而是在/etc/network/interfaces裡面 1.Debian網路配置 配置網絡卡:修改vi /etc/network/interfaces 新增如下 auto eth

Linux中高階網路配置-team方式多繫結

team簡介 在redhat7.0以上版本,配置多網絡卡繫結時,專門提供了team工具來實現多網絡卡的繫結。 team :也是鏈路聚合 最多支援8塊網絡卡,不需要手動載入相應核心模組 支援模式: broadcast 廣播容錯 roundrobin 輪詢

Linux中高階網路配置-bond方式雙繫結

bond簡介 bond可以兩塊網絡卡虛擬成為一塊網絡卡,在網絡卡繫結後,看起來是一個單獨的乙太網介面裝置並具有相同的 IP 地址。既能新增網路頻寬,同時又能做相應的冗餘,輪詢和負載均衡。 bond 網絡卡的工作模式 網絡卡 bond 後的工作模式有兩種:主備的工作方式和負載

獲取指定對應的IP地址

1 #include <stdio.h> 2 #include <string.h> 3 #include <sys/socket.h> 4 #include <sys/types.h> 5 #include <sys/ioctl.h>