计算机网络课程设计Ping

本文介绍了如何在Windows 10环境下利用Codeblocks编写C++代码,通过Socket编程实现ICMP ping功能,包括IP和ICMP报头结构定义、数据包解码、校验和计算,以及处理ICMP回应和超时情况。
摘要由CSDN通过智能技术生成

一、题目

Ping

二、环境

win10    codeblocks

三、代码实现

#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>

using namespace std;
#pragma comment(lib, "Ws2_32.lib")
//IP 报头
typedef struct
{
    unsigned char hdr_len:4;         //4 位头部长度
    unsigned char version:4;         //4 位版本号
    unsigned char tos;             //8 位服务类型
    unsigned short total_len;         //16 位总长度
    unsigned short identifier;         //16 位标识符
    unsigned short frag_and_flags;     //3 位标志加 13 位片偏移
    unsigned char ttl;             //8 位生存时间
    unsigned char protocol;         //8 位上层协议号
    unsigned short checksum;         //16 位校验和
    unsigned long sourceIP;         //32 位源 IP 地址
    unsigned long destIP;         //32 位目的 IP 地址
} IP_HEADER;
//ICMP 报头
typedef struct
{

    BYTE type;     //8 位类型字段
    BYTE code;     //8 位代码字段
    USHORT cksum;     //16 位校验和
    USHORT id;     //16 位标识符
    USHORT seq;     //16 位序列号
} ICMP_HEADER;
//报文解码结构
typedef struct
{
    USHORT usSeqNo;         //序列号
    DWORD dwRoundTripTime;     //往返时间
    in_addr dwIPaddr;         //返回报文的 IP 地址
}DECODE_RESULT;
//计算网际校验和函数
USHORT checksum(USHORT *pBuf,int iSize)
{
    unsigned long cksum=0;
    while(iSize>1)
    {
        cksum+=*pBuf++;
        iSize-=sizeof(USHORT);
    }
    if(iSize)
    {
        cksum+=*(UCHAR *)pBuf;
    }
    cksum=(cksum>>16)+(cksum&0xffff);
    cksum+=(cksum>>16);
    return (USHORT)(~cksum);
}
//对数据包进行解码

