【分享】Linux C实现ping程序的源码 (linux c ping程序源码)

作为计算机网络中最基础的工具之一,Ping是广泛使用的网络诊断工具。它通过向指定的目标主机发送ICMP回显请求(即“ping请求”),并接收到相应的ICMP回显应答(即“ping响应”),来测试目标主机是否能够访问、响应的时间以及包丢失率等。在Linux系统中,我们可以自行开发一个Ping程序,并完成自己的定制化需求。本文将分享如何在Linux C语言中实现基本的Ping程序的源码,希望对初学C语言和网络编程的读者有所帮助。

一、Ping程序的实现原理

实际上,Ping程序并不是一件很复杂的事情。它主要分为两个部分:发送ICMP Echo请求和接收ICMP Echo回复。具体而言,实现Ping程序主要通过以下三个步骤:

1. 创建ICMP Echo请求报文:这个报文包含了目标主机的IP地址、类型为0x08的ICMP Echo请求的头部信息、以及发送请求的时间等信息。

2. 发送ICMP Echo请求:调用系统提供的“sendto()”函数,将构建好的ICMP Echo请求报文发送到目标主机的IP地址上,即向目标IP地址发送一个ICMP Echo请求报文。

3. 接收ICMP Echo回复:调用系统提供的“recvfrom()”函数,接收目标主机返回的ICMP Echo回复报文(如果有的话),并进行相关的解析。

有了以上三步,我们就可以实现一个基本的Ping程序。

二、Ping程序的源码实现

以下是一个基于Linux C语言实现的Ping程序的源码:

“`

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define PACKET_SIZE 4096

char sendpacket[PACKET_SIZE];

char recvpacket[PACKET_SIZE];

int sockfd,datalen = 56;

int nsend = 0,nreceived = 0;

pid_t pid;

struct sockaddr_in dest_addr;

struct sockaddr_in from;

struct timeval tvrecv;

void statistics(void);

unsigned short cal_chksum(unsigned short *addr,int len);

void bl(const char *errMsg) {

printf(“%s\n”, errMsg);

exit(1);

}

void send_packet(void) {

int pktsize;

nsend++;

memset(sendpacket, 0, PACKET_SIZE);

sprintf(sendpacket, “%c%d”, 8, nsend);

pktsize = 8 + datalen;

*(unsigned short *)(sendpacket + 2) = htons(pktsize);

*(unsigned short *)(sendpacket + 4) = htons(0);

*(unsigned short *)(sendpacket + 6) = htons(1);

if (sendto(sockfd, sendpacket, pktsize, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr))

bl(“sendto error”);

}

}

void recv_packet(void) {

int n,fromlen;

fromlen = sizeof(from);

signal(SIGALRM,statistics);

alarm(10);

while (1) {

n = recvfrom(sockfd, recvpacket, sizeof(recvpacket), 0, (struct sockaddr *)&from, &fromlen);

if (n

if (errno == EINTR) {

continue;

}

bl(“recvfrom error”);

}

if (n

printf(“The received packet size is less than 16 bytes\n”);

continue;

}

if (recvpacket[0] != 0x08) {

printf(“The received packet is not ICMP Echo Reply packet\n”);

continue;

}

if (recvpacket[1] != 0) {

printf(“The received packet is not ICMP Echo Reply packet\n”);

continue;

}

if (*(unsigned short *)(recvpacket + 4) != htons(0)) {

printf(“The received packet header checksum is not correct\n”);

continue;

}

if (*(unsigned short *)(recvpacket + 6) != htons(1)) {

printf(“The received packet header identifier is not correct\n”);

continue;

}

if (*(unsigned short *)(recvpacket + 8) != htons(nsend)) {

printf(“The received packet sequence number is not correct\n”);

continue;

}

gettimeofday(&tvrecv,NULL);

statistics();

break;

}

}

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);

}

void statistics(void) {

char buffer[256];

sprintf(buffer,”————-PING statistics————-\n%d packets tranitted, %d received”, nsend, nreceived);

if(nsend – nreceived > 0) {

sprintf(buffer + strlen(buffer),”, %%%d packet loss\n”,(nsend – nreceived) / nsend * 100);

}

else {

strcat(buffer,”, 0.00% packet loss\n”);

}

printf(“%s\n”,buffer);

if (nsend >= 3) {

exit(0);

}

alarm(1);

send_packet();

recv_packet();

}

int mn(int argc,char **argv) {

struct hostent *host;

struct protoent *protocol;

unsigned long inaddr = 0l;

int size = 50 * 1024;

if (argc

bl(“usage: ping “);

}

if ((protocol = getprotobyname(“icmp”)) == NULL) {

bl(“getprotobyname() fled”);

}

if ((sockfd = socket(AF_INET, SOCK_RAW, protocol->p_proto))

bl(“socket() fled”);

}

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) {

bl(“gethostbyname() fled”);

}

memcpy((char *)&dest_addr.sin_addr, host->h_addr, host->h_length);

}

else {

dest_addr.sin_addr.s_addr = inaddr;

}

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();

return 0;

}

“`

