计算机网络 Raw_Socket编程 Ping C语言
计算机网络做了一个附加题,用C语言Raw_Socket实现ping指令。
通过本部的Mooc学习了一下Socket编程,然后成功写了出来orz
先放一下代码:
#include <stdio.h>
#include <winsock2.h>
#include <time.h>
#include <iostream>
#include <windows.h>
#include <Mmsystem.h>
#pragma comment(lib,"winmm.lib")
#pragma comment(lib,"ws2_32.lib") using namespace std;
const int IP_Header_min_lenght=20;
/*
Ver 4
Hlen 4
1
Servicetype 8
1
Total length 16
2
Identifier 16
2
flag and frag offset 16
2
TimeToLive 8
1
Prorocol 8
CHECKSUM 16
SRC ADDR 32
DES ADDR 32
160/8=20
*/
typedef struct icmp_hdr
{
unsigned char icmp_type; // 类型
unsigned char icmp_code; // 代码
unsigned short icmp_checksum; // 校验和
unsigned short icmp_id; // ID号,设为PID
unsigned short icmp_sequence; // 序列号
unsigned long icmp_timestamp; // 时间戳
} ICMP_HDR, *PICMP_HDR; unsigned short checksum(USHORT* buff, int size)
{
unsigned long cksum = 0;
while(size>1)
{
cksum += *buff++;
size -= sizeof(USHORT);
}
// 是奇数
if(size)
{
cksum += *(UCHAR*)buff;
}
// 将32位的chsum高16位和低16位相加,然后取反
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
return (USHORT)(~cksum);
} bool SetTimeout(SOCKET sRaw, int nTime)
{
int ret = setsockopt(sRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&nTime, sizeof(nTime));
return ret != SOCKET_ERROR;
} double GetTickCountA()
{
__int64 Freq = 0;
__int64 Count = 0;
if(QueryPerformanceFrequency((LARGE_INTEGER*)&Freq)&& Freq > 0&& QueryPerformanceCounter((LARGE_INTEGER*)&Count))
{
//乘以1000,把秒化为毫秒
return (double)Count / (double)Freq * 1000.0;
}
return 0.0;
}
int main()
{ char website_name[]="www.google.com";// www.baidu.com www.google.com
char *DestIp;
double succ=0;
double fail=0;
int min_RTT=999999999;
int max_RTT=-1;
int sum_RTT=0;
DestIp = (char*)malloc(20*sizeof(char));
//初始化WindowsSocketsAPI
WSADATA wsaData;
WORD sockVersion = MAKEWORD(2,2);//声明版本 2.2
WSAStartup(sockVersion, &wsaData);//启动
//获取IP地址
struct hostent *curr_hostent = gethostbyname(website_name);
DestIp = inet_ntoa( *(struct in_addr*)curr_hostent->h_addr_list[0] );
//DestIp = "192.168.88.1";
printf("正在ping %s:[%s] \n",website_name,DestIp);
//printf("%s\n",curr_hostent->h_addr_list[0]);
//printf("%s\n",inet_ntoa(*(struct in_addr*)curr_hostent->h_addr_list[0]));
if(!curr_hostent){
puts("Get IP address error!");
//system("pause");
// exit(0);
}
//创立套接字
SOCKET sRaw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);//AF_INET=ipv4 SOCK_RAW ICMP协议
SetTimeout(sRaw, 1000);
// 设置目的地址
SOCKADDR_IN dest;
dest.sin_family = AF_INET;//ipv4地址家族
dest.sin_port = htons(0);//将整型变量从主机字节顺序转变成网络字节顺序
dest.sin_addr.S_un.S_addr = inet_addr(DestIp);
// 创建ICMP数据包并填写
char buff[sizeof(ICMP_HDR) + 32];
ICMP_HDR* pIcmp = (ICMP_HDR*)buff;
pIcmp->icmp_type = 8; // 请求一个ICMP回显
pIcmp->icmp_code = 0;
pIcmp->icmp_id = (unsigned short)GetCurrentProcessId();
unsigned short nSeq = 0;
char recvBuf[1024];
SOCKADDR_IN from;
int nLen = sizeof(from);
for(int o=0;o<10;o++)
{
int nRet;
pIcmp->icmp_checksum = 0;//为了后续checksum函数正常 需要先置位0
pIcmp->icmp_timestamp = GetTickCountA();
pIcmp->icmp_sequence = nSeq++;
pIcmp->icmp_checksum = checksum((unsigned short*)buff, sizeof(ICMP_HDR) + 32);
//发送包
nRet = sendto(sRaw, buff, sizeof(ICMP_HDR) + 32, 0, (SOCKADDR *)&dest, sizeof(dest));//发送
if(nRet == SOCKET_ERROR)
{
printf(" sendto() failed: %d \n", ::WSAGetLastError());
return -1;
}
//接收包
nRet = recvfrom(sRaw, recvBuf, 1024, 0, (sockaddr*)&from, &nLen);
if(nRet == SOCKET_ERROR)
{
if(WSAGetLastError() == WSAETIMEDOUT)
{
printf(" Request timed out\n");
fail++;
continue;
}
fail++;
printf(" Failed. Error code: %d\n", WSAGetLastError());
return -1;
}
// 解析包
int nTick = GetTickCountA();
if(nRet < IP_Header_min_lenght + sizeof(ICMP_HDR))
{
printf(" Returned too few bytes from %s \n", inet_ntoa(from.sin_addr));
fail++;
}
// 接收包中包含IP头,所以加20得到ICMP头
ICMP_HDR* pRecvIcmp = (ICMP_HDR*)(recvBuf + IP_Header_min_lenght); // (ICMP_HDR*)(recvBuf + sizeof(IPHeader));
if(pRecvIcmp->icmp_type != 0) // 回显
{
if(pRecvIcmp->icmp_type == 3) // 回显
{
printf(" Unreachable\n");fail++;
}
else if(pRecvIcmp->icmp_type == 4) // 回显
{
printf(" Origin suppression \n");fail++;
}
else if(pRecvIcmp->icmp_type == 5) // 回显
{
printf(" Redirect \n");fail++;
}
else if(pRecvIcmp->icmp_type == 8) // 回显
{
printf(" Echo request\n");fail++;
}
else if(pRecvIcmp->icmp_type == 9) // 回显
{
printf(" Router announcement\n");fail++;
}
else if(pRecvIcmp->icmp_type == 10) // 回显
{
printf(" Router request \n");fail++;
}
else if(pRecvIcmp->icmp_type == 11) // 回显
{
printf(" Time out \n");fail++;
}
else if(pRecvIcmp->icmp_type == 17) // 回显
{
printf(" Address subnet request\n");fail++;
}
else if(pRecvIcmp->icmp_type == 18) // 回显
{
printf(" Address subnet response\n");fail++;
}
return -1;
}
succ++;
printf(" 来自 %s的回复: ", inet_ntoa(from.sin_addr));
printf(" 字节=%d bytes :", nRet, inet_ntoa(from.sin_addr));
printf(" 时间= %d ms", nTick - pRecvIcmp->icmp_timestamp);
printf(" TTL= %d \n", *(recvBuf+8));
min_RTT=min(min_RTT,(int)(nTick - pRecvIcmp->icmp_timestamp));
max_RTT=max(max_RTT,(int)(nTick - pRecvIcmp->icmp_timestamp));
sum_RTT=sum_RTT+nTick - pRecvIcmp->icmp_timestamp;
Sleep(1000);//一秒
}
printf("数据包丢失率:%lf",fail*100/(succ+fail));
cout<< "%"<<endl;
printf("最小 RTT = %d ms \n", min_RTT);
printf("最大 RTT = %d ms\n", max_RTT);
printf("平均 RTT = %d ms\n", sum_RTT/10); return 0;
}
还是算比较基础的吧,注释里面该有的都有啦~
主要就是socket编程和IP数据报的知识。
对于socket编程:我在编写raw_socket抓包程序时去听了本部计算机网络的MOOC,其中一章就是关于socket编程的。其中讲了声明winsocket版本、建立套接字、发送与接受等等等等函数。在编写程序时我们找到对应的函数调用即可。这里我有一个之前误解的地方:我原以为是gethostbyname将站点名翻译为32位点分地址,但在实际编写程序并调试后才发现是inet_ntoa的功劳。在了解了调用socket函数创立套接字、用gethostbyname和inet_ntoa获得ip地址、用sendto和recvfrom收发包后,接下来的工作就是填写数据包了。
我们需要写的是一个IP数据报,这与在创建socket时填写的AF_INET参数相一致。他在IP数据报的首部后的数据部分即为ICMP报文,中间有六个项,在接下来的核心代码注解中详细展示。我们需要创建一个ICMP报文大小+IC数据报头大小的缓冲区,填写每一段的内容后发送。这里有一个细节时为了正确计算校验和,需要先将其置零并填写完其余所有数据后再计算。在接受到返回的数据后,用与抓包程序类似的解析方式即可。
再放一些查资料找到的东西备用吧:
sin_zero用于填充结构体使之保持与struct sockaddr一样的长度。
(从onenote复制下来是张图片...
感谢本部mooc拯救我于苦海之中
计算机网络 Raw_Socket编程 Ping C语言的更多相关文章
- winsock教程- windows下的socket编程(c语言实现)
winsock教程- windows下的socket编程(c语言实现) 使用winsock进行socket 编程 这是一个学习windows下socket编程(c语言)的快速指南.这是因为一下 ...
- 计算机网络 & 网络编程 期末总结与测评题
第一部分:网络编程部分的相关知识 Socket套接字定义: 套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开.读写和关闭等操作.套接字允许应用程序 ...
- 我为什么反对推荐新人编程C/C++语言入门?
虽然我接触编程以及计算机时间比较早,但是正式打算转入程序员这个行当差不多是大学第四年的事情 从03年接触计算机,07年开始接触计算机编程, 期间接触过的技术包括 缓冲区溢出(看高手写的shellcod ...
- 结对编程项目——C语言实现WordCount Web化
结对编程项目 代码地址 201631062219,201631011410 gitee项目地址:https://gitee.com/xxlznb/pair_programming 作业地址:https ...
- extern的使用详解(多文件编程)——C语言
extern——关键字 extern是C语言中的一个关键字,一般用在变量名前或函数名前,作用是用来说明“此变量/函数是在别处定义的,要在此处引用”,extern这个关键字大部分读者应该是在变量的存储类 ...
- 编程基础-c语言中指针、sizeof用法总结
1.指针 学习 C 语言的指针既简单又有趣.通过指针,可以简化一些 C 编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的.所以,想要成为一名优秀的 C 程序员,学习指针是很有必要的. ...
- 第一章 T-SQL查询和编程基础 T-SQL语言基础(1)
T-SQL查询和编程基础(1) 1.1 理论背景 SQL是为查询和管理关系型数据库管理系统(RDBMS)中的数据而专门设计的一种标准语言. RDBMS是一种基于关系模型的数据库管理系统,关系模型则是一 ...
- 【自学编程】C语言编程简单的小程序,计算长方体体积!
计算长方体体积 有朋友会说长方体体积还不好算吗?长X宽X高.没错用计算器一下就可以出结果,编程反而麻烦些,但是我们说的是这种思维,如果复杂的重复运算的话写好程序就非常简单了. 简单运算下一个固定高度的 ...
- 【嵌入式】C语言高级编程▁▁▁嵌入式C语言入门编程学习!
✍ 1.C 语言标准 什么是 C 语言标准呢? 我们生活的现实世界,就是由各种标准构成的,正是这些标准,我们的社会才会有条不紊的运行. 比如我们过马路,遵循的交通规则就是一个标准:红灯停,绿灯行,黄 ...
随机推荐
- OpenCV常用操作函数大全
https://blog.csdn.net/Vici__/article/details/100714822 目录 cv2常用类: 1.图片加载.显示和保存 2.图像显示窗口创建与销毁 3.图片的常用 ...
- 【Python】300行代码搞定HTML模板渲染
一.前言 模板语言由HTML代码和逻辑控制代码组成,此处@PHP.通过模板语言可以快速的生成预想的HTML页面.应该算是后端渲染不可缺少的组成部分. 二.功能介绍 通过使用学习tornado.bott ...
- 推荐一款颜值逆天且功能齐全的开源Shell工具!
前言 以前在windows上一直使用的SSH工具是XShell,后来转到mac平台,XShell没有mac版本.所以之前一直在找一款颜值高,功能齐全的可以作为日常使用的Shell工具. 我知道mac下 ...
- Map、HashMap、Properties、TreeMap
1.掌握Map接口中常用方法. 2.遍历Map集合的两种方式都要精通. 第一种:获取所有key,遍历每个key,通过key获取value. 第二种:获取Set<Map.Entry>即可,遍 ...
- 用idea生成javadoc在线文档
1.点击需要生成文档的包 2.点击tools--->选择generate javaDoc /1选择输出目录 /2防止中文乱码建议加上: -encoding utf-8 -charset utf- ...
- Maven的pom.xml的格式与约束
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...
- 最强最全面的Hive SQL开发指南,超四万字全面解析
本文整体分为两部分,第一部分是简写,如果能看懂会用,就直接从此部分查,方便快捷,如果不是很理解此SQL的用法,则查看第二部分,是详细说明,当然第二部分语句也会更全一些! 第一部分: hive模糊搜索表 ...
- STL的equal_range()
equal_range()根据键值,返回一对迭代器的pair对象. 如果该键值在容器中存在,则pair对象中的第一个迭代器指向该键关联的第一个实例,第二个迭代器指向该键关联的最后一个实例的下一位置. ...
- MEGAN4,MEGAN5和MEGAN6的Linux安装和使用
目录 MEGAN 4 MEGAN 5 MEGAN 6 MEGAN(Metagenome Analyzer)是宏基因组学进行物种和功能研究的常用软件,实际上现在的Diamond+MEGAN6已经是一套比 ...
- JuiceFS 数据读写流程详解
对于文件系统而言,其读写的效率对整体的系统性能有决定性的影响,本文我们将通过介绍 JuiceFS 的读写请求处理流程,让大家对 JuiceFS 的特性有更进一步的了解. 写入流程 JuiceFS 对大 ...