0


Ping程序的实现(计网课设)

写在前面

本题目为Ping程序的实现,为计算机网络课程设计,采用VS运行,忽略了一些报错,如C4996、C4233,程序能正常运行,可供同样需要完成课设的小伙伴们参考使用(ps:俺也是多方参考的,谢谢巨人的肩膀,😀)


一、课设要求

利用c语言实现的Ping命令,能用于测试一个主机到另一个主机间的联通情况,程序还提供了几个选项以实现不同的功能。
(1)实现Ping功能。程序能实现基本的Ping操作,发送ICMP回显请求报文,接收显应答报文。
(2)能输出指定条数的记录。程序提供了“-n”选项,用以输出指定条数的记录。
(3)能按照指定大小输出每条记录。程序提供了“datasize”选项,用以指定输出的数据报的大小。
(4)能输出用户帮助。程序提供了用户帮助,显示程序提供的选项以及选项格式等。

二、实现原理(俺认为是这样)

利用socket原始套接字(可自定义IP)实现发送/监听ICMP报文,实现基本的Ping功能,向目标主机发送ICMP回应请求,然后等待目标主机的ICMP回应答复。通过这种方式,它可以判断目标主机是否在线,以及网络延迟(即ping值)。

1.运行截图

功能1为默认Ping4次,数据报大小为32字节。其余功能均可指定次数或数据报大小,不做赘述。此处截图展示为功能4,指定次数和数据报大小。(也可直接Ping IP地址,俺懒得截图,可自己测试)

2.完整代码

#include<stdio.h>//标准输入输出函数 #include<stdlib.h>//实用程序库函数#include<winsock.h>//网络编程socket相关部分API #include<conio.h>//通用输入输出库#include<stdbool.h>//bool:true\false#pragmawarning(disable:4996)#pragmawarning(disable:4233)#pragmacomment(lib,"ws2_32.lib")//导入库文件:Windows Sockets应用程序接口, 用于支持Internet和网络应用程序#defineICMP_ECHOREPLY0//ICMP 回应答复类型码为0 #defineICMP_ECHOREQ8//ICMP 回应请求类型码为8 #defineREQ_DATASIZE32//默认请求数据报大小#definesent4//默认已发送条数int sentnum = sent;int PagSize = REQ_DATASIZE;//定义 IP 首部格式typedefstructIPHeader{
    u_char VIHL;//版本和首部长度 
    u_char ToS;//服务类型 
    u_short TotalLen;//总长度 
    u_short ID;//标识号 
    u_short Frag_Flags;//片偏移量 
    u_char TTL;//生存时间 
    u_char Protocol;//协议 
    u_short Checksum;//首部校验和structin_addr SrcIP;//源 IP 地址 structin_addr DestIP;//目的地址}IPHDR,* PIPHDR;//定义 ICMP 首部格式typedefstructICMPHeader{
    u_char Type;//类型 
    u_char Code;//代码 
    u_short Checksum;//首部校验和 
    u_short ID;//标识 
    u_short Seq;//序列号char Data;//数据}ICMPHDR,* PICMPHDR;//定义 ICMP 回应请求typedefstructREQUEST{
    ICMPHDR icmpHdr;
    DWORD dwTime;char cData[REQ_DATASIZE];}REQUEST,* PECHOREQUEST;//定义 ICMP 回应答复typedefstructREPLY{
    IPHDR ipHdr;
    REQUEST echoRequest;char cFiller[256];}REPLY,* PECHOREPLY;//计算校验和