三、源码解析

1. 在程序开头,我们先定义了一些全局变量,准备接下来的码实现所需要的参数。

“`

char sendpacket[PACKET_SIZE];

char recvpacket[PACKET_SIZE];

int sockfd,datalen = 56;

int nsend = 0,nreceived = 0;

pid_t pid;

struct sockaddr_in dest_addr;

struct sockaddr_in from;

struct timeval tvrecv;

“`

其中,sendpacket和recvpacket用于存储发送和接收的数据包;sockfd是套接字描述符;datalen表示数据部分的大小;nsend和nreceived分别表示发送的和接收的数量;pid表示进程的PID;dest_addr表示目标地址;from表示数据发送方的地址信息;tvrecv是记录接收到数据包的详细时间信息。

2. statistics()是用于统计发送和接收信息的函数。

“`

void statistics(void) {

char buffer[256];

sprintf(buffer,”————-PING statistics————-\n%d packets tranitted, %d received”, nsend, nreceived);

if(nsend – nreceived > 0) {

sprintf(buffer + strlen(buffer),”, %%%d packet loss\n”,(nsend – nreceived) / nsend * 100);

}

else {

strcat(buffer,”, 0.00% packet loss\n”);

}

printf(“%s\n”,buffer);

if (nsend >= 3) {

exit(0);

}

alarm(1);

send_packet();

recv_packet();

}

“`

其中,sprintf()用于格式化打印,将统计信息存储在buffer中;if…else语句用于计算丢包率;printf()用于将字符串打印到终端;nsend>=3时用于结束程序;alarm()用于设置定时器,1秒后开始轮训执行send_packet()和recv_packet()。

3. cal_chksum()函数用于计算校验和。

“`

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);

}

“`

我们主要用于对构建报文头进行校验和计算。

4. send_packet()函数用于发送数据包。

“`

void send_packet(void) {

int pktsize;

nsend++;

memset(sendpacket, 0, PACKET_SIZE);

sprintf(sendpacket, “%c%d”, 8, nsend);

pktsize = 8 + datalen;

*(unsigned short *)(sendpacket + 2) = htons(pktsize);

*(unsigned short *)(sendpacket + 4) = htons(0);

*(unsigned short *)(sendpacket + 6) = htons(1);

if (sendto(sockfd, sendpacket, pktsize, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr))

bl(“sendto error”);

}

}

“`

在该函数中,我们首先增加nsend的值以记录发送数量;然后重置sendpacket数组;接下来根据记录时间和编号构建请求包并计算包大小;然后向目标地址发送ICMP Echo请求,可以看到这个过程主要通过系统提供的sendto函数实现;最后增加错误处理,避免出现异常退出。

5. recv_packet()函数用于接收数据包。

