需要测试外网的联通性,想到了用ping。网上下载了ping的源代码,调试下整理如下:

 /******************************************************************************\
* ping.c - Simple ping utility using SOCK_RAW
*
* This is a part of the Microsoft Source Code Samples.
* Copyright 1996-1997 Microsoft Corporation.
* All rights reserved.
* This source code is only intended as a supplement to
* Microsoft Development Tools and/or WinHelp documentation.
* See these sources for detailed information regarding the
* Microsoft samples programs.
\******************************************************************************/ #pragma pack(4) #define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment(lib,"ws2_32.lib") #define ICMP_ECHO 8
#define ICMP_ECHOREPLY 0 #define ICMP_MIN 8 // minimum 8 byte icmp packet (just header) /* The IP header */
typedef struct iphdr
{
unsigned int h_len:; // length of the header
unsigned int version:; // Version of IP
unsigned char tos; // Type of service
unsigned short total_len; // total length of the packet
unsigned short ident; // unique identifier
unsigned short frag_and_flags; // flags
unsigned char ttl;
unsigned char proto; // protocol (TCP, UDP etc)
unsigned short checksum; // IP checksum
unsigned int sourceIP;
unsigned int destIP;
}IpHeader; //
// ICMP header
//
typedef struct _ihdr {
BYTE i_type; //消息类型
BYTE i_code; //代码 /* type sub code */
USHORT i_cksum; //校验和
USHORT i_id; //ID号
USHORT i_seq; //序列号
ULONG timestamp; //时间戳 /* This is not the std header, but we reserve space for time */
}IcmpHeader; //ICMP报文 包括报头和数据 #define STATUS_FAILED 0xFFFF
#define DEF_PACKET_SIZE 32
#define MAX_PACKET 1024 #define xmalloc(s) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,(s))
#define xfree(p) HeapFree (GetProcessHeap(),0,(p)) void fill_icmp_data(char *, int);
USHORT checksum(USHORT *, int);
void decode_resp(char *,int ,struct sockaddr_in *); int main(int argc, char **argv)
{
WSADATA wsaData;
SOCKET sockRaw;
struct sockaddr_in dest;
struct hostent * hp;
int bread,datasize;
int timeout = ;
char *dest_ip;
char *icmp_data;
char *recvbuf;
unsigned int addr=;
USHORT seq_no = ;
struct sockaddr_in from;
int fromlen = sizeof(from); if (WSAStartup(MAKEWORD(,),&wsaData) != )
{
fprintf(stderr,"WSAStartup failed: %d\n",GetLastError());
ExitProcess(STATUS_FAILED);
} /*
为了使用发送接收超时设置(即设置SO_RCVTIMEO, SO_SNDTIMEO),
// 必须将标志位设为WSA_FLAG_OVERLAPPED !
*/
sockRaw = WSASocket (AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, ,WSA_FLAG_OVERLAPPED); //建立一个原始套接字
//sockRaw = WSASocket (AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0,0); if (sockRaw == INVALID_SOCKET)
{
fprintf(stderr,"WSASocket() failed: %d\n",WSAGetLastError());
ExitProcess(STATUS_FAILED);
} timeout = ; //设置接收超时时间
bread = setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout, sizeof(timeout)); //RECVTIMEO是接收超时时间
if(bread == SOCKET_ERROR)
{
fprintf(stderr,"failed to set recv timeout: %d\n",WSAGetLastError());
ExitProcess(STATUS_FAILED);
} timeout = ; //设置发送超时时间
bread = setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout, sizeof(timeout)); //SNDTIMEO是发送超时时间
if(bread == SOCKET_ERROR)
{
fprintf(stderr,"failed to set send timeout: %d\n",WSAGetLastError());
ExitProcess(STATUS_FAILED);
}
memset(&dest,,sizeof(dest)); //目标地址清零 hp = gethostbyname("www.baidu.com"); //通过域名或者主机名获取IP地址
if (!hp) //失败返回NULL
{
ExitProcess(STATUS_FAILED);
}
else
{
addr = inet_addr("14.215.177.37"); //www.baidu.com的ip地址
} if ((!hp) && (addr == INADDR_NONE)) //既不是域名也不是点分十进制的IP地址
{
ExitProcess(STATUS_FAILED);
} if (hp != NULL) //获取的是域名
memcpy(&(dest.sin_addr),hp->h_addr,hp->h_length); //从hostent得到的对方ip地址
else
dest.sin_addr.s_addr = addr; if (hp)
dest.sin_family = hp->h_addrtype; //sin_family不是一定只能填AF_INET吗?
else
dest.sin_family = AF_INET; dest_ip = inet_ntoa(dest.sin_addr); //目标IP地址 datasize = DEF_PACKET_SIZE; //ICMP包数据大小设定为32 datasize += sizeof(IcmpHeader); //另外加上ICMP包的包头 其实包头占12个字节 icmp_data = (char *)xmalloc(MAX_PACKET);//发送icmp_data数据包内存
recvbuf = (char *)xmalloc(MAX_PACKET); //存放接收到的数据 if (!icmp_data) //分配内存
{
ExitProcess(STATUS_FAILED);
} memset(icmp_data,,MAX_PACKET);
fill_icmp_data(icmp_data,datasize); //只填充了ICMP包 fprintf(stdout,"\nPinging %s ....\n\n",dest_ip); while()
{
int bwrote; ((IcmpHeader*)icmp_data)->i_cksum = ;
((IcmpHeader*)icmp_data)->timestamp = GetTickCount(); //时间戳 ((IcmpHeader*)icmp_data)->i_seq = seq_no++; //ICMP的序列号
((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data, datasize); //icmp校验位 //下面这个函数的问题是 发送数据只是ICMP数据包,而接收到的数据时包含ip头的 也就是发送和接收不对等
//问题是sockRaw 设定了协议为 IPPROTO_ICMP
bwrote = sendto(sockRaw,icmp_data,datasize,,(struct sockaddr*)&dest, sizeof(dest));
if (bwrote == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT) //发送时间超时
{
printf("timed out\n");
continue;
} fprintf(stderr,"sendto failed: %d\n",WSAGetLastError());
ExitProcess(STATUS_FAILED);
} if (bwrote < datasize )
{
fprintf(stdout,"Wrote %d bytes\n",bwrote);
} bread = recvfrom(sockRaw,recvbuf,MAX_PACKET,,(struct sockaddr*)&from, &fromlen);
if (bread == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
printf("timed out\n");
continue;
}
fprintf(stderr,"recvfrom failed: %d\n",WSAGetLastError());
ExitProcess(STATUS_FAILED);
}
decode_resp(recvbuf,bread,&from); Sleep();
} WSACleanup();
system("pause"); return ;
} /*
The response is an IP packet. We must decode the IP header to locate
the ICMP data
*/
void decode_resp(char *buf, int bytes,struct sockaddr_in *from)
{
IpHeader *iphdr;
IcmpHeader *icmphdr;
unsigned short iphdrlen; iphdr = (IpHeader *)buf; //接收到的数据就是原始的IP数据报 iphdrlen = iphdr->h_len * ; // number of 32-bit words *4 = bytes if (bytes < iphdrlen + ICMP_MIN)
{
printf("Too few bytes from %s\n",inet_ntoa(from->sin_addr));
} icmphdr = (IcmpHeader*)(buf + iphdrlen); if(icmphdr->i_type == )
{
printf("network unreachable -- Response from %s.\n",inet_ntoa(from->sin_addr));
return ;
} if (icmphdr->i_id != (USHORT)GetCurrentProcessId())
{
fprintf(stderr,"someone else's packet!\n");
return ;
}
printf("%d bytes from %s:",bytes, inet_ntoa(from->sin_addr));
printf(" icmp_seq = %d ",icmphdr->i_seq);
printf(" time: %d ms ",GetTickCount()-icmphdr->timestamp);
printf(" ttl: %d",iphdr->ttl);
printf("\n");
} //完成ICMP的校验
USHORT checksum(USHORT *buffer, int size)
{
unsigned long cksum=; while(size >)
{
cksum+=*buffer++;
size -=sizeof(USHORT);
} if(size )
{
cksum += *(UCHAR*)buffer;
} cksum = (cksum >> ) + (cksum & 0xffff);
cksum += (cksum >>);
return (USHORT)(~cksum);
} /*
Helper function to fill in various stuff in our ICMP request.
*/
void fill_icmp_data(char * icmp_data, int datasize){ IcmpHeader *icmp_hdr;
char *datapart; icmp_hdr = (IcmpHeader*)icmp_data; icmp_hdr->i_type = ICMP_ECHO; //ICMP_ECHO要求收到包的主机回复此ICMP包
icmp_hdr->i_code = ;
icmp_hdr->i_id = (USHORT)GetCurrentProcessId(); //id填当前进程的id
icmp_hdr->i_cksum = ;
icmp_hdr->i_seq = ; datapart = icmp_data + sizeof(IcmpHeader);
//
// Place some junk in the buffer.
//
memset(datapart,'E', datasize - sizeof(IcmpHeader)); //填充了一些废物
}

