「Ping」WinSock实作Ping

2024-05-01 03:18
文章标签 ping winsock 实作

本文主要是介绍「Ping」WinSock实作Ping,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

// IP 结构

typedef struct _iphdr

{

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

    unsigned int   version:4;      // IP协议版本

    unsigned char  tos;            // 服务类型

    unsigned short total_len;      // 总数据包大小

    unsigned short ident;          // 唯一识别符

    unsigned short frag_and_flags; // 旗标

    unsigned char  ttl;            // TTL生存时间

    unsigned char  proto;          // 协议 (TCP, UDP )

    unsigned short checksum;       // IP 检验和

 

    unsigned int   sourceIP;       // IP地址

    unsigned int   destIP;         // 目的IP地址

} IpHeader;

 

// ICMP 结构

typedef struct _icmphdr

{

    BYTE   i_type;                 // ICMP 报文类型

    BYTE   i_code;                 // ICMP 代码

    USHORT i_cksum;                // 检验和

    USHORT i_id;                   // 标识符

    USHORT i_seq;                  // 序列号

    // 预留空间

    ULONG  timestamp;              // 时间戳

} IcmpHeader;

 

//    构造ICMP回应请求报头

void  FillICMPData(char *icmp_data, int datasize)

{

    IcmpHeader *icmp_hdr = NULL;

    char       *datapart = NULL;

 

    icmp_hdr = (IcmpHeader*)icmp_data;

    icmp_hdr->i_type = 8;        // 请求一个ICMP回应

    icmp_hdr->i_code = 0;        // 代码字段 0

    icmp_hdr->i_id = (USHORT)GetCurrentProcessId();  // 获取当前进程一个唯一的标识符,填充为ICMP标识符

    icmp_hdr->i_cksum = 0;       // 检验和置零

    icmp_hdr->i_seq = 0;         // 序列号

    // icmp_data 加上头部大小,datapart就指向ICMP数据包中的具体内容

    datapart = icmp_data + sizeof(IcmpHeader); 

    // 在数据包正文中填充无用字符 E

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

}

 

//  计算检验和(各字节相加,再取反)

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

}

 

// 解析ICMP回应报头

INT DecodeICMPHeader(char *buf, int bytes)

{

    IpHeader       *iphdr = NULL;

    IcmpHeader     *icmphdr = NULL;

    unsigned short  iphdrlen;

    DWORD           tick;

 

    tick = GetTickCount();

    iphdr = (IpHeader *)buf;

    // Number of 32-bit words * 4 = bytes

    iphdrlen = iphdr->h_len * 4;

    // 回应的包字节不够

    if (bytes  < (iphdrlen + 8)) return -1;

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

    // 回应类型表示未成功

    if (icmphdr->i_type != 0) return -1;

    // 唯一标识号不匹配,别人的包

    if (icmphdr->i_id != (USHORT)GetCurrentProcessId()) return -1;

   

    // 返回耗费时间,毫秒

    return tick - icmphdr->timestamp;

}

 

INT  Ping(LPSTR IP)

{

    WSADATA            wsaData;

    SOCKET             sockRaw;

    sockaddr_in        dest;

    INT                bread,

                       datasize = 32,   // 默认包大小

                       timeout  = 1000; // 超时时间

    CHAR              *icmp_data = NULL,

                      *recvbuf = NULL;

    USHORT             seq_no = 0;

 

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) return -1;

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

    if (sockRaw == INVALID_SOCKET)

    {

        if (WSAGetLastError() == 10013) 

        { 

            WSACleanup();

            return -2;  // 创建原始套接字权限不够

        }

        else{ 

            WSACleanup();

            return -3;  // 未知错误

        }

       

    }

 

    bread = setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));

    if(bread == SOCKET_ERROR)

    {

        closesocket(sockRaw);

        WSACleanup();

        return -4;   // 设置套接字选项失败

    }

    bread = setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));

    if(bread == SOCKET_ERROR)

    {

        closesocket(sockRaw);

        WSACleanup();

        return -4;   // 设置套接字选项失败

    }

    memset(&dest, 0, sizeof(dest));

    dest.sin_family = AF_INET;

    dest.sin_addr.S_un.S_addr = inet_addr(IP);

 

    datasize += sizeof(IcmpHeader);

 

    icmp_data = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,1024);

    recvbuf = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,1024);

    if (!icmp_data)

    {

        closesocket(sockRaw);

        WSACleanup();

        return -5;  // 堆内存分配失败

    }

    if (!recvbuf)  

    {

        HeapFree(GetProcessHeap(), 0, recvbuf);

        closesocket(sockRaw);

        WSACleanup();

        return -5;  // 堆内存分配失败

    }

   

    memset(icmp_data,0,1024);

    FillICMPData(icmp_data,datasize);

 

    INT nCount,utime;  // 尝试 ping 4

    for (nCount = 0;nCount != 4; nCount++)

    {

        INT bwrote;

        ((IcmpHeader*)icmp_data)->i_cksum = 0;  // 检验和置零

        ((IcmpHeader*)icmp_data)->i_seq = seq_no++;  // ICMP包序列号

        // 返回从操作系统启动到现在所经过的毫秒数

        ((IcmpHeader*)icmp_data)->timestamp = GetTickCount(); // 本地发送时间戳

        // 计算检验和(注意这里,一定要最后计算,哈哈~我为了让时间更准确,就犯了这个错误)

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

 

        bwrote = sendto(sockRaw, icmp_data, datasize, 0, (sockaddr*)&dest, sizeof(dest));

        if (bwrote == SOCKET_ERROR)

        {

            if (WSAGetLastError() == WSAETIMEDOUT)  // 超时

                continue;

            HeapFree(GetProcessHeap(), 0, recvbuf);

            HeapFree(GetProcessHeap(), 0, icmp_data);

            closesocket(sockRaw);

            WSACleanup();

            return -6;   // 发送ICMP报头失败

        }

        bread = recv(sockRaw, recvbuf, 1024, 0);

        if (bread == SOCKET_ERROR)

        {

            if (WSAGetLastError() == WSAETIMEDOUT)  // 超时

                continue;

            HeapFree(GetProcessHeap(), 0, recvbuf);

            HeapFree(GetProcessHeap(), 0, icmp_data);

            closesocket(sockRaw);

            WSACleanup();

            return -7;   // 接收ICMP回应失败

        }

        utime = DecodeICMPHeader(recvbuf,bread);

        if (utime != -1)

            break;

        Sleep(300);

    }

 

    closesocket(sockRaw);

    HeapFree(GetProcessHeap(), 0, recvbuf);

    HeapFree(GetProcessHeap(), 0, icmp_data);

    WSACleanup();

    if(nCount == 4)

        return -8;  // 超时

    else

        return utime;  // 成功,返回耗时

}

 


 

   使用原始套接字需要管理员权限.不然就会返回10013错误代码.