BOOL DecodeIcmpResponse(char * pBuf,int iPacketSize,DECODE_RESULT &DecodeResult,BYTE
ICMP_ECHO_REPLY,BYTE   ICMP_TIMEOUT)
{
    //检查数据报大小的合法性
    IP_HEADER* pIpHdr = (IP_HEADER*)pBuf;
    int iIpHdrLen = pIpHdr->hdr_len * 4;
    if (iPacketSize < (int)(iIpHdrLen+sizeof(ICMP_HEADER)))
        return FALSE;
    //根据 ICMP 报文类型提取 ID 字段和序列号字段
    ICMP_HEADER *pIcmpHdr=(ICMP_HEADER *)(pBuf+iIpHdrLen);
    USHORT usID,usSquNo;
    if(pIcmpHdr->type==ICMP_ECHO_REPLY)     //ICMP 回显应答报文
    {
        usID=pIcmpHdr->id;         //报文 ID
        usSquNo=pIcmpHdr->seq;     //报文序列号
    }
    else if(pIcmpHdr->type==ICMP_TIMEOUT)//ICMP 超时差错报文
    {
        char * pInnerIpHdr=pBuf+iIpHdrLen+sizeof(ICMP_HEADER); //载荷中的 IP 头
        int iInnerIPHdrLen=((IP_HEADER *)pInnerIpHdr)->hdr_len*4; //载荷中的 IP 头长
        ICMP_HEADER * pInnerIcmpHdr=(ICMP_HEADER *)(pInnerIpHdr+iInnerIPHdrLen);//载荷中的 ICMP 头
        usID=pInnerIcmpHdr->id;         //报文 ID
        usSquNo=pInnerIcmpHdr->seq;     //序列号
    }
    else
    {
        return false;
    }
    //检查 ID 和序列号以确定收到期待数据报
    if(usID!=(USHORT)GetCurrentProcessId()||usSquNo!=DecodeResult.usSeqNo)
    {
        return false;
    }

    //记录 IP 地址并计算往返时间
    DecodeResult.dwIPaddr.s_addr=pIpHdr->sourceIP;
    DecodeResult.dwRoundTripTime=GetTickCount()-DecodeResult.dwRoundTripTime;
    //处理正确收到的 ICMP 数据报
    if (pIcmpHdr->type == ICMP_ECHO_REPLY ||pIcmpHdr->type == ICMP_TIMEOUT)
    {
        //输出往返时间信息
        if(DecodeResult.dwRoundTripTime)
            cout<<"    在线     ping: "<<DecodeResult.dwRoundTripTime<<"<"<<"ms"<<">"<<flush;
        else
            cout<<"    在线    "<<"<1ms"<<">"<<flush;
    }
    return true;
}
int main()
{
    //初始化 Windows sockets 网络环境
    WSADATA wsa;
    WSAStartup(MAKEWORD(2,2),&wsa);
    char IpAddress[255];
    //cout<<"请输入起始IP地址或域名:";
    //cin>>IpAddress;
    //for(int i=51;i<=81;i++) {
    for(int i=30;i<=81;i++) {
        //string s="10.1.12.";
        string s="10.152.110.";
        s+=to_string(i);
        //得到 IP 地址
        strcpy(IpAddress, s.c_str());
        u_long ulDestIP = inet_addr(IpAddress);
        cout<<IpAddress ;
        //转换不成功时按域名解析
        if (ulDestIP == INADDR_NONE) {
            hostent *pHostent = gethostbyname(IpAddress);
            if (pHostent) {
                ulDestIP = (*(in_addr *) pHostent->h_addr).s_addr;
            } else {
                cout << "输入的 IP 地址或域名无效!" << endl;
                WSACleanup();
                //return 0;
            }
        }

        //填充目地端 socket 地址
        sockaddr_in destSockAddr;
        ZeroMemory(&destSockAddr, sizeof(sockaddr_in));
        destSockAddr.sin_family = AF_INET;
        destSockAddr.sin_addr.s_addr = ulDestIP;
        //创建原始套接字
        SOCKET sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0,
                                   WSA_FLAG_OVERLAPPED);
        //超时时间
        int iTimeout = 3000;
        //接收超时
        setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char *) &iTimeout, sizeof(iTimeout));
        //发送超时
        setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char *) &iTimeout, sizeof(iTimeout));
        //构造 ICMP 回显请求消息,并以 TTL 递增的顺序发送报文
        //ICMP 类型字段
        const BYTE ICMP_ECHO_REQUEST = 8;     //请求回显
        const BYTE ICMP_ECHO_REPLY = 0;     //回显应答
        const BYTE ICMP_TIMEOUT = 11;         //传输超时
        //其他常量定义
        const int DEF_ICMP_DATA_SIZE = 32;     //ICMP 报文默认数据字段长度
        const int MAX_ICMP_PACKET_SIZE = 1024;//ICMP 报文最大长度(包括报头)
        const DWORD DEF_ICMP_TIMEOUT = 3000;     //回显应答超时时间
        const int DEF_MAX_HOP = 50;             //最大跳站数
        //填充 ICMP 报文中每次发送时不变的字段

        char IcmpSendBuf[sizeof(ICMP_HEADER) + DEF_ICMP_DATA_SIZE];//发送缓冲区
        memset(IcmpSendBuf, 0, sizeof(IcmpSendBuf));         //初始化发送缓冲区
        char IcmpRecvBuf[MAX_ICMP_PACKET_SIZE];         //接收缓冲区
        memset(IcmpRecvBuf, 0, sizeof(IcmpRecvBuf));         //初始化接收缓冲区
        ICMP_HEADER *pIcmpHeader = (ICMP_HEADER *) IcmpSendBuf;
        pIcmpHeader->type = ICMP_ECHO_REQUEST;             //类型为请求回显
        pIcmpHeader->code = 0;                 //代码字段为 0
        pIcmpHeader->id = (USHORT) GetCurrentProcessId();     //ID 字段为当前进程号
        memset(IcmpSendBuf + sizeof(ICMP_HEADER), 'E', DEF_ICMP_DATA_SIZE);//数据字段
        USHORT usSeqNo = 0;             //ICMP 报文序列号
        int iTTL = 30;             //TTL 初始值为 1
        BOOL bReachDestHost = FALSE;     //循环退出标志
        int iMaxHot = DEF_MAX_HOP;     //循环的最大次数
        DECODE_RESULT DecodeResult;     //传递给报文解码函数的结构化参数
        while (!bReachDestHost && iMaxHot--) {
            int flag=0;
            //设置 IP 报头的 TTL 字段
            setsockopt(sockRaw, IPPROTO_IP, IP_TTL, (char *) &iTTL, sizeof(iTTL));
            //cout<<iTTL<<flush;     //输出当前序号
            //填充 ICMP 报文中每次发送变化的字段
            ((ICMP_HEADER *) IcmpSendBuf)->cksum = 0;     //校验和先置为 0
            ((ICMP_HEADER *) IcmpSendBuf)->seq = htons(usSeqNo++);     //填充序列号
            ((ICMP_HEADER *) IcmpSendBuf)->cksum = checksum((USHORT *) IcmpSendBuf,
                                                            sizeof(ICMP_HEADER) + DEF_ICMP_DATA_SIZE); //计算校验和
            //记录序列号和当前时间
            DecodeResult.usSeqNo = ((ICMP_HEADER *) IcmpSendBuf)->seq;     //当前序号
            DecodeResult.dwRoundTripTime = GetTickCount();         //当前时间
            //发送 TCP 回显请求信息
            sendto(sockRaw, IcmpSendBuf, sizeof(IcmpSendBuf), 0, (sockaddr *) &destSockAddr, sizeof(destSockAddr));
            //接收 ICMP 差错报文并进行解析处理
            sockaddr_in from;         //对端 socket 地址
            int iFromLen = sizeof(from);     //地址结构大小
            int iReadDataLen;         //接收数据长度
            while (1) {
                //接收数据
                iReadDataLen = recvfrom(sockRaw, IcmpRecvBuf, MAX_ICMP_PACKET_SIZE, 0, (sockaddr *) &from, &
                        iFromLen);
                if (iReadDataLen != SOCKET_ERROR)//有数据到达
                {
                    //对数据包进行解码
                    if (DecodeIcmpResponse(IcmpRecvBuf, iReadDataLen, DecodeResult, ICMP_ECHO_REPLY, ICMP_TIMEOUT)) {
                        //到达目的地,退出循环
                        if (DecodeResult.dwIPaddr.s_addr == destSockAddr.sin_addr.s_addr)
                            bReachDestHost = true;
                        //输出 IP 地址
                        cout << "\t  目的IP为:" << inet_ntoa(DecodeResult.dwIPaddr) << endl;
                        break;
                    }
                } else if (WSAGetLastError() == WSAETIMEDOUT)     //接收超时,输出*号
                {
                    flag=1;
                    cout  << "    不在线" << endl;
                    break;
                } else {
                    break;
                }
            }
            //iTTL++;     //递增 TTL 值
            if(flag==1)
                break;
        }
    }
}