“`

void recv_packet(void) {

int n,fromlen;

fromlen = sizeof(from);

signal(SIGALRM,statistics);

alarm(10);

while (1) {

n = recvfrom(sockfd, recvpacket, sizeof(recvpacket), 0, (struct sockaddr *)&from, &fromlen);

if (n

if (errno == EINTR) {

continue;

}

bl(“recvfrom error”);

}

if (n

printf(“The received packet size is less than 16 bytes\n”);

continue;

}

if (recvpacket[0] != 0x08) {

printf(“The received packet is not ICMP Echo Reply packet\n”);

continue;

}

if (recvpacket[1] != 0) {

printf(“The received packet is not ICMP Echo Reply packet\n”);

continue;

}

if (*(unsigned short *)(recvpacket + 4) != htons(0)) {

printf(“The received packet header checksum is not correct\n”);

continue;

}

if (*(unsigned short *)(recvpacket + 6) != htons(1)) {

printf(“The received packet header identifier is not correct\n”);

continue;

}

if (*(unsigned short *)(recvpacket + 8) != htons(nsend)) {

printf(“The received packet sequence number is not correct\n”);

continue;

}

gettimeofday(&tvrecv,NULL);

statistics();

break;

}

}

“`

在该函数中,我们主要是用于在规定的时间内接收报文,并对其的长度、类型头部信息、校验和、标识符和编号进行检查,确认接收到的数据包是有效的ICMP Echo回传包。

6. mn()函数是整个程序的入口函数。

“`

int mn(int argc,char **argv) {

struct hostent *host;

struct protoent *protocol;

unsigned long inaddr = 0l;

int size = 50 * 1024;

if (argc

bl(“usage: ping “);

}

if ((protocol = getprotobyname(“icmp”)) == NULL) {

bl(“getprotobyname() fled”);

}

if ((sockfd = socket(AF_INET, SOCK_RAW, protocol->p_proto))

bl(“socket() fled”);

}

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) {

bl(“gethostbyname() fled”);

}

memcpy((char *)&dest_addr.sin_addr, host->h_addr, host->h_length);

}

else {

dest_addr.sin_addr.s_addr = inaddr;

}

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();

return 0;

}

“`

在mn函数中,首先我们通过输入参数获取IP地址;然后创建原始套接字sci;接着设置套接字传输协议和接收缓冲区大小;接下来根据输入的IP地址,构建目标地址;最后输出ping的基本信息并开始轮询执行send_packet()和recv_packet()函数。

四、

相关问题拓展阅读:

linux下如何得到可执行文件的源代码?

Linux发行版中,程序都是编译好的二进制文件,系统和光盘中也不会提供这升绝友个程序的源代码。你需要到Linux发行版的网站去搜索有没有源代码。

还有一些开源项目,宏碧例如gdb,gcc,内核等有专门的网站。如果你吵槐的是redhat,suse,centos等linux,可以去redhat网站搜索其rpm的源代码包。

首者毁先必须设置程帆宽序的可执行性,

利用chmod来进行设置

2,利用编译器来进行编译一般.cpp用g++编译 .c用gcc编译

3程序上传一般使用ssh软件态嫌亮进行

Linux所有程序都是开源的,你下载下来的都是源代码,直接打开就可以…………解压缩

被编译好的程序是不可能查看源代码的,也乱侍是不可以修改的晌厅。

当然,linux所谓开源软件,是指你下的软件是源代码,需要你现用gcc编宴陪隐译后才能用。

比中下面文件脊裤hi.cpp

#include

main()

{

COUT>>”信野如hillo world!”>>endl;

}

写好后保滑启存好

g++ -o hi.out hi.cpp

chmod u+x hi.out

./hi.out

Linux中源码编译安装程序包括哪些基本步骤?

./configure

make

make install

之一步:创建编译脚本

进入到源码目录 执行 ./configure –prefix=/…/…..(–prefix=后面是吵隐想要安装到的目录)

第二部:编译

执行 make

第三部:安装

执行 make install

当然上面这几部都是最基本的步骤,如果想优化编译,散山要在./configure 后面加参数,或者configure之后手动修改Makefile文件 如O2(优化等级) FLAGS 等编译参数的修改。

以上都是源码包的编译

如果升掘厅是自己写的C代码 直接 用gcc编译即可。

例如 编译test.c

执行 gcc -o test test.c即可将test.c编译为可执行的文件 test

自己打出来的 要采纳啊!

cat README

linux c ping程序源码的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于linux c ping程序源码,【分享】Linux C实现ping程序的源码,linux下如何得到可执行文件的源代码?,Linux中源码编译安装程序包括哪些基本步骤?的信息别忘了在本站进行查找喔。


数据运维技术 » 【分享】Linux C实现ping程序的源码 (linux c ping程序源码)