我下到代码的时候,第91行创建原始套接字的地方原本是被屏蔽的第92行,区别在与创建套接字时赋予的标志位不一样。

WSASocket函数的定义如下:

SOCKET WSASocket (
  int af,
  int type,
  int protocol,
  LPWSAPROTOCOL_INFO lpProtocolInfo,
  GROUP g,
  DWORD dwFlags
  );

af:[in]一个地址族规范。目前仅支持AF_INET格式,亦即ARPA Internet地址格式。

type:新套接口的类型描述。

protocol:套接口使用的特定协议,如果调用者不愿指定协议则定为0。

lpProtocolInfo:一个指向PROTOCOL_INFO结构的指针,该结构定义所创建套接口的特性。如果本参数非零,则前三个参数(af, type, protocol)被忽略。

g:保留给未来使用的套接口组。套接口组的标识符。

iFlags:套接口属性描述。

具体详细介绍看微软官方介绍文档:https://msdn.microsoft.com/en-us/library/ms742212(VS.85).aspx

Windows ping源码的更多相关文章

  1. E-Form++ for Windows CE源码库2020,嵌入式开放源码!

    E-Form++ for Windows CE源码库2020! 现在就把这个下载到您的Windows CE中,体验极致HMI触摸.  Windows CE评估版下载! 1. E-Form++ for ...

  2. yate: windows下源码下载,配置,编译

    源码下载:使用svn下载checkout:http://voip.null.ro/svn/yate/trunk 配置:(本人使用的是vs2008,故下载的qt工具都是对应2008) 1. 下载并安装q ...

  3. windows下源码安装调试postgresql

    环境:windows 10 postgresql版本:postgresql-9.6.5 使用工具:vs2017社区版 辅助工具:perl.diff.flex.bison 相关工具下载地址: perl下 ...

  4. windows平台源码编译最新版openssl

    本文有问题,待改中................. 1.从openssl官网下载最新版openssl      https://www.openssl.org/source/ The latest ...

  5. 32位win7+vs2008编译mysql 5.6.22源码并安装

    以下这部分安装说明是来自http://www.2cto.com/database/201407/316681.html的win7+vs2010源码编译mysql,文章最后会说明用vs2008编译遇见的 ...

  6. thingsboard源码编译启动

    开发环境 不同的版本对应的开发环境不同(这里以3.3.3版本说明) jdk11+:参考jdk11+安装(win) Maven3.6+:Maven安装配置 Git:参考Git安装 IDEA: 参考IDE ...

  7. [安卓]windows下如何安装Android源码

    本文改写于:http://www.cnblogs.com/skyme/archive/2011/05/14/2046040.html 1.下载并安装git: 在git-scm.com上下载并安装git ...

  8. 【完全开源】知乎日报UWP版(下篇):商店APP、github源码、功能说明。Windows APP 良心出品。

    目录 说明 功能 截图+视频 关于源码和声明 说明 陆陆续续大概花了一个月的时间,APP算是基本完成了.12月份一直在外出差,在出差期间进行了两次功能完善,然后断断续续修补了一些bug,到目前为止,我 ...

  9. Windows编译Nginx源码

    Windows下的Nginx战役,人不作就不会死!就像是拿着麦当劳的优惠券去买肯德基一样,别扭啊 Nginx是一款轻量级的Web 服务器.反向代理服务器.邮件服务器等等集一大串荣誉于一身的大牌人物!他 ...