四、实验结果

结果

目 录 摘 要 1 关键词: 计算机网络网络规划;网络 ;校园网 1 一. 前言 2 二. 学校描述 3 三. 需求分析 4 3.1 带宽 4 3.2 子网与VLAN规划 4 3.3 实现的信息服务 4 3.4 应用程序 5 3.5 存储系统分析 5 3.6 系统及数据安全分析 5 3.7 QoS 5 3.8 网间隔离 6 四. 拓扑图及方案整体描述 7 4.1 主干网传输方案 7 4.2 Internet接入方案 7 4.3 远程访问支持 8 4.4 子网划分 9 4.5 网间隔离方案 12 4.6存储方案 13 4.7 备选型 13 4.8 软件 14 4.9 信息服务方案 14 4.10 综合布线方案 15 4.10.1 结构化布线 15 4.10.2 综合布线系统 16 4.10.3目标 17 4.10.4工作区 18 4.10.6主干线区 19 4.10.7子配线间 20 4.10.8主配线间 21 五. 网络管理 22 5.1网络管理 22 5.2远程监控 22 5.2.1 制作客户端安装盘 22 5.2.2 配置客户机 23 5.2.3远程控制 23 5.3网络安全 24 六. 系统主要备报价 26 七. 网络测试及协议数据包分析 27 参考资料 31 致谢 32 摘 要 当今的世界正从工业化社会向信息化社会转变。一方面,社会经济已由基于资源的经济逐渐转向基于知识的经济,人们对信息的需求越来越迫切,信息在经济的发展中起着越来越重要的作用,信息的交流成为发展经济最重要的因素。另一方面,随着算机、网络和多媒体等信息技术的飞速发展,信息的传递越来越快捷,信息的处理能力越来越强,信息的表现形式也越来越丰富,对社会经济和人们的生活产生了深刻的影响。网上教育以受众广、投入低、不受师资的校舍等条件限制、容易开展高水平教育、教学质量相对容易保证等特点而受到教育界的广泛重视,目前国内一些重事业高校的网站已经开展这方面的应用。但现在中小学的校园网正在建阶段。中小学建校园计算机网络的根本动机,就是提高学校的管理效益和教学质量。而并非只有大量的资金投入,建具有规模的计算机网络,才能开展学校的教育手段。架满足学校应用需求的小的局域网络、教学网络同样也能发挥大的教育效益。 网络是信息时代的产物,互联网本身以及各种基于互联网的应用都成了学校教学的内容,这就需要一个平台支持实施这种教学活动.校园网就是利用学校原有的内部局域网, ,通过架WEB服务器、FTP服务器、论坛、在线流媒体等,从而组建起一个校园网络,促进学校教育的发展. 关键词: 计算机网络网络规划;网络 ;校园网 一. 前言 在二十一世纪的现代信息社会中,对于网络办公和学习是越来越受到重视很运用,几乎在全球的绝大多数学校都组建了网络环境。在通信备不断普及的今天,原始的教学方式已经不能完全满足我们学习和生活的需求。为了提高教育和学习的质量,所有师生对网络办公和学习的需求是迫在眉睫,所有人都希望在校园里的都能上网络。正所谓大有大的难处,校园网一般具有较大的规模,它不是网络备的简单组合,而是一种整体的校园系统。校园网必须满足校园扩展需要,确保高吞吐量、安全性。在很多时候,由于校园网络的种种问题的产生,以及学校对网络的需求随着学校的扩招却迅速增大。因此迫切需要在保障学生的学习和生活不受到影响的前提下,通过引进先进的组网模式,改造或者重新规划建校园网络,以满足学校对网络的需求,保障学校的正常运转,同时实现教育信息化的目标。要求全新的校园网络能够最大限度的保护现有网络投资,以最低的造价实现信息化校园网络的标准,以满足日益增长的校园建需求;建成后的网络应易于应用、管理和维护;施工过程中不能影响学校的正常运转,所提供的方案最好具有推广的价值,或者能够有效的降低网络接入与运行费用,保障大多数学生、教职员工都能够有条件接入、使用网络,提高学习与工作效率,实现教育的信息化。鉴于现在大多数校园都已经普及了有线网络,为了保障投资和提高网络的利用率以及最大限度的保障网络的健壮性,我在下面的方案中主要是以校园网组网为主。为所有师生提供最好的网络接入方式。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值