您的位置:首页 > 理论基础 > 计算机网络

计算机网络实验五:写一个PING程序

2017-11-07 20:01 351 查看
一、作业要求

  1.ping的gui程序

  2.作业内要求有可执行的exe,作业链接,Readme,gif图,以及源代码

二、完成步骤

  1.创建类型为SOCK_RAW的一个套接字,同时设定协议*IPPROTO_ICMP。

  2.创建并初始化ICMP头。

  3.调用sendto或WSASendto,将ICMP请求发给远程主机。

  4.调用recvfrom或WSARecvfrom,以接收任何ICMP响应。

三、代码实现

#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#pragma  comment (lib, "ws2_32.lib")

#define ICMP_ECHOREPLY  0  // ICMP回复应答
#define ICMP_ECHOREQ    8  // ICMP回应请求
#define REQ_DATASIZE 32     // 请求数据报大小

// 定义IP首部格式
typedef struct _IPHeader
{
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;       // 首部校验和
struct  in_addr SrcIP;  // 源IP地址
struct  in_addr DestIP; // 目的地址
}IPHDR, *PIPHDR;

// 定义ICMP首部格式
typedef struct _ICMPHeader
{
u_char  Type;           // 类型
u_char  Code;           // 代码
u_short Checksum;       // 首部校验和
u_short ID;             // 标识
u_short Seq;            // 序列号
char    Data;           // 数据
}ICMPHDR, *PICMPHDR;

// 定义ICMP回应请求
typedef struct _ECHOREQUEST
{
ICMPHDR icmpHdr;
DWORD   dwTime;
char    cData[REQ_DATASIZE];
}ECHOREQUEST, *PECHOREQUEST;

// 定义ICMP回应答复
typedef struct _ECHOREPLY
{
IPHDR   ipHdr;
ECHOREQUEST echoRequest;
char    cFiller[256];
}ECHOREPLY, *PECHOREPLY;

// 计算校验和
u_short checksum(u_short *buffer, int len)
{
register int nleft = len;
register u_short *w = buffer;
register u_short answer;
register int sum = 0;
// 使用32bit的累加器,进行16bit的反馈计算
while( nleft > 1 )  {
sum += *w++;
nleft -= 2;
}
// 补全奇数位
if( nleft == 1 ) {
u_short u = 0;

*(u_char *)(&u) = *(u_char *)w ;
sum += u;
}
// 将反馈的16bit从高位移至地位
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16);         /* add carry */
answer = ~sum;             /* truncate to 16 bits */
return (answer);
}

// 发送回应请求函数
int SendEchoRequest(SOCKET s,struct sockaddr_in *lpstToAddr)
{
static ECHOREQUEST echoReq;
static nId = 1;
static 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 < REQ_DATASIZE; nRet++)
echoReq.cData[nRet] = ' '+nRet;

// 储存发送的时间
echoReq.dwTime = GetTickCount();

// 计算回应请求的校验和
echoReq.icmpHdr.Checksum = checksum((u_short *)&echoReq, sizeof(ECHOREQUEST));

// 发送回应请求
nRet = sendto(s,                        // 建立起的套接字
(LPSTR)&echoReq,           // 发送的缓冲区内容
sizeof(ECHOREQUEST),
0,                         // 标志位
(struct sockaddr*)lpstToAddr, // 发送的目标地址
sizeof(SOCKADDR_IN));   // 地址结构长度

if (nRet == SOCKET_ERROR)
{
printf("sendto() error:%d\n",WSAGetLastError());
}
return (nRet);
}

// 接收应答回复并进行解析
DWORD RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL)
{
ECHOREPLY echoReply;
int nRet;
int nAddrLen = sizeof(struct sockaddr_in);

//接收应答回复
nRet = recvfrom(s,                  // 接收的套接字
(LPSTR)&echoReply,  // 接收的缓冲区
sizeof(ECHOREPLY),  // 缓冲区长度
0,                  // 标识
(LPSOCKADDR)lpsaFrom,   // 接收的地址
&nAddrLen);         // 地址结构长度

// 检验接收结果
if (nRet == SOCKET_ERROR)
{
printf("recvfrom() error:%d\n",WSAGetLastError());
}
// 记录返回的TTL
*pTTL = echoReply.ipHdr.TTL;
//返回应答时间
return(echoReply.echoRequest.dwTime);
}

// 等待回应答复,使用select机制
int WaitForEchoReply(SOCKET s)
{
struct timeval timeout;
fd_set readfds;

readfds.fd_count = 1;
readfds.fd_array[0] = s;
timeout.tv_sec = 5;
timeout.tv_usec = 0;

return(select(1, &readfds, NULL, NULL, &timeout));
}

// Ping功能实现
void Ping(char *pstrHost)
{
SOCKET    rawSocket;
LPHOSTENT lpHost;
struct    sockaddr_in destIP;
struct    sockaddr_in srcIP;
DWORD     dwTimeSent;
DWORD     dwElapsed;
u_char    cTTL;
int       nLoop;
int       nRet;

// 创建原始套接字,ICMP类型
rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (rawSocket == SOCKET_E
ad2e
RROR)
{
printf("socket() error:%d\n",WSAGetLastError());
return;
}

// 检测目标主机
lpHost = gethostbyname(pstrHost);
if (lpHost == NULL)
{
printf("Host not found: %s\n", pstrHost);
return;
}

// 设置目标机地址
destIP.sin_addr.s_addr = *((u_long FAR *) (lpHost->h_addr));
destIP.sin_family = AF_INET;
destIP.sin_port = 0;

// 提示开始进行Ping
printf("\nPinging %s with %d bytes of data:\n",
inet_ntoa(destIP.sin_addr),
REQ_DATASIZE);

// 发起多次Ping测试
for (nLoop = 0; nLoop < 4; nLoop++)
{
//发送ICMP回应请求
SendEchoRequest(rawSocket, &destIP);

// 使用select()等待回复的数据
nRet = WaitForEchoReply(rawSocket);
if (nRet == SOCKET_ERROR)
{
printf("select() error:%d\n",WSAGetLastError());
break;
}
if (!nRet)
{
printf("\nRequest time out");
break;
}

//接收回复
dwTimeSent = RecvEchoReply(rawSocket, &srcIP, &cTTL);

// 计算花费的时间
dwElapsed = GetTickCount() - dwTimeSent;
printf("\nReply from %s: bytes=%d time=%ldms TTL=%d",
inet_ntoa(srcIP.sin_addr),
REQ_DATASIZE,
dwElapsed,
cTTL);
}

printf("\n");
nRet = closesocket(rawSocket); // 关闭套接字,释放资源
if (nRet == SOCKET_ERROR)
{
printf("closesocket() error:%d\n",WSAGetLastError());
}
}

void main(int argc, char **argv)
{
WSADATA wsd;
int nRet;

// 检测输入的参数
if (argc != 2)
{
printf("\nUsage: ping hostname\n");
return;
}
// 初始化Winsock
if (WSAStartup(MAKEWORD(1,1), &wsd) != 0)
{
printf("加载Winsock失败!\n");
}

//开始Ping
Ping(argv[1]);

// 释放Winsock资源
WSACleanup();
}


四、实现图例

  1.


  2.


五、程序实现文件链接

http://pan.baidu.com/s/1cdeQKI

六、参考博文

http://blog.sina.com.cn/s/blog_47120f8f0101fjul.html

http://blog.csdn.net/ivy8966/article/details/54612601?locationNum=6&fps=1

http://blog.csdn.net/qq_36249516/article/details/78467009

http://download.csdn.net/download/qq_36249516/10107387

七、总结与体会

由于最近在积极准备软件评测师的考试,所以没能好好静下心来做这次ping程序的实验,大部分借鉴了此次实验其他参与人员的实验流程,希望有机会完成软考资格考试的时候,再来细致地做这个实验,看起来很有意思!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  计算机网络-ping