日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux ping库函数,在Linux上用C++实现Ping

發布時間:2023/12/8 linux 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux ping库函数,在Linux上用C++实现Ping 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

首先我們在terminal上使用ping命令并用wireshark軟件抓包,看看實現ping命令需要那些協議,以及ping的數據包由那些內容構成。

ping.png

catch_ping.png

用wireshark抓包后,發現ping命令發送的請求報文和收到的應答報文都是ICMP(Internet Control Message Protocol)網際控制報文協議。我們再仔細解析請求報文和應答報文:

request.png

reply.png

發現ICMP報文是裝在IP數據報中的,并且ICMP報文具有以下字段:Type(8位)、Code(8位)、Cheksum(16位)、Identifier(16位)、Sequence(16位)、Timestamp(8位)、Data。

結合以上對ping命令的分析,我們可以想到,實現ping命令,需要用到ICMP協議的相關內容。想要實現對IMCP報文自定義構建,我們還需要用到SOCK_RAW原始套接字的相關內容。在Linux上申請原始套接字需要root權限,但是ping命令可以被普通用戶正常運行,因此我們還需要用到在Linux上以普通用戶運行特權指令的相關內容(在Linux上以普通用戶運行特權指令在我的上一篇博客有詳細的說明,本文不在贅述)。此外,我們可以觀察到ping命令是通過捕獲Ctrl+C指令后才結束的,但是在結束之前,還對整個發送接收情況做了總結,因此,實現Ping命令還需要用到Linux上的信號機制。

ICMP報文

IMCP協議用于在IP主機、路由器之間傳遞控制消息,允許主機或路由器報告差錯情況和提供有關異常情況的報告。ICMP協議不是高層協議(看起來好像是高層協議,因為ICMP報文是裝在IP數據報中,作為其中的數據部分),而是IP層的協議。ICMP報文作為IP層數據報的數據,加上數據報的首部,組成IP數據報發送出去。ICMP報文格式如下圖所示:

ICMP.png

在謝希仁編著的第7版計算機網絡教材中,ICMP報文的格式與上圖相同,但是從上文的抓包情況來看,真正的ICMP報文在16位序列號數據之后,Data數據之前還加入了8位的時間戳數據。

仔細對比上文對請求報文和應答報文的抓包數據,發現除了ICMP報文的序列號(seq)因包而異以外,Type字段也不相同。查閱文獻后知道ICMP報文類型是根據Type字段和Code字段的組合來確定的。Type = 0,Code = 0代表回送請求(Echo Request),Type = 8,Code = 0代表回送應答(Echo Reply),分別對應ping命令的請求報文和應答報文。

對于ICMP報文的校驗和(Checksum)字段的計算,只需要以下幾個步驟:

將校驗和字段置零。

將每兩個字節(16位)相加(二進制求和)直到最后得出結果,若出現最后還剩一個字節繼續與前面結果相加。

(溢出)將高16位與低16位相加,直到高16位為0為止。

將最后的結果(二進制)取反。

用C/C++對ICMP報文數據的構造,可以直接利用ip_icmp.h頭文件中有關ICMP報文的內容進行構造。struct icmp結構如下:

struct icmp

{

uint8_t icmp_type; /* type of message, see below */

uint8_t icmp_code; /* type sub code */

uint16_t icmp_cksum; /* ones complement checksum of struct */

union

{

unsigned char ih_pptr; /* ICMP_PARAMPROB */

struct in_addr ih_gwaddr; /* gateway address */

struct ih_idseq /* echo datagram */

{

uint16_t icd_id;

uint16_t icd_seq;

} ih_idseq;

uint32_t ih_void;

/* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */

struct ih_pmtu

{

uint16_t ipm_void;

uint16_t ipm_nextmtu;

} ih_pmtu;

struct ih_rtradv

{

uint8_t irt_num_addrs;

uint8_t irt_wpa;

uint16_t irt_lifetime;

} ih_rtradv;

} icmp_hun;

#define icmp_pptr icmp_hun.ih_pptr

#define icmp_gwaddr icmp_hun.ih_gwaddr

#define icmp_id icmp_hun.ih_idseq.icd_id

#define icmp_seq icmp_hun.ih_idseq.icd_seq

#define icmp_void icmp_hun.ih_void

#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void

#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu

#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs

#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa

#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime

union

{

struct

{

uint32_t its_otime;

uint32_t its_rtime;

uint32_t its_ttime;

} id_ts;

struct

{

struct ip idi_ip;

/* options and then 64 bits of data */

} id_ip;

struct icmp_ra_addr id_radv;

uint32_t id_mask;

uint8_t id_data[1];

} icmp_dun;

#define icmp_otime icmp_dun.id_ts.its_otime

#define icmp_rtime icmp_dun.id_ts.its_rtime

#define icmp_ttime icmp_dun.id_ts.its_ttime

#define icmp_ip icmp_dun.id_ip.idi_ip

#define icmp_radv icmp_dun.id_radv

#define icmp_mask icmp_dun.id_mask

#define icmp_data icmp_dun.id_data

};