u_short checksum(u_short* buffer,int len){registerint nleft = len;register u_short* w = buffer;register u_short answer;registerint sum =0;//使用 32 位累加器 ,进行 16 位的反馈计算while(nleft >1){
        sum +=*w++;
        nleft -=2;//每16位相加,因此字节数-2}//补全奇数位if(nleft ==1){
        u_short u =0;*(u_char*)(&u)=*(u_char*)w;
        sum += u;}//将反馈的 16 位从高位移到低位
    sum =(sum >>16)+(sum &0xffff);//高16位和低16位相加,sum & 0xffff将高16位置0
    sum +=(sum >>16);//将低16位与溢出值相加
    answer =~sum;//取反码return(answer);}//发送回应请求函数intSendRequest(SOCKET s,structsockaddr_in* lpstToAddr){static REQUEST echoReq;staticint nId =1;staticint nSeq =1;int nRet;//填充回应请求消息
    echoReq.icmpHdr.Type = ICMP_ECHOREQ;
    echoReq.icmpHdr.Code =0;
    echoReq.icmpHdr.Checksum =0;
    echoReq.icmpHdr.ID = nId++;
    echoReq.icmpHdr.Seq = nSeq++;//填充要发送的数据for(nRet =0; nRet < PagSize; nRet++){
        echoReq.cData[nRet]='1'+ nRet;}//存储发送的时间
    echoReq.dwTime =GetTickCount();//计算回应请求的校验和
    echoReq.icmpHdr.Checksum =checksum((u_short*)&echoReq,sizeof(REQUEST));//发送回应请求
    nRet =sendto(s,(LPSTR)&echoReq,sizeof(REQUEST),0,(structsockaddr*)lpstToAddr,sizeof(SOCKADDR_IN));if(nRet == SOCKET_ERROR){printf("send to() error:%d\n",WSAGetLastError());}return(nRet);}//接收应答回复并进行解析
DWORD ReceiveReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char* pTTL){
    REPLY echoReply;int nRet;int nAddrLen =sizeof(structsockaddr_in);//接收应答回复
    nRet =recvfrom(s,(LPSTR)&echoReply,sizeof(REPLY),0,(LPSOCKADDR)lpsaFrom,&nAddrLen);//检验接收结果if(nRet == SOCKET_ERROR){printf("recvfrom() error:%d\n",WSAGetLastError());}//记录返回的 TTL *pTTL = echoReply.ipHdr.TTL;//返回应答时间return(echoReply.echoRequest.dwTime);}//等待回应答复 ,使用select模型,设置等待时间,并返回相应的状态intWaitReply(SOCKET s){structtimeval timeout;
    fd_set readfds;
    readfds.fd_count =1;
    readfds.fd_array[0]= s;
    timeout.tv_sec =1;
    timeout.tv_usec =0;//设计时间为1s,超过1s为超时//调用select函数可以确定一个或多个套接字的状态,判断套接字上是否有数据,或者能否向一个套接字写入数据。return(select(1,&readfds,NULL,NULL,&timeout));//0:超时;大于0的值时,表示SOCKET数;失败时返回SOCKET_ERROR}//PING 功能实现voidPing(char* pstrHost, bool logic){char c;
    SOCKET rawSocket;
    LPHOSTENT lpHost;structsockaddr_in destIP;structsockaddr_in srcIP;
    DWORD dwTimeSent;
    DWORD dwElapsed;
    u_char cTTL;int nLoop;int nRet, mintime =100000, maxtime =0, averagetime =0;int reveived =0, lost =0;
    rawSocket =socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);//创建原始套接字 ,ICMP 类型// 第二个注释函数 socket if(rawSocket == SOCKET_ERROR){printf("socket() error:%d\n",WSAGetLastError());return;}//检测目标主机
    lpHost =gethostbyname(pstrHost);//返回对应于给定主机名的包含主机名字和地址信息的hostent结构的指针if(lpHost ==NULL){printf("Host not found:%s\n", pstrHost);return;}//设置目标机地址 
    destIP.sin_addr.s_addr =*((u_long FAR*)(lpHost->h_addr));// 设置目标 IP 
    destIP.sin_family = AF_INET;//地址规格
    destIP.sin_port =0;//提示开始进行 PING printf("\nPinging %s [%s] with %d bytes of data:\n", pstrHost,inet_ntoa(destIP.sin_addr), PagSize);//发起多次 PING 测试for(nLoop =0; nLoop < sentnum; nLoop++){if(logic) sentnum = sentnum +1;//发送 ICMP 回应请求 SendRequest(rawSocket,&destIP);//等待回复的数据
        nRet =WaitReply(rawSocket);if(nRet == SOCKET_ERROR){printf("select() error:%d\n",WSAGetLastError());break;}if(!nRet){
            lost++;printf("\nRequest time out.");continue;}//接收回复
        dwTimeSent =ReceiveReply(rawSocket,&srcIP,&cTTL);
        reveived++;//计算花费的时间
        dwElapsed =GetTickCount()- dwTimeSent;if(dwElapsed > maxtime) maxtime = dwElapsed;if(dwElapsed < mintime) mintime = dwElapsed;
        averagetime += dwElapsed;printf("\nReply from %s: bytes = %d time = %ldms TTL = %d",inet_ntoa(srcIP.sin_addr), PagSize, dwElapsed, cTTL);if(_kbhit())/* Use _getch to throw key away. */{if((c =_getch())==0x2)//crrl -b break;}elseSleep(1000);}printf("\n\n");printf("Ping statistics for %s:\n",inet_ntoa(srcIP.sin_addr));printf(" Packets: Sent = %d, Received = %d, Lost = %d (%.f%% loss),\n",
        sentnum, reveived, lost,(float)(lost *1.0/ sentnum)*100);if(lost ==0){printf("Approximate round trip times in milli-seconds:\n");printf(" Mintime = %dms, Maxtime = %dms, Averagetime = %dms\n", mintime, maxtime, averagetime / sentnum);}printf("\n\n");
    nRet =closesocket(rawSocket);if(nRet == SOCKET_ERROR){printf("closesocket() error:%d\n",WSAGetLastError());}}/*显示信息函数*/voidUserHelp(){printf("\t\t\t使用说明: ping 域名或ip地址\n");printf("\t\t\t功能1  Ping\n");printf("\t\t\t功能2  输出指定条数的记录\n");printf("\t\t\t功能3  按指定大小输出每条记录(最小32,最大1024)\n");printf("\t\t\t功能4  按指定大小输出指定条数的记录\n");printf("\t\t\t功能0  退出程序\n");printf("\n\n");}//主程序voidmain(){while(1){
        WSADATA wsd;// 检测输入的参数 //初始化 Winsock if(WSAStartup(MAKEWORD(1,1),&wsd)!=0){// 第一个函数说明 WSAStartup() printf(" 加载 Winsock 失败 !\n");}char opt[200];char* ptr = opt;
        bool log = false;int i;//获取用户输入的功能数字int n,size;printf("欢迎使用Ping程序\n");UserHelp();printf("请输入你所需要使用的功能:\n    功能");scanf("%d",&i);//i为获取到的用户输入的功能数字switch(i){case1://Ping功能{printf("Ping  ");scanf("%s", opt);//输入ping 的地址 字符串break;}case2://输出指定条数{printf("Ping  ");scanf("%s", opt);printf("请输入想要Ping该服务器的次数:");scanf("%d",&n);
            sentnum = n;break;}case3://输出指定大小{printf("Ping  ");scanf("%s", opt);printf("请输入想要传送的数据包大小:");scanf("%d",&size);
            PagSize = size;break;}case4://输出指定大小指定条数{printf("Ping  ");scanf("%s", opt);printf("请输入想要Ping该服务器的次数:");scanf("%d",&n);printf("请输入想要传送的数据包大小:");scanf("%d",&size);
            sentnum = n;
            PagSize = size;break;}case0://退出程序{printf("正在退出程序...");//1s后退出程序Sleep(1000);exit(0);break;}default:{UserHelp();}}//开始 PING Ping(ptr, log);//程序释放 Winsock 资源WSACleanup();}}

标签: 网络 windows

本文转载自: https://blog.csdn.net/KUMAKAIHA/article/details/128400556
版权归原作者 快跑有熊孩子 所有, 如有侵权,请联系我们删除。

“Ping程序的实现(计网课设)”的评论:

还没有评论