/*************************************************
* 重邮汇测通信技术有限公司
* 文 件 名: PingThread.cpp
* 作 者: 倪又华
* 编写日期: 18/6/2013 21:32
* 说 明: 本段程序采用Qt线程函数来实现于Qt界面的
参数的交互传递
* 使用环境: Qt creator 4.7及以上 linux系统debian 6
***************************************************/
#include <QStringList>
#include "TraceThread.h"
#define BUFSIZE 1500
/* 本程序所要用到变量 */
// 接收的缓存
char g_cRecvbuf[BUFSIZE];
// 发送缓存
char g_cSendbuf[BUFSIZE];
// 设置包的大小
int g_nDataLen = 64,flag;
char *g_pchHost;
// 发送和接收端口
u_short g_usSport, g_usDport = 32768+666;
int g_nRecvfd = 0,g_nTtl,g_nDone = 0;
int gotalarm,g_nSendfd = 0;
//获得<hostname>在pargv[]中的索引
char g_str_host[NI_MAXHOST];
extern QStringList g_strListEdit;
char *g_output_ip,*g_str_dest;
double g_rtt_array[3];
// 需要探测的数量
int g_nprobe, g_nprobes = 3 ;
// 设置最大执行次数
int g_nMaxTtl = 30;
struct sockaddr_in dest_addr;
pid_t pid;
struct timeval tvrecv;
struct timeval tval;
struct proto
{
const char *(*icmpcode)(int);
int (*recv)(int, struct timeval *);
// 发送数据
struct sockaddr *sasend;
// 接收数据
struct sockaddr *sarecv;
// 最后一次接收的数据
struct sockaddr *salast;
// 绑定源ip端口
struct sockaddr *sabind;
// sockaddr的长度
socklen_t salen;
// icmp协议
int icmpproto;
int nTtllevel;
int nTtloptname;
} *pProto;
/* 本程序所要使用的函数*/
// 发送ICMP报文
void send_packet();
// icmp数据报的检验和算法
unsigned short cal_chksum(unsigned short *addr,int len);
// 除去报头,产生icmp报文
int pack(int pack_no);
// 端口不可达时反馈的信息
const char *icmpcode_v4(int);
// 接收数据报
int recv_v4(int, struct timeval *);
// 超时信号函数
void sig_alrm(int);
// 主循环函数,发送icmp探测信息
void traceloop(void);
// 计算往返时间差,计算出rtt的值
void tv_sub(struct timeval *, struct timeval *);
/* 以下几个解析地址的 */
char *Sock_ntop_host(const struct sockaddr *sa, socklen_t salen);
char *sock_ntop_host(const struct sockaddr *sa, socklen_t salen);
void sock_set_port(struct sockaddr *sa, socklen_t salen, int port);
int sock_cmp_addr(const struct sockaddr *sa1, const struct sockaddr *sa2,
socklen_t salen);
struct addrinfo *Host_serv(const char *host, const char *serv,
int family, int socktype);
// 初始化结构体proto
struct proto proto_v4 = { icmpcode_v4, recv_v4, NULL, NULL, NULL, NULL, 0,
IPPROTO_ICMP, IPPROTO_IP, IP_TTL };
/* 主循环函数 */
void TraceThread::traceloop(void)
{
// 初始化变量
mStrDestInfo = " ";
// 让目的端口+1
int nSeq = 0;
// 接收icmp报文返回的参数
int nCode;
// 计算本机与目的主机距离的的时间
double rtt = 0;
// icmp报文长度
int packetsize;
// 生成icmp报文
packetsize = pack(g_nTtl);
// 设置发送端口
setsockopt(g_nSendfd, pProto->nTtllevel, pProto->nTtloptname,
&g_nTtl, sizeof(int));
// 初始化
bzero(pProto->salast, pProto->salen);
// 发送icmp数据报
send_packet();
// 打印跳数
printf("%2d ", g_nTtl);
// 刷新内存
fflush(stdout);
// 发送3次探测数据
for (g_nprobe = 0; g_nprobe < g_nprobes; g_nprobe++)
{
// 判断线程终止的信号
if( flag == 0 )
break;
// 每发送一次就让目的端口+1
nSeq++;
// 设置目的端口
sock_set_port(pProto->sasend, pProto->salen, htons(g_usDport + nSeq));
// 发送数据报
sendto(g_nSendfd, g_cSendbuf, packetsize, 0, pProto->sasend, pProto->salen);
// 获取接收时间
//send_packet();
gettimeofday(&tval, NULL);
// 判断数据是否接收成功
nCode = (*pProto->recv)(nSeq, &tvrecv);
// 数据接收失败
if ( nCode == -3)
{
// 超时打印"*"
printf(" *");
mStrRtt = QString("* ");
}
// 数据接收成功
else
{
// 显示应答信息
if (sock_cmp_addr(pProto->sarecv, pProto->salast, pProto->salen) != 0)
{
// 获取目的ip信息
g_str_dest = sock_ntop_host(pProto->sarecv, pProto->salen);
if (getnameinfo(pProto->sarecv, pProto->salen, g_str_host,
sizeof(g_str_host), NULL, 0, 0) == 0)
{
printf(" %s (%s)", g_str_host,g_str_dest);
mStrDestInfo = QString("%1 (%2) ").arg(g_str_host).arg(g_str_dest);
}
else
printf(" %s",sock_ntop_host(pProto->sarecv, pProto->salen));
memcpy(pProto->salast, pProto->sarecv, pProto->salen);
}
// 计算往返时间差
tv_sub(&tvrecv, &tval);
// 计算出rrt值
rtt = tvrecv.tv_sec * 1000.0 + tvrecv.tv_usec / 1000.0;
// 将时间转换成QString
mStrRtt = QString::number(rtt).append("ms ");
printf(" %.3f ms", rtt);
// 目的端口不能到达
if (nCode == -1)
g_nDone++;
else if (nCode >= 0)
printf(" (ICMP %s)", (*pProto->icmpcode)(nCode));
}
// 发送参数并触发信号传递到Qt界面
sleep(1);
emit IpDnsSingals(mStrDestInfo,mStrRtt,g_nTtl,g_nprobe);
}
printf("\n");
}
/* 接收数据报 */
int recv_v4(int, struct timeval *tv)
{
int hlen1, hlen2, icmplen, nEchoRet;
socklen_t sockLen;
ssize_t size;
struct ip *ip, *hip;
struct icmp *icmp;
gotalarm = 0;
alarm(3);
// 读入返送到原始套接字的所有ICMP消息
while(1)
{
if (gotalarm)
return(-3);
sockLen = pProto->salen;
// 判断数据报是否接收成功
size = recvfrom(g_nRecvfd, g_cRecvbuf, sizeof(g_cRecvbuf),
0, pProto->sarecv, &sockLen);
if (size < 0)
{
if (errno == EINTR)
continue;
}
// 获取ICMP头部指针
ip = (struct ip *) g_cRecvbuf;
// ipheader长度
hlen1 = ip->ip_hl << 2;
icmp = (struct icmp *) (g_cRecvbuf + hlen1);
if ( (icmplen = size - hlen1) < 8)
continue;
// 处理ICMP传输中超时错误
if (icmp->icmp_type == ICMP_TIMXCEED &&
icmp->icmp_code == ICMP_TIMXCEED_INTRANS)
{
if (icmplen < 8 + sizeof(struct ip))
continue;
hip = (struct ip *) (g_cRecvbuf + hlen1 + 8);
hlen2 = hip->ip_hl << 2;
if (icmplen < 8 + hlen2 + 4)
continue;
// 接收数据成功
else
{
nEchoRet = -2;
break;
}
}
// 处理ICMP端口不能抵达错误
else if (icmp->icmp_type == ICMP_UNREACH)
{
qDebug("Noreplay port");
if (icmplen < 8 + sizeof(struct ip))
continue;
hip = (struct ip *) (g_cRecvbuf + hlen1 + 8);
hlen2 = hip->ip_hl << 2;
if (icmplen < 8 + hlen2 + 4)
continue;
else
nEchoRet = icmp->icmp_code;
}
}
alarm(0);
// 记录返