上述結構體中我們只需要關注icmp_type、icmp_code、icmp_cksum、icmp_seq、icmp_id、icmp_data字段即可。我們可以直接在內存中利用指針對icmp結構體的指針指向數據塊進行構造,以達到對整個IMCP報文的構建。例如:

icmp_pointer->icmp_type = ICMP_ECHO;

icmp_pointer->icmp_code = 0;

icmp_pointer->icmp_cksum = 0; //計算校驗和之前先要將校驗位置零

icmp_pointer->icmp_seq = send_pack_num + 1; //用send_pack_num作為ICMP包序列號

icmp_pointer->icmp_id = getpid(); //用進程號作為ICMP包標志

IP數據報

在ping命令中,我們使用recvfrom()函數接收到的回應報文是IP數據報,并且我們需要用到以下字段:

IP報頭長度IHL(Internet Header Length)以4字節為一個單位來記錄IP報頭的長度,是上述IP數據結構的ip_hl變量。

生存時間TTL(Time To Live)以秒為單位,指出IP數據報能在網絡上停留的最長時間,其值由發送方設定,并在經過路由的每一個節點時減一,當該值為0時,數據報將被丟棄,是上述IP數據結構的ip_ttl變量。

使用方法與上述ICMP報文結構體使用方法一致,這里給出struct ip的定義,不在過多說明:

struct ip

{

#if __BYTE_ORDER == __LITTLE_ENDIAN

unsigned int ip_hl:4; /* header length */

unsigned int ip_v:4; /* version */

#endif

#if __BYTE_ORDER == __BIG_ENDIAN

unsigned int ip_v:4; /* version */

unsigned int ip_hl:4; /* header length */

#endif

uint8_t ip_tos; /* type of service */

unsigned short ip_len; /* total length */

unsigned short ip_id; /* identification */

unsigned short ip_off; /* fragment offset field */

#define IP_RF 0x8000 /* reserved fragment flag */

#define IP_DF 0x4000 /* dont fragment flag */

#define IP_MF 0x2000 /* more fragments flag */

#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */

uint8_t ip_ttl; /* time to live */

uint8_t ip_p; /* protocol */

unsigned short ip_sum; /* checksum */

struct in_addr ip_src, ip_dst; /* source and dest address */

};

SOCK_RAW原始套接字

實際上,我們常用的網絡編程都是在應用層的手法操作,也就是大多數程序員接觸到的流式套接字(SOCK_STREAM)和數據包式套接字(SOCK_DGRAM)。而這些數據包都是由系統提供的協議棧實現,用戶只需要填充應用層報文即可,由系統完成底層報文頭的填充并發送。然而Ping命令的實現中需要執行更底層的操作,這個時候就需要使用原始套接字(SOCK_RAW)來實現。

原始套接字(SOCK_RAW)是一種不同于SOCK_STREAM、SOCK_DGRAM的套接字,它實現于系統核心。原始套接字可以實現普通套接字無法處理ICMP、IGMP等網絡報文,原始套接字也可以處理特殊的IPv4報文,此外,利用原始套接字,可以通過IP_HDRINCL套接字選項由用戶構造IP頭。總體來說,原始套接字可以處理普通的網絡報文之外,還可以處理一些特殊協議報文以及操作IP層及其以上的數據。

創建原始套接字的方法如下:

