当前位置:编程学习 > C/C++ >>

ping命令的C语言实现(linux, IPv4,简单版)

这个程序主要运用了ICMPv4协议(回显请求)来测试本机到某服务器的网络是否连通,因为其中用到了原始套接字,所以运行该程序需要管理员权限。
 
PS:本程序只支持一种输入方式:./myping <hostname>,不支持其他参数。
 
思路:
1:根据hostname参数创建原始套接字。
2:每隔1秒钟向服务器发送一个ICMP回显请求。
3:循环接收从服务器返回的应答并处理其数据。
 
上代码:
[cpp] 
#include <signal.h>  
#include <stdio.h>  
#include <errno.h>  
#include <stdlib.h>  
  
#include <netinet/in_systm.h>  
#include <netinet/ip.h>  
#include <netinet/ip_icmp.h>  
#include <arpa/inet.h>  
#include <netdb.h>  
#include <sys/un.h>  
  
//各种缓冲区的长度  
#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 timeval *);  
  
//发送ICMP回显请求  
void send_msg(void);  
  
//循环发送、接收信息  
void readloop(void);  
  
//定时器入门函数,每隔一秒一次发送ICMP请求  
void sig_alrm(int);  
  
//计算两个时间之间的间隔  
void tv_sub(struct timeval *, struct timeval *);  
  
//获取服务器的地址等信息  
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 <hostname>  
    if( argc != 2 )  
        error_quit("usage: myping <hostname>");  
  
    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 timeval 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);  
    msg.msg_name = g_proto.sarecv;  
    msg.msg_iov = &iov;  
    msg.msg_iovlen = 1;  
    msg.msg_control = controlbuf;  
  
    //开始死循环,不断读取和处理从服务器中返回的信息  
    while( 1 )  
    {  
        msg.msg_namelen = g_proto.salen;  
        msg.msg_controllen = sizeof(controlbuf);  
        n = recvmsg(g_sockfd, &msg, 0);  
        if (n < 0)  
        {  
            if (errno == EINTR)  
                continue;  
            else  
                error_quit("recvmsg error");  
        } &nbs
补充:软件开发 , C语言 ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,