linuxc实现ping命令

不及物动词 其他 22

回复

共3条回复 我来回复
  • worktile的头像
    worktile
    Worktile官方账号
    评论

    要在Linux环境下实现ping命令,可以使用C语言编写一个简单的程序。

    首先,需要包含以下头文件:

    “`c
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    “`

    然后,定义一些宏和全局变量:

    “`c
    #define PACKET_SIZE 4096
    #define MAX_WAIT_TIME 5
    #define MAX_NO_PACKETS 3

    int sockfd, datalen = 56;
    char sendpacket[PACKET_SIZE];
    char recvpacket[PACKET_SIZE];
    int nsend = 0, nreceived = 0;

    struct sockaddr_in dest_addr;
    struct timeval tvrecv;
    “`

    接下来,编写一些辅助函数,包括校验和计算、发送ICMP报文、接收并解析ICMP报文等:

    校验和计算函数:

    “`c
    unsigned short cal_chksum(unsigned short *addr, int len)
    {
    int nleft = len;
    int sum = 0;
    unsigned short *w = addr;
    unsigned short answer = 0;

    while (nleft > 1)
    {
    sum += *w++;
    nleft -= 2;
    }

    if (nleft == 1)
    {
    *(unsigned char *)(&answer) = *(unsigned char *)w;
    sum += answer;
    }

    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    answer = ~sum;
    return answer;
    }
    “`

    发送ICMP报文函数:

    “`c
    void send_packet()
    {
    int packetsize;
    memset(sendpacket, 0, sizeof(sendpacket));
    struct icmp *icmp = (struct icmp *)sendpacket;
    icmp->icmp_type = ICMP_ECHO;
    icmp->icmp_code = 0;
    icmp->icmp_cksum = 0;
    icmp->icmp_seq = nsend++;
    icmp->icmp_id = getpid();
    memset(icmp->icmp_data, 0xa5, datalen);
    gettimeofday((struct timeval *)icmp->icmp_data, NULL);
    packetsize = 8 + datalen;
    icmp->icmp_cksum = cal_chksum((unsigned short *)icmp, packetsize);
    sendto(sockfd, sendpacket, packetsize, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    }
    “`

    接收并解析ICMP报文函数:

    “`c
    void recv_packet()
    {
    int n;
    socklen_t fromlen;
    extern int errno;
    signal(SIGALRM, recv_packet);
    fromlen = sizeof(dest_addr);
    alarm(MAX_WAIT_TIME);
    while (1)
    {
    if ((n = recvfrom(sockfd, recvpacket, sizeof(recvpacket), 0, (struct sockaddr *)&dest_addr, &fromlen)) < 0) { if (errno == EINTR) continue; perror("recvfrom error"); return; } gettimeofday(&tvrecv, NULL); icmp_handler(); nreceived++; break; }}```解析ICMP报文函数:```cvoid icmp_handler(){ struct icmp *icmp = (struct icmp *)recvpacket; if (icmp->icmp_type == ICMP_ECHOREPLY && icmp->icmp_id == getpid())
    printf(“%d bytes from %s icmp_seq=%u ttl=%d time=%.2f ms\n”,
    datalen,
    inet_ntoa(dest_addr.sin_addr),
    icmp->icmp_seq,
    icmp->icmp_ttl,
    (double)(tvrecv.tv_usec – ((struct timeval *)icmp->icmp_data)->tv_usec) / 1000);
    }
    “`

    最后,编写主函数,并实现ping命令的主要逻辑:

    “`c
    int main(int argc, char *argv[])
    {
    struct hostent *host;
    struct protoent *protocol;
    unsigned long inaddr = 0l;
    int waittime = MAX_WAIT_TIME;
    int size = 50 * 1024;
    if (argc < 2) { printf("usage:%s hostname/IP address\n", argv[0]); exit(1); } if ((protocol = getprotobyname("icmp")) == NULL) { perror("getprotobyname"); exit(1); } if ((sockfd = socket(AF_INET, SOCK_RAW, protocol->p_proto)) < 0) { perror("socket error"); exit(1); } setuid(getuid()); setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); bzero(&dest_addr, sizeof(dest_addr)); dest_addr.sin_family = AF_INET; if (inaddr = inet_addr(argv[1]) == INADDR_NONE) { if ((host = gethostbyname(argv[1])) == NULL) { perror("gethostbyname error"); exit(1); } memcpy((char *)&dest_addr.sin_addr, host->h_addr, host->h_length);
    }
    else
    {
    dest_addr.sin_addr.s_addr = inet_addr(argv[1]);
    }
    pid_t pid = getpid();
    printf(“PING %s(%s):%d bytes data in ICMP packets.\n”, argv[1], inet_ntoa(dest_addr.sin_addr), datalen);
    send_packet();
    recv_packet();
    printf(“%s:received = %d, lost = %d (%d%% loss)\n”, argv[1], nreceived, nsend – nreceived, (nsend – nreceived) / nsend * 100);
    close(sockfd);
    return 0;
    }
    “`

    将上述代码保存为一个文件(例如ping.c),然后通过以下命令编译并运行程序:

    “`
    gcc -o ping ping.c
    sudo ./ping
    “`

    其中,``指定要ping的主机名或IP地址。程序将输出类似于ping命令的结果。

    这就是在Linux环境下使用C语言实现ping命令的方法。

    2年前 0条评论
  • 不及物动词的头像
    不及物动词
    这个人很懒,什么都没有留下~
    评论

    要在Linux环境下实现ping命令,可以使用C语言编写代码。以下是基本的步骤和代码示例。

    步骤1: 包含所需的头文件
    “`c
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    “`

    步骤2: 定义数据结构
    “`c
    struct ping_pkt {
    struct icmphdr hdr;
    char msg[64-sizeof(struct icmphdr)];
    };
    “`

    步骤3: 实现校验和计算函数
    “`c
    unsigned short checksum(void *b, int len) {
    unsigned short *buf = (unsigned short *)b;
    unsigned int sum = 0;
    unsigned short result;

    for (sum = 0; len > 1; len -= 2)
    sum += *buf++;
    if (len == 1)
    sum += *(unsigned char *)buf;
    sum = (sum >> 16) + (sum & 0xFFFF);
    sum += (sum >> 16);
    result = ~sum;
    return result;
    }
    “`

    步骤4: 实现ping函数
    “`c
    void ping(const char *hostname) {
    int sockfd;
    struct sockaddr_in addr;
    struct hostent *host;
    struct ping_pkt pkt;
    struct timeval tv;
    int ttl_val = 64, msg_count = 0;

    sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    if (sockfd < 0) { printf("Error in socket creation.\n"); return; } setsockopt(sockfd, SOL_IP, IP_TTL, &ttl_val, sizeof(ttl_val)); setuid(getuid()); addr.sin_family = AF_INET; host = gethostbyname(hostname); if (!host) { printf("Couldn't resolve hostname.\n"); return; } printf("PING %s (%s):\n", hostname, inet_ntoa(*(struct in_addr *)host->h_addr));

    while (msg_count < 10) { bzero(&pkt, sizeof(pkt)); pkt.hdr.type = ICMP_ECHO; pkt.hdr.un.echo.id = getpid(); for (int i = 0; i < sizeof(pkt.msg) - 1; i++) pkt.msg[i] = i + '0'; pkt.msg[sizeof(pkt.msg) - 1] = 0; pkt.hdr.un.echo.sequence = msg_count++; pkt.hdr.checksum = checksum(&pkt, sizeof(pkt)); usleep(200000); // 等待200ms gettimeofday(&tv, NULL); double sent_time = tv.tv_sec + tv.tv_usec / 1000000.0; if (sendto(sockfd, &pkt, sizeof(pkt), 0, (struct sockaddr *)&addr, sizeof(addr)) <= 0) { printf("Error in sending packet.\n"); continue; } struct ping_pkt rcv_pkt; socklen_t addr_len = sizeof(addr); if (recvfrom(sockfd, &rcv_pkt, sizeof(rcv_pkt), 0, (struct sockaddr *)&addr, &addr_len) <= 0) printf("Packet lost.\n"); else { gettimeofday(&tv, NULL); double rcv_time = tv.tv_sec + tv.tv_usec / 1000000.0; double rtt = rcv_time - sent_time; printf("Reply from %s: seq=%d, rtt=%.3f ms\n", inet_ntoa(addr.sin_addr), rcv_pkt.hdr.un.echo.sequence, rtt * 1000); } } close(sockfd);}```步骤5: 调用ping函数```cint main() { const char *hostname = "www.example.com"; ping(hostname); return 0;}```以上就是使用C语言在Linux环境下实现ping命令的基本步骤和代码示例。通过使用原始套接字和ICMP协议,可以发送和接收ping消息,并计算往返时间(RTT)以检测主机是否可达。

    2年前 0条评论
  • fiy的头像
    fiy
    Worktile&PingCode市场小伙伴
    评论

    在Linux中,我们可以使用C语言来实现类似于ping命令的功能,即发送ICMP Echo请求并接收ICMP Echo回复。下面是一个基本的实现过程:

    1. 导入必要的头文件
    “`
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    “`

    2. 定义常量
    “`
    #define PACKET_SIZE 4096
    #define MAX_WAIT_TIME 5
    #define MAX_NO_PACKETS 3
    “`
    – PACKET_SIZE定义了发送和接收缓冲区的大小。
    – MAX_WAIT_TIME定义了等待响应的最大时间(以秒为单位)。
    – MAX_NO_PACKETS定义了发送ICMP Echo请求的次数。

    3. 定义全局变量
    “`
    struct timeval tvsend;
    struct timeval tvrecv;

    char sendpacket[PACKET_SIZE];
    char recvpacket[PACKET_SIZE];

    int sockfd;
    int datalen = 56;
    int nsend = 0, nreceived = 0;
    pid_t pid;
    “`
    – tvsend和tvrecv用于记录发送和接收时间。
    – sendpacket和recvpacket分别用于存储发送和接收数据。
    – sockfd是套接字描述符。
    – datalen定义了要发送的数据长度。
    – nsend和nreceived分别表示已发送和已接收的ICMP Echo请求的数量。
    – pid保存了进程的ID。

    4. 定义函数
    – 定义校验和函数
    “`
    unsigned short cal_chksum(unsigned short *addr, int len) {
    int nleft = len;
    int sum = 0;
    unsigned short *w = addr;
    unsigned short answer = 0;

    while (nleft > 1) {
    sum += *w++;
    nleft -= 2;
    }
    if (nleft == 1) {
    *(unsigned char *)(&answer) = *(unsigned char *)w;
    sum += answer;
    }
    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    answer = ~sum;
    return answer;
    }
    “`
    – 发送ICMP Echo请求函数
    “`
    void send_packet() {
    struct timeval tv;
    struct icmp *icmp;
    int len;
    icmp = (struct icmp *)sendpacket;
    memset(icmp, 0, sizeof(struct icmp));
    icmp->icmp_type = ICMP_ECHO;
    icmp->icmp_code = 0;
    icmp->icmp_cksum = 0;
    icmp->icmp_seq = nsend++;
    icmp->icmp_id = pid;
    len = 8 + datalen;
    icmp->icmp_cksum = cal_chksum((unsigned short *)icmp, len);
    gettimeofday(&tv, NULL);
    memcpy(&tvsend, &tv, sizeof(struct timeval));
    sendto(sockfd, sendpacket, len, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    }
    “`
    – 接收ICMP Echo回复函数
    “`
    void recv_packet() {
    int n, fromlen;
    extern int errno;
    struct timeval tv;
    fd_set rfds;

    fromlen = sizeof(from);
    gettimeofday(&tv, NULL);
    tv.tv_sec = MAX_WAIT_TIME;
    tv.tv_usec = 0;
    FD_ZERO(&rfds);
    FD_SET(sockfd, &rfds);

    while (1) {
    int retval = select(sockfd + 1, &rfds, NULL, NULL, &tv);
    if (retval == -1) {
    if (errno == EINTR) {
    continue;
    }
    else {
    perror(“select”);
    break;
    }
    }
    else if (retval == 0) {
    printf(“Request timeout for icmp_seq %d\n”, nsend);
    break;
    }
    n = recvfrom(sockfd, recvpacket, sizeof(recvpacket), 0, (struct sockaddr *)&from, &fromlen);
    if (n > 0) {
    gettimeofday(&tv, NULL);
    memcpy(&tvrecv, &tv, sizeof(struct timeval));
    if (n < sizeof(struct ip)) { printf("Packet too short (%d bytes) from %s\n", n, inet_ntoa(from.sin_addr)); continue; } struct ip *ip = (struct ip *)recvpacket; struct icmp *icmp = (struct icmp *)(recvpacket + (ip->ip_hl << 2)); if (icmp->icmp_type == ICMP_ECHOREPLY && icmp->icmp_id == pid) {
    printf(“%d bytes from %s: icmp_seq=%u ttl=%d time=%.3f ms\n”,
    n, inet_ntoa(from.sin_addr), icmp->icmp_seq, ip->ip_ttl,
    (tvrecv.tv_sec – tvsend.tv_sec) * 1000.0 + (tvrecv.tv_usec – tvsend.tv_usec) / 1000.0);
    }
    else {
    printf(“Error: packet received is not a reply from %s\n”, inet_ntoa(from.sin_addr));
    }
    }
    }
    }
    “`
    – 结束处理函数
    “`
    void statistics() {
    printf(“— %s ping statistics —\n”, dest_str);
    printf(“%d packets transmitted, %d received, %d%% packet loss\n”,
    nsend, nreceived, (nsend – nreceived) / nsend * 100);
    close(sockfd);
    }
    “`

    5. 主函数
    “`
    int main(int argc, char *argv[]) {
    struct hostent *host;
    struct protoent *protocol;
    unsigned long inaddr = 0l;

    if (argc < 2) { printf("usage: %s hostname/IP address\n", argv[0]); exit(1); } protocol = getprotobyname("icmp"); if (protocol == NULL) { perror("getprotobyname"); exit(1); } sockfd = socket(AF_INET, SOCK_RAW, protocol->p_proto);
    if (sockfd < 0) { perror("socket"); exit(1); } setuid(getuid()); pid = getpid(); bzero(&dest_addr, sizeof(dest_addr)); dest_addr.sin_family = AF_INET; inaddr = inet_addr(argv[1]); if (inaddr == INADDR_NONE) { host = gethostbyname(argv[1]); if (host == NULL) { perror("gethostbyname"); exit(1); } memcpy((char *)&dest_addr.sin_addr, host->h_addr, host->h_length);
    }
    else {
    memcpy((char *)&dest_addr.sin_addr, &inaddr, sizeof(inaddr));
    }

    strcpy(dest_str, argv[1]);

    printf(“PING %s (%s): %d data bytes\n”, dest_str, inet_ntoa(dest_addr.sin_addr), datalen);

    send_packet();
    recv_packet();
    statistics();

    return 0;
    }
    “`

    以上是一个简单的实现ping命令的方法,可以基于这个基础上进行编写和扩展。需要注意的是,该实现使用了原始套接字,因此需要root权限才能运行。

    2年前 0条评论
注册PingCode 在线客服
站长微信
站长微信
电话联系

400-800-1024

工作日9:30-21:00在线

分享本页
返回顶部