socket(AF_INET, SOCK_RAW, protocol);

這里的重點在于protocol字段,使用原始套接字之后,這個字段就不能簡單的置零了。在頭文件netinet/in.h中定義了系統中該字段目前能取的值,注意:有些系統中不一定實現了netinet/in.h中的所有協議。源代碼的linux/in.h中和netinet/in.h中的內容一樣。我們常見的有IPPROTO_TCP,IPPROTO_UDP和IPPROTO_ICMP。我們可以通過一下方法創建需要的ICMP協議的原始套接字:

struct protoent * protocol; //獲取協議用

//通過協議名稱獲取協議編號

if((protocol = getprotobyname("icmp")) == NULL){

fprintf(stderr, "Get protocol error:%s \n\a", strerror(errno));

exit(1);

}

//創建原始套接字,這里需要root權限,申請完成之后應該降權處理

if((sock_fd = socket(AF_INET, SOCK_RAW, protocol->p_proto)) == -1){

fprintf(stderr, "Greate RAW socket error:%s \n\a", strerror(errno));

exit(1);

}

//降權處理,使該進程的EUID,SUID的值變成RUID的值

setuid(getuid());

用這種方式我就可以得到原始的IP包了,然后就可以自定義IP所承載的具體協議類型,如TCP,UDP或ICMP,并手動對每種承載在IP協議之上的報文進行填充。

Linux上的捕獲Ctrl+C信號

在Linux C/C++程序中,如果程序一直以死循環的狀態運行,以Ctrl+C結束,并且希望在結束時輸出一些統計數據幫助用戶分析,我們就需要用到信號處理機制。通過捕獲到的需要的信號后,執行信號處理函數。為此我們需要用到sigaction()函數,該函數的的功能是檢查或修改與制定信號相關聯的處理動作,其原型為:

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

signum參數用于指定需要捕獲的型號類型,act參數指定新的型號處理方式,oldact參數輸出先前型號的處理方式(如果不為NULL的話)。這個函數的關鍵在于對struct sigaction結構體的設置,我們首先找到該結構體的定義:

struct sigaction

{

/* Signal handler. */

#if defined __USE_POSIX199309 || defined __USE_XOPEN_EXTENDED

union

{

/* Used if SA_SIGINFO is not set. */

__sighandler_t sa_handler;

/* Used if SA_SIGINFO is set. */

void (*sa_sigaction) (int, siginfo_t *, void *);

}

__sigaction_handler;

# define sa_handler __sigaction_handler.sa_handler

# define sa_sigaction __sigaction_handler.sa_sigaction

#else

__sighandler_t sa_handler;

#endif

/* Additional set of signals to be blocked. */

__sigset_t sa_mask;

/* Special flags. */

int sa_flags;

/* Restore handler. */

void (*sa_restorer) (void);

};

注意到有如下幾個字段:

sa_handler 與 sa_sigaction這兩個字段為聯合體,因此只能同時設置一個,這兩個字段的作用都是用來存儲信號處理函數的指針,但是sa_sigaction作為信號處理函數,可以傳入自定義的參數,而sa_handler不行

sa_mask用來設置在處理該信號時暫時將sa_mask 指定的信號集擱置

sa_flags用來設置信號處理的其他相關操作,有下列數值可用,用OR運算組合:

A_NOCLDSTOP:如果參數signum為SIGCHLD,則當子進程暫停時并不會通知父進程

SA_ONESHOT/SA_RESETHAND:當調用新的信號處理函數前,將此信號處理方式改為系統預設的方式

SA_RESTART:被信號中斷的系統調用會自行重啟

SA_NOMASK/SA_NODEFER:在處理此信號未結束前不理會此信號的再次到來

SA_SIGINFO:信號處理函數是帶有三個參數的sa_sigaction

因此,實現ping命令過程中,對Ctrl+C的捕獲及處理的具體實現方法如下:

void SingnalHandler(int signo) { //信號處理函數

//處理過程

...

exit(0);

}

int main(int argc, char * argv[]) {

struct sigaction action; //sigaction結構體

action.sa_handler = SingnalHandler;

sigemptyset(&action.sa_mask);

action.sa_flags = 0;

sigaction(SIGINT,&action,NULL); //SIGINT = 2捕獲Ctrl+C

while(1)

{

//死循環

...

sleep(1);

}

}

