最近由于做毕业设计需要进行网络连接时延参数的测量,自己也搜了一下,没有找到太多想要的内容,幸而最后还是自己研究出来了,特在此分享一下:
在这里我测量DNS查询时延和TCP连接时延用的是winpcap,它是一个基于Win32平台的,用于捕获网络数据包并进行分析的开源库,如果没有学过,可以点击这里看一下教程,用个一两天就能学会。
网络编程的第一步当然是建立结构体
//ipv4首部
typedef struct ip_header
{
u_char ver_ihl; //版本+首部长度共8bit
u_char tos; //服务类型
u_short tlen; //总长
u_short identification; //标识
u_short flags_fo; //标志位3bit,段偏移量13bit
u_char ttl; //存活时间
u_char proto; //协议
u_short crc; //首部校验和
ip_address saddr; //源地址
ip_address daddr; //目标地址
u_int op_pad; //选项与填充
}ip_header;
//udp首部
typedef struct udp_header
{
u_short sport; //源端口
u_short dport; //目标端口
u_short len; //udp数据包长度
u_short crc; //校验和
}udp_header;
//dns首部
typedef struct dns_header
{
u_short id; //标识
u_short flag; //标志
}dns_header;
//定义TCP首部
typedef struct tcp_header
{
u_short sport; //16位源端口
u_short dport; //16位目的端口
unsigned int seq; //32位序列号
unsigned int ack; //32位确认号
unsigned char lenres; //4位首部长度/6位保留字
unsigned char flag; //8位标志位
u_short win; //16位窗口大小
u_short sum; //16位校验和
u_short urp; //16位紧急数据偏移量
}tcp_header;
//版本+首部长度共8bit
u_char tos; //服务类型
u_short tlen; //总长
u_short identification; //标识
u_short flags_fo; //标志位3bit,段偏移量13bit
u_char ttl; //存活时间
u_char proto; //协议
u_short crc; //首部校验和
ip_address saddr; //源地址
ip_address daddr; //目标地址
u_int op_pad; //选项与填充
}ip_header;
//udp首部
typedef struct udp_header
{
u_short sport; //源端口
u_short dport; //目标端口
u_short len; //udp数据包长度
u_short crc; //校验和
}udp_header;
//dns首部
typedef struct dns_header
{
u_short id; //标识
u_short flag; //标志
}dns_header;
//定义TCP首部
typedef struct tcp_header
{
u_short sport; //16位源端口
u_short dport; //16位目的端口
unsigned int seq; //32位序列号
unsigned int ack; //32位确认号
unsigned char lenres; //4位首部长度/6位保留字
unsigned char flag; //8位标志位
u_short win; //16位窗口大小
u_short sum; //16位校验和
u_short urp; //16位紧急数据偏移量
}tcp_header;
总共四个结构体,但是要说明的是,这里的DNS首部是经过简化的,实际的DNS协议报文结构如下:
因为DNS 查询时延是指从“终端向DNS服务器发起查询请求”到“终端收到DNS 服务器成功响应”之间的时间间隔。DNS传输层采用UDP 协议,知名端口号为53。从DNS 协议头的标志(flags)字段中的QR 字段,可以判断一个DNS 消息的类型(是查询还是响应等),而一对DNS 请求与响应消息有相同的标志(Transaction ID),我们通过Transaction ID 就可以确定哪个响应消息是与哪个请求消息对应的,那么对应的两条消息之间的间隔自然就是DNS查询时延了,所以只取标识和标志两个作为首部内容就可以了。
然后就是主要代码了
struct tm *ltime;
char timestr[16];
time_t local_tv_sec;
time_t local_tv_usec
ip_header *ih;
udp_header *uh;
dns_header *dnsh;
tcp_header *th;
u_int ip_len;
u_int tcp_header_len;
u_int tcp_body_len;
time_t time_dnsr[10]; //dns请求
time_t time_dnsq[10]; //dns回应
memset(time_dnsr,0,sizeof(time_dnsr));
memset(time_dnsq,0,sizeof(time_dnsq));
time_t time_tcp_1=0;
time_t time_tcp_2=0;
time_t time_tcp_3=0;
while((res = pcap_next_ex( adhandle, &header, &pkt_data)) >= 0)
{
if(res == 0)
/* Timeout elapsed */
continue;
//将时间戳转换为可识别的格式
local_tv_sec=header->ts.tv_sec;
local_tv_usec=header->ts.tv_usec;
//获取ip数据包头部的位置
ih=(ip_header*)(pkt_data+14); //都是按字节算的
//获取udp首部的位置
ip_len=(ih->ver_ihl & 0xf)*4;
if(ih->proto==0x11)
{
uh=(udp_header*)((u_char*)ih+ip_len);
//获取dns首部的位置
dnsh=(dns_header*)((u_char*)uh+8);
if(dnsh->flag==0x8081)
{
time_dnsr[i]=local_tv_sec*1000000+local_tv_usec; //单位是微妙
if(time_dnsq[i]!=0 && time_dnsr[i]!=0)
{
dns_delay=time_dnsr[i]-time_dnsq[i];
printf("\nDNS查询时延:%d ms \n",dns_delay);
memset(time_dnsr,0,sizeof(time_dnsr));
memset(time_dnsq,0,sizeof(time_dnsq));
}
i++;
}
else
{
time_dnsq[j]=local_tv_sec*1000000+local_tv_usec;
j++;
}
}
else if(dns_delay)
{
th=(tcp_header*)((u_char*)ih+ip_len); //unsigned char占一个字节
//tcp数据包头部长度
tcp_header_len=(((th->lenres<<4)|(th->lenres>>4)) & 0xf)*4;
if(th->flag==0x02)
{
time_tcp_1=(local_tv_sec*1000000+local_tv_usec);
}
if(th->flag==0x12 && time_tcp_1!=0)
{
time_tcp_2=(local_tv_sec*1000000+local_tv_usec);
}
if(th->flag==0x10 && time_tcp_1!=0 && time_tcp_2!=0 && time_tcp_3==0)
{
time_tcp_3=(local_tv_sec*1000000+local_tv_usec);
printf("TCP连接时延:%.6d us\n",time_tcp_3-time_tcp_1);
//都是按字节算的
//获取udp首部的位置
ip_len=(ih->ver_ihl & 0xf)*4;
if(ih->proto==0x11)
{
uh=(udp_header*)((u_char*)ih+ip_len);
//获取dns首部的位置
dnsh=(dns_header*)((u_char*)uh+8);
if(dnsh->flag==0x8081)
{
time_dnsr[i]=local_tv_sec*1000000+local_tv_usec; //单位是微妙
if(time_dnsq[i]!=0 && time_dnsr[i]!=0)
{
dns_delay=time_dnsr[i]-time_dnsq[i];
printf("\nDNS查询时延:%d ms \n",dns_delay);
memset(time_dnsr,0,sizeof(time_dnsr));
memset(time_dnsq,0,sizeof(time_dnsq));
}
i++;
}
else
{
time_dnsq[j]=local_tv_sec*1000000+local_tv_usec;
j++;
}
}
else if(dns_delay)
{
th=(tcp_header*)((u_char*)ih+ip_len); //unsigned char占一个字节
//tcp数据包头部长度
tcp_header_len=(((th->lenres<<4)|(th->lenres>>4)) & 0xf)*4;
if(th->flag==0x02)
{
time_tcp_1=(local_tv_sec*1000000+local_tv_usec);
}
if(th->flag==0x12 && time_tcp_1!=0)
{
time_tcp_2=(local_tv_sec*1000000+local_tv_usec);
}
if(th->flag==0x10 && time_tcp_1!=0 && time_tcp_2!=0 && time_tcp_3==0)
{
time_tcp_3=(local_tv_sec*1000000+local_tv_usec);
printf("TCP连接时延:%.6d us\n",time_tcp_3-time_tcp_1);
time_tcp_1=0; time_tcp_2=0; time_tcp_3=0;
}
}
}
}
}
运行结果如下
关注公众号,掌握更多多媒体领域知识与资讯
文章帮到你了?可以扫描如下二维码进行打赏~,打赏多少您随意~