随机推荐

  1. BZOJ 1879 [Sdoi2009]Bill的挑战 ——状压DP

    本来打算好好写写SDOI的DP题目,但是忒难了, 太难了,就写的这三道题仿佛是可做的. 生在弱省真是兴奋. 这题目直接状压,f[i][j]表示匹配到i,状态集合为j的方案数,然后递推即可. #incl ...

  2. [luoguP2606] [ZJOI2010]排列计数(DP)

    传送门 如果能够根据题意看出这是一个堆的话,那么就有些思路了.. 首先堆顶必须是最小元素,然后左右儿子可以预处理出来都有多少个数, 把剩余的数任意分配给两个儿子,用排列组合即可 dp(now) = d ...

  3. bzoj1853: [Scoi2010]幸运数字 dp+容斥原理

    在中国,很多人都把6和8视为是幸运数字!lxhgww也这样认为,于是他定义自己的“幸运号码”是十进制表示中只包含数字6和8的那些号码,比如68,666,888都是“幸运号码”!但是这种“幸运号码”总是 ...

  4. Codevs 1043 ==洛谷 P1004 方格取数

    题目描述 设有N*N的方格图(N<=9),我们将其中的某些方格中填入正整数,而其他的方格中则放 人数字0.如下图所示(见样例): A 0 0 0 0 0 0 0 0 0 0 13 0 0 6 0 ...

  5. echarts3样例

    <script type="text/javascript" src="echarts.min.js"></script> <di ...

  6. jstl 标签 的应用

    参考文档:http://www.yiibai.com/jsp/jstl_core_choose_tag.html 1.jstl中foreach序号 <c:forEach items=" ...

  7. spring-boot-nginx代理-docker-compose部署

    在本地测试,使用docker部署不用在意环境 java测试项目: web框架:spring boot 框架 项目管理:maven 数据库:redis + postgres + mongo 部署相关:n ...

  8. BZOJ——1649: [Usaco2006 Dec]Cow Roller Coaster

    http://www.lydsy.com/JudgeOnline/problem.php?id=1649 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 7 ...

  9. codevs——1570 去看电影

    1570 去看电影  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解       题目描述 Description 农夫约翰带着他的一些奶牛去看电影.而他的 ...

  10. 【SQL Server 学习系列】-- 获取字符串中出现某字符的次数及字符某次出现的下标

    ) = '1_BB_CC_DD_AA_EE_YY_WW_HH_GG' --// 1. 获取下划线在字符串中出现的次数 SELECT LEN(@Str) - LEN(REPLACE(@Str, '_', ...