最終實現代碼

1、main.cpp

#include

#include "src/ping.h"

Ping * p;

void SingnalHandler(int signo) {

p->statistic();

exit(0);

}

int main(int argc, char * argv[]) {

struct sigaction action;

action.sa_handler = SingnalHandler;

sigemptyset(&action.sa_mask);

action.sa_flags = 0;

sigaction(SIGINT,&action,NULL);

Ping ping(argv[1], 1);

p = &ping;

ping.CreateSocket();

while(1)

{

ping.SendPacket();

ping.RecvPacket();

sleep(1);

}

}

2、ping.h(在src目錄下)

//

// Created by mylord on 2019/9/26.

//

#ifndef MYPING_PING_H

#define MYPING_PING_H

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define PACK_SIZE 32 //最小的ICMP數據包大小,8字節的ICMP包頭,16字節的DATA,其中DATA是timeval結構體

class Ping {

private:

std::string input_domain; //用來存儲通過main函數的參數傳入的域名或者ip

std::string backup_ip; //通過輸入的域名或者ip轉化成為的ip備份

int sock_fd;

int max_wait_time; //最大等待時間

int send_pack_num; //發送的數據包數量

int recv_pack_num; //收到的數據包數量

int lost_pack_num; //丟失的數據包數量

struct sockaddr_in send_addr; //發送到目標的套接字結構體

struct sockaddr_in recv_addr; //接受來自目標的套接字結構體

char send_pack[PACK_SIZE]; //用于保存發送的ICMP包

char recv_pack[PACK_SIZE + 20]; //用于保存接收的ICMP包

struct timeval first_send_time; //第一次發送ICMP數據包時的UNIX時間戳

struct timeval recv_time; //接收ICMP數據包時的UNIX時間戳

double min_time;

double max_time;

double sum_time;

int GeneratePacket();

int ResolvePakcet(int pack_szie);

unsigned short CalculateCksum(unsigned short * send_pack, int pack_size);

public:

Ping(const char * ip, int max_wait_time);

~Ping();

void CreateSocket();

void SendPacket();

void RecvPacket();

void statistic();

};

#endif //MYPING_PING_H

3、ping.cpp(在src目錄下)

//

// Created by mylord on 2019/9/26.

//

#include "ping.h"

Ping::Ping(const char * ip, int max_wait_time){

this->input_domain = ip;

this->max_wait_time = max_wait_time < 3 ? max_wait_time : 3;

this->send_pack_num = 0;

this->recv_pack_num = 0;

this->lost_pack_num = 0;

this->min_time = 0;

this->max_time = 0;

this->sum_time = 0;

}

Ping::~Ping() {

if(close(sock_fd) == -1) {

fprintf(stderr, "Close socket error:%s \n\a", strerror(errno));

exit(1);

}

}

void Ping::CreateSocket(){

struct protoent * protocol; //獲取協議用

unsigned long in_addr; //用來保存網絡字節序的二進制地址

struct hostent host_info, * host_pointer; //用于gethostbyname_r存放IP信息

char buff[2048]; //gethostbyname_r函數臨時的緩沖區,用來存儲過程中的各種信息

int errnop = 0; //gethostbyname_r函數存儲錯誤碼

//通過協議名稱獲取協議編號

if((protocol = getprotobyname("icmp")) == NULL){

fprintf(stderr, "Get protocol error:%s \n\a", strerror(errno));

exit(1);

}

//創建原始套接字,這里需要root權限,申請完成之后應該降權處理

if((sock_fd = socket(AF_INET, SOCK_RAW, protocol->p_proto)) == -1){

fprintf(stderr, "Greate RAW socket error:%s \n\a", strerror(errno));

exit(1);

}

//降權處理,使該進程的EUID,SUID的值變成RUID的值

setuid(getuid());

//設置send_addr結構體

send_addr.sin_family = AF_INET;

//判斷用戶輸入的點分十進制的ip地址還是域名,如果是域名則將其轉化為ip地址,并備份

//inet_addr()將一個點分十進制的IP轉換成一個長整數型數

if((in_addr = inet_addr(input_domain.c_str())) == INADDR_NONE){

//輸入的不是點分十進制的ip地址

if(gethostbyname_r(input_domain.c_str(), &host_info, buff, sizeof(buff), &host_pointer, &errnop)){

//非法域名

fprintf(stderr, "Get host by name error:%s \n\a", strerror(errno));

exit(1);

} else{

//輸入的是域名

this->send_addr.sin_addr = *((struct in_addr *)host_pointer->h_addr);

}

} else{

//輸入的是點分十進制的地址

this->send_addr.sin_addr.s_addr = in_addr;

}

//將ip地址備份下來

this->backup_ip = inet_ntoa(send_addr.sin_addr);

printf("PING %s (%s) %d(%d) bytes of data.\n", input_domain.c_str(),

backup_ip.c_str(), PACK_SIZE - 8, PACK_SIZE + 20);

gettimeofday(&first_send_time, NULL);

}