这篇关于「Ping」WinSock实作Ping的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/950605

相关文章

C# 如何同时Ping多个IP地址

在C#中,如果需要同时ping多个IP地址,可以采用多线程或异步编程的方式来实现,以便可以同时进行多个ping操作。以下是两种常用的方法: 方法一:使用多线程(Task 或 Thread) 使用Task是更现代和推荐的方式,因为它内置了更好的线程管理和异常处理机制。以下是一个使用Task的示例,展示如何同时ping多个IP地址: using System; using System.Co

Winsock服务器内存资源管理

一般来讲, 在服务器上,如果有足够的资源,Winsock server,理论上可以支持成千的并发连接。而现实是,我们没有足够的资源可供使用,分配。本文主要来讨论一下内存资源之于Winsock server开发的重要性。 一)基本概念。 -> Pages,Locked Pages.         在现代操作系统中,内存管理会把主存(RAM)分成Pages来管理。 Paging(或者swap

Linux Ubuntu 能PING IP但不能PING主机域名的解决方法

------------------------------------------------------------------------------- ------------------------------------------------------------------------------- vi /etc/nsswitch.conf hosts: file

linux下 ping: unknown host www.baidu.com” 解决方法

问题现象 :   ping 和 telnet 都无法正常使用   而nslookup 可以正常解析到域名 $ ping  www.baidu.com  ping: unknown host  www.baidu.com $ telnet baidu.com 80  baidu.com/80: Name or service not known

深入理解 Linux 的 ping、telnet 和 curl 命令

深入理解 Linux 的 ping、telnet 和 curl 命令 在 Linux 系统中,网络调试和测试是日常运维工作的重要部分。ping、telnet 和 curl 是常用的网络命令工具,用于测试网络连接、访问远程服务器和发送 HTTP 请求等操作。本文将详细介绍这些命令的语法、使用场景和区别,尤其是 curl 命令中的 GET 和 POST 请求。 1. ping 命令 1.1

虚拟机无法查看ip地址,ping不通百度的解决思路

一、虚拟机centos中ifconfig看不到ens33的ip地址解决方案 1、检查网络适配器是否为"桥接模式"     2、查看网络配置文件 cd /etc/sysconfig/network-scripts/ vi ifcfg-ens33   其中HWADDR,为ifconfig查看到的ens33网口的物理地址 将ONBOOT设置为yes 重启网络:service ne

主机与虚拟机都可以上网,但是互相ping不通

问题:主机与虚拟机都可以上网,但是互相ping不通 可能:相关入站规则没有启用 解决:第4步双击后,打勾设置“已启用”

Centos 开放端口 查看 防火墙 ping telnet

centos中安装telnet yum install telnet   1、开放端口 firewall-cmd --zone=public --add-port=5672/tcp --permanent   # 开放5672端口 firewall-cmd --zone=public --remove-port=5672/tcp --permanent  #关闭5672端口 fir

路由表和三层转发(ping过程)详解

1、路由表概述        路由表的表项根据设备不同,可能是不同的。但是目的IP地址、网络掩码、网关、接口、标志这几项是必须存在的。例如跃点数metric,引用次数ref等在不同设备上可能不同。 1.1、表项理解的问题 诸如IP地址、网络掩码等很好理解,那么网关和接口表项使用来做什么的呢? (1)      网关:是下一站路由器的IP地址 (2)      接口:是将数据报从哪个接口发出

VM虚拟机:虚拟机能ping通主机,主机ping不通虚拟机,永久解决办法。

最近在安装VM虚拟机的时候,出现了虚拟机能ping通主机,主机ping不通虚拟机。着实令人恶心,尤其是虚拟机在设置网络的时候,网上五花八门,修改什么配置的都有,最多的就是修改宿主机的ipv4,这种我个人感觉不可取。宿主机不要乱改配置,需要修改尽量在虚拟机中修改即可。         还需要注意一点关键点,想要用永久解决一定是宿主机和虚拟机在同一网段下,很多同学上来就是一顿ping