网络编程实验_ping实验报告

网络编程-PING程序设计实验指导书

一.实验目的

(1)熟悉原始套接字编程。

(2)了解网络的结构。

(3)了解网络传输底层协议。

二.实验要求

PING程序是用于测试网络连通性的程序。要求在WINDOWS环境下实现基本的PING程序功能.

在命令提示符下输入:

      PING ***.***.***.***

其中***为目的主机的IP地址,不要求支持域名,对是否带有开关变量也不做要求。不带开关变量时,要求返回4次响应。

返回信息的格式:

      REPLY FROM ***.***.***.***

      REQUEST TimeOut  (无法PING通的情况)

三.实验原理

1、PING的工作原理

ping 程序是用来探测主机到主机之间是否可通信,如果不能ping到某台主机,表明不能和这台主机建立连接。ping 使用的是ICMP协议,它发送ICMP回送请求消息给目的主机。ICMP协议规定:目的主机必须返回ICMP回送应答消息给源主机。如果源主机在一定时间内收到应答,则认为主机可达。

ICMP协议通过IP协议发送的,IP协议是一种无连接的,不可靠的数据包协议。因此,保证数据送达的工作应该由其他的模块来完成。其中一个重要的模块就是ICMP(网络控制报文)协议。

当传送IP数据包发生错误--比如主机不可达,路由不可达等等,ICMP协议将会把错误信息封包,然后传送回给主机。给主机一个处理错误的机会,这也就是为什么说建立在IP层以上的协议是可能做到安全的原因。ICMP数据包由8bit的错误类型和8bit的代码和16bit的校验和组成。而前 16bit就组成了ICMP所要传递的信息。

PING利用ICMP协议包来侦测另一个主机是否可达。原理是用类型码为0的ICMP发请求,受到请求的主机则用类型码为8的ICMP回应。ping程序来计算间隔时间,并计算有多少个包被送达。用户就可以判断网络大致的情况。

IP数据报

     TCP/IP协议定义了一个在因特网上传输的包, 称为IP数据报(IP datagram). 这是一个与硬件无关的虚拟包, 由包头和数据两部分组成, 包头中的源地址和目的地址都是IP协议地址.

    

    

ICMP

    TCP/IP组件包括一个ICMP(Internet Control Message Protocol)协议, 该协议定义了的报文类型: Echo,Echo Reply,用于ping程序的基本实现

下图是ICMP报文的传送:

2、RAW模式的SOCKET编程

  PING程序是面向用户的应用程序,该程序使用ICMP的封装机制,通过IP协议来工作。为了实现直接对IP和ICMP包进行操作,实验中使用RAW模式的SOCKET编程。

熟悉SOCKET的编程,包括基本的系统调用如SOCKET、BIND等.

3、具体内容

(1) 定义数据结构

     需要定义好IP数据报、ICMP包等相关的数据结构

(2) 程序实现

     在WINDOWS环境下实现PING程序

四. 实验步骤和注意事项

1、 熟悉IP以及ICMP协议的工作机制

2、 熟悉RAW模式的SOCKET编程

3、编写PING的实现程序

4、编译环境中需要包括SOCKET库 WS2_32.lib

5、 在模拟实现环境下调试并运行自己编写的PING程序

6、最后提交源程序,撰写实验报告

    

初步流程图      

具体步骤

    1、定义IP头和ICMP头

       该程序定义自己的IP头和ICMP头数据结构,代码如下:

       //IP首部数据结构

       typedef struct iphdr

{

           unsigned int h_len : 4 ; //首部长度

           unsigned int version : 4 ; //版本

           unsigned char tos ; //服务类型

           unsigned short total_len ; //报文总长度

           unsigned short ident ; //标识

           unsigned short frag_and_flags ; //偏移量

           unsigned char ttl ; //寿命

           unsigned char proto ; //协议

           unsigned short checksum ;// 首部校验和

           unsigned int sourceIP ;// 源站IP

           unsigned int destIP ;// 目的站IP

};

//ICMP首部数据结构

typedef struct icmphdr

{

    BYTE i_type ; //类型

    BYTE i_code ; //代码

    USHORT i_cksum ; //首部校验和

    USHORT i_id ; //标识

    USHORT i_seq ; //序列号

    ULONG timestamp ; //时间戳

};

      2.定义变量

WSADATA wsaData ;

SOCKET sockRaw ;

struct sockaddr_in dest,from ;

struct hostent*hp ;

int bread,datasize=32,packnum=6;

int fromlen=sizeof(from);

int timeout=1000 ;

int statistic=0 ;

int ts=0;

char*dest_ip ;

char*icmp_data ;

char*recvbuf ;

unsigned int addr=0 ;

USHORT seq_no=0 ;

int temp;

3、定义函数:

   (1)void fill_icmp_data(char*icmp_data,int datasize,int ts);

//填充ICMP报头

void fill_icmp_data(char*icmp_data,int datasize,int ts)

{

    IcmpHeader*icmp_hdr ;

    char*datapart ;

    icmp_hdr=(IcmpHeader*)icmp_data ;

    icmp_hdr->i_type=ICMP_ECHO ;

    icmp_hdr->i_code=0 ;

    icmp_hdr->i_id=(USHORT)GetCurrentProcessId();

    icmp_hdr->i_cksum=0 ;

    icmp_hdr->i_seq=0 ;

    icmp_hdr->timestamp=ts;

    datapart=icmp_data+sizeof(IcmpHeader);   

    memset(datapart,'E',datasize-sizeof(IcmpHeader));

}

(2)USHORT checksum(USHORT*buffer,int size);

//计算ICMP首部校验和

USHORT checksum(USHORT*buffer,int size)