unsigned short Ping::CalculateCksum(unsigned short * send_pack, int pack_size){

int check_sum = 0; //校驗和

int nleft = pack_size; //還未計算校驗和的數據長度

unsigned short * p = send_pack; //用來做臨時指針

unsigned short temp; //用來處理字節長度為奇數的情況

while(nleft > 1){

check_sum += *p++; //check_sum先加以后,p的指針才向后移

nleft -= 2;

}

//奇數個長度

if(nleft == 1){

//利用char類型是8個字節,將剩下的一個字節壓入unsigned short(16字節)的高八位

*(unsigned char *)&temp = *(unsigned char *)p;

check_sum += temp;

}

check_sum = (check_sum >> 16) + (check_sum & 0xffff); //將之前計算結果的高16位和低16位相加

check_sum += (check_sum >> 16); //防止上一步也出現溢出

temp = ~check_sum; //temp是最后的校驗和

return temp;

}

int Ping::GeneratePacket()

{

int pack_size;

struct icmp * icmp_pointer;

struct timeval * time_pointer;

//將發送的char[]類型的send_pack直接強制轉化為icmp結構體類型,方便修改數據

icmp_pointer = (struct icmp *)send_pack;

//type為echo類型且code為0代表回顯應答(ping應答)

icmp_pointer->icmp_type = ICMP_ECHO;

icmp_pointer->icmp_code = 0;

icmp_pointer->icmp_cksum = 0; //計算校驗和之前先要將校驗位置0

icmp_pointer->icmp_seq = send_pack_num + 1; //用send_pack_num作為ICMP包序列號

icmp_pointer->icmp_id = getpid(); //用進程號作為ICMP包標志

pack_size = PACK_SIZE;

//將icmp結構體中的數據字段直接強制類型轉化為timeval類型,方便將Unix時間戳賦值給icmp_data

time_pointer = (struct timeval *)icmp_pointer->icmp_data;

gettimeofday(time_pointer, NULL);

icmp_pointer->icmp_cksum = CalculateCksum((unsigned short *)send_pack, pack_size);

return pack_size;

}

void Ping::SendPacket() {

int pack_size = GeneratePacket();

if((sendto(sock_fd, send_pack, pack_size, 0, (const struct sockaddr *)&send_addr, sizeof(send_addr))) < 0){

fprintf(stderr, "Sendto error:%s \n\a", strerror(errno));

exit(1);

}

this->send_pack_num++;

}

//要對收到的IP數據包去IP報頭操作,校驗ICMP,提取時間戳

int Ping::ResolvePakcet(int pack_size) {

int icmp_len, ip_header_len;

struct icmp * icmp_pointer;

struct ip * ip_pointer = (struct ip *)recv_pack;

double rtt;

struct timeval * time_send;

ip_header_len = ip_pointer->ip_hl << 2; //ip報頭長度=ip報頭的長度標志乘4

icmp_pointer = (struct icmp *)(recv_pack + ip_header_len); //pIcmp指向的是ICMP頭部,因此要跳過IP頭部數據

icmp_len = pack_size - ip_header_len; //ICMP報頭及ICMP數據報的總長度

//收到的ICMP包長度小于報頭

if(icmp_len < 8) {

printf("received ICMP pack lenth:%d(%d) is error!\n", pack_size, icmp_len);

lost_pack_num++;

return -1;

}

if((icmp_pointer->icmp_type == ICMP_ECHOREPLY) &&

(backup_ip == inet_ntoa(recv_addr.sin_addr)) &&

(icmp_pointer->icmp_id == getpid())){

time_send = (struct timeval *)icmp_pointer->icmp_data;

if((recv_time.tv_usec -= time_send->tv_usec) < 0) {

--recv_time.tv_sec;

recv_time.tv_usec += 10000000;

}

rtt = (recv_time.tv_sec - time_send->tv_sec) * 1000 + (double)recv_time.tv_usec / 1000.0;

if(rtt > (double)max_wait_time * 1000)

rtt = max_time;

if(min_time == 0 | rtt < min_time)

min_time = rtt;

if(rtt > max_time)

max_time = rtt;

sum_time += rtt;

printf("%d byte from %s : icmp_seq=%u ttl=%d time=%.1fms\n",

icmp_len,

inet_ntoa(recv_addr.sin_addr),

icmp_pointer->icmp_seq,

ip_pointer->ip_ttl,

rtt);

recv_pack_num++;

} else{

printf("throw away the old package %d\tbyte from %s\ticmp_seq=%u\ticmp_id=%u\tpid=%d\n",

icmp_len, inet_ntoa(recv_addr.sin_addr), icmp_pointer->icmp_seq,

icmp_pointer->icmp_id, getpid());

return -1;

}

}

void Ping::RecvPacket() {

int recv_size, fromlen;

fromlen = sizeof(struct sockaddr);

while(recv_pack_num + lost_pack_num < send_pack_num) {

fd_set fds;

FD_ZERO(&fds); //每次循環都必須清空FD_Set

FD_SET(sock_fd, &fds); //將sock_fd加入集合

int maxfd = sock_fd + 1;

struct timeval timeout;

timeout.tv_sec = this->max_wait_time;

timeout.tv_usec = 0;

//使用select實現非阻塞IO

int n = select(maxfd, NULL, &fds, NULL, &timeout);

switch(n) {

case -1:

fprintf(stderr, "Select error:%s \n\a", strerror(errno));

exit(1);

case 0:

printf("select time out, lost packet!\n");

lost_pack_num++;

break;

default:

//判斷sock_fd是否還在集合中

if(FD_ISSET(sock_fd, &fds)) {

//還在集合中則說明收到了回顯的數據包

if((recv_size = recvfrom(sock_fd, recv_pack, sizeof(recv_pack),

0, (struct sockaddr *)&recv_addr, (socklen_t *)&fromlen)) < 0) {

fprintf(stderr, "packet error(size:%d):%s \n\a", recv_size, strerror(errno));

lost_pack_num++;

} else{

//收到了可能合適的數據包

gettimeofday(&recv_time, NULL);

ResolvePakcet(recv_size);

}

}

break;

}

}

}

void Ping::statistic() {

double total_time;

struct timeval final_time;

gettimeofday(&final_time, NULL);

if((final_time.tv_usec -= first_send_time.tv_usec) < 0) {

--final_time.tv_sec;

final_time.tv_usec += 10000000;

}

total_time = (final_time.tv_sec - first_send_time.tv_sec) * 1000 + (double)final_time.tv_usec / 1000.0;

printf("\n--- %s ping statistics ---\n",input_domain.c_str());

printf("%d packets transmitted, %d received, %.0f%% packet loss, time %.0f ms\n",

send_pack_num, recv_pack_num, (double)(send_pack_num - recv_pack_num) / (double)send_pack_num,

total_time);

printf("rtt min/avg/max = %.3f/%.3f/%.3f ms\n", min_time, (double)sum_time / recv_pack_num, max_time);

}

4、CMakeLists.txt

cmake_minimum_required(VERSION 3.10)

project(MyPing)

set(CMAKE_CXX_STANDARD 14)

add_executable(MyPing main.cpp src/ping.cpp src/ping.h)

5、編譯及運行過程

cmake .

sudo make

sudo chmod u+s MyPing

./MyPing www.baidu.com

總結

以上是生活随笔為你收集整理的linux ping库函数,在Linux上用C++实现Ping的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。