这个程序主要运用了ICMPv4协议(回显请求)来测试本机到某服务器的网络是否连通,因为其中用到了原始套接字,所以运行该程序需要管理员权限。
PS:本程序只支持一种输入方式:./myping ,不支持其他参数。
思路:
1:根据hostname参数创建原始套接字。
2:每隔1秒钟向服务器发送一个ICMP回显请求。
3:循环接收从服务器返回的应答并处理其数据。
上代码:
[cpp]
#include
#include
#include
#include
#include
#include
#include
#include
#include
//各种缓冲区的长度
#define BUFSIZE 1500
//ICMP回显请求的长度
#define DATA_LEN 56
struct proto
{
struct sockaddr *sasend; /* sockaddr{} for send, from getaddrinfo */
struct sockaddr *sarecv; /* sockaddr{} for receiving */
socklen_t salen; /* length of sockaddr{}s */
int icmpproto; /* IPPROTO_xxx value for ICMP */
};
//全局变量
pid_t g_pid;
int g_sockfd;
struct proto g_proto = { NULL, NULL, 0, IPPROTO_ICMP };
//处理服务器返回的ICMP回显信息
void proc_msg(char *, ssize_t, struct msghdr *, struct timeva l *);
//发送ICMP回显请求
void send_msg(void);
//循环发送、接收信息
void readloop(void);
//定时器入门函数,每隔一秒一次发送ICMP请求
void sig_alrm(int);
//计算两个时间之间的间隔
void tv_sub(struct timeva l *, struct timeva l *);
//获取服务器的地址等信息
struct addrinfo *host_serv(const char *host,
const char *serv, int family, int socktype);
//根据服务器信息,得到服务器的IP地址
char *sock_ntop_host(const struct sockaddr *sa, socklen_t salen);
//计算校验和
uint16_t in_cksum(uint16_t *addr, int len);
//输出错误信息,退出程序
void error_quit(const char *str);
int main(int argc, char **argv)
{
int c;
struct addrinfo *ai;
struct sockaddr_in *sin;
char *ip_address;
char *host;
//本程序只支持一种输入方式:./myping
if( argc != 2 )
error_quit("usage: myping ");
host = argv[1];
//将pid的高二位全置为0,ICMP的ID只有16位
g_pid = getpid() & 0xffff;
//设置定时器,每秒钟向服务器发送一次请求
signal(SIGALRM, sig_alrm);
//获取服务器的信息(addrinfo结构)
ai = host_serv(host, NULL, 0, 0);
ip_address = sock_ntop_host(ai->ai_addr, ai->ai_addrlen);
printf("PING %s (%s): %d data bytes\n",
ai->ai_canonname ai->ai_canonname : ip_address,
ip_address, DATA_LEN);
//如果返回的协议簇不是AF_INET(IPv4),则退出
if ( ai->ai_family != AF_INET )
error_quit("unknown address family");
//设置proto结构体
g_proto.sasend = ai->ai_addr;
g_proto.sarecv = calloc(1, ai->ai_addrlen);
g_proto.salen = ai->ai_addrlen;
//开始循环发送/接收请求
readloop();
return 0;
}
void readloop(void)
{
int size;
char recvbuf[BUFSIZE];
char controlbuf[BUFSIZE];
struct msghdr msg;
struct iovec iov;
ssize_t n;
struct timeva l tval;
//创建一个IPv4的原始套接字
g_sockfd = socket(g_proto.sasend->sa_family, SOCK_RAW, g_proto.icmpproto);
if( -1 == g_sockfd )
error_quit("socket error");
//放弃管理员权限
//这个程序中,只用创建原始套接字时需要管理员权限
setuid(getuid());
//设置socket的接收缓冲区
size = 60 * 1024;
setsockopt(g_sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
//发出第一个请求
sig_alrm(SIGALRM);
//为recvmsg调用设置msghdr结构
iov.iov_base = recvbuf;
iov.iov_len = sizeof(recvbuf);