{

    unsigned long cksum=0 ;

    while(size>1)

    {

        cksum+=*buffer++;

        size-=sizeof(USHORT);

    }

    if(size)

    {

        cksum+=*(UCHAR*)buffer ;

    }

    cksum=(cksum>>16)+(cksum&0xffff);

    cksum+=(cksum>>16);

    return(USHORT)(~cksum);

}

(3)int decode_resp(char*buf,int bytes,struct sockaddr_in*from);

//解析收到的数据包

int decode_resp(char*buf,int bytes,struct sockaddr_in*from)

{

    IpHeader*iphdr ;

    IcmpHeader*icmphdr ;

    unsigned short iphdrlen ;

    iphdr=(IpHeader*)buf ;

    iphdrlen=(iphdr->h_len)*4 ;   

    if(bytes<iphdrlen+ICMP_MIN)

    {

       cout<<"Too few bytes from "<<inet_ntoa(from->sin_addr)<<endl;

    }

    icmphdr=(IcmpHeader*)(buf+iphdrlen);

    if(icmphdr->i_type!=ICMP_ECHOREPLY)

    {

       cout<<"non-echo type "<<icmphdr->i_type<<" recvd\n";

        return 1 ;

    }

    if(icmphdr->i_id!=(USHORT)GetCurrentProcessId())

    {

       cout<<"someone else's packet!\n";

        return 1 ;

    }

    bytes-=32;

   cout<<"Reply from "<<inet_ntoa(from->sin_addr)<<":"<<" bytes:"<<bytes;

   cout<<" packnum = "<<icmphdr->i_seq+1<<".";

   cout<<" time: "<<GetTickCount()-icmphdr->timestamp<<" ms "<<endl;  

    return 0 ;

}  

   4.主函数的具体流程(int main(

     1)原始套接字的创建:

SOCKET sockRaw ;

sockRaw=WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL,0,WSA_FLAG_OVERLAPPED;  

if(sockRaw==INVALID_SOCKET)

{

cout<<"WSASocket() failed: "<<WSAGetLastError()<<endl;

   ExitProcess(STATUS_FAILED);

}

     2)定义套接字选项

bread=setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,sizeof(timeout));如果没有出现错误,setsocket返回值为0;

3)定义sockaddr_in类型的dest,from两个变量。

        sock addr_in结构是专门被Windows Sockets用来详细说明套接字连接地址的。     

struct sockaddr_in dest,from;

struct hostent*hp;

char*dest_ip ;//用来放要ping的目的地址。

if(hp!=NULL)

        memcpy(&(dest.sin_addr),hp->h_addr,hp->h_length);

       else

         dest.sin_addr.s_addr=addr ;

        if(hp)

       dest.sin_family=hp->h_addrtype ;

       else

       dest.sin_family=AF_INET ;

dest_ip=inet_ntoa(dest.sin_addr);

4)创建ICMP包

    datasize+=sizeof(IcmpHeader);

    icmp_data=(char*)xmalloc(MAX_PACKET);

    recvbuf=(char*)xmalloc(MAX_PACKET);

    if(!icmp_data)

    {      

      cout<<"HeapAlloc failed "<<GetLastError()<<endl;

        ExitProcess(STATUS_FAILED);

    }   

    memset(icmp_data,0,MAX_PACKET);

    fill_icmp_data(icmp_data,datasize,ts);//函数已经在前面定义

5)发送和接收ICMP包

   for(int i=0;i < packnum;++i)

    {

        int bwrote ;

        ((IcmpHeader*)icmp_data)->i_cksum=0 ;

        ((IcmpHeader*)icmp_data)->timestamp=GetTickCount();

        ((IcmpHeader*)icmp_data)->i_seq=seq_no++;

     ((IcmpHeader*)icmp_data)->i_cksum=checksum((USHORT*)icmp_data,datasize);

//checksum用来计算校验和,函数已经在前面定义。

        bwrote=sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*)&dest,sizeof(dest));//sendto用来发送数据到指定的地址。

        if(bwrote==SOCKET_ERROR)

        {

            if(WSAGetLastError()==WSAETIMEDOUT)

            {              

              cout<<"Request timed out.\n";

                continue ;

            }            

          cout<<"sendto failed: "<<WSAGetLastError()<<endl;

            ExitProcess(STATUS_FAILED);

        }

        if(bwrote<datasize)

        {

           

          cout<<"Wrote "<<bwrote<<" bytes\n";

        }

        bread=recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct sockaddr*)&from,&fromlen);// recvfrom用来接收数据并保存

        //bread changed

        if(bread==SOCKET_ERROR)

        {

            if(WSAGetLastError()==WSAETIMEDOUT)

            {               

              cout<<"Request timed out.\n";

                continue ;

            }           

          cout<<"recvfrom failed: "<<WSAGetLastError()<<endl;

            ExitProcess(STATUS_FAILED);

          }

        if(!decode_resp(recvbuf,bread,&from))

 //decode_resp用来解析收到的ICMP数据包,函数已经在前面定义。

            statistic++;

      

          Sleep(1000);

       }

6)输出统计结果

五、程序实现的结果:

     在运行程序时,要选择菜单“工程→设置 →Link”,在对象/库模块中添加Ws2_32.lib,如下图:

 

在DOS窗口下到ping.exe的当前目录下,输入ping,如下图:

 

若输入ping 172.24.218.166,如下图:

 

若输入ping 172.24.218.166 –l 88 –n 8,如下图:

若输入ping 172.24.218.166 –s 20## –w 20## –l 88 –n 8,如下图:

 

输入ping 172.22.218.18,如下图:

 

 

第二篇:ping实验报告

        

相关推荐