计算机网络 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 语言标准呢? 我们生活的现实世界,就是由各种标准构成的,正是这些标准,我们的社会才会有条不紊的运行. 比如我们过马路,遵循的交通规则就是一个标准:红灯停,绿灯行,黄 ...
随机推荐
- dotNET开发之MVC中Controller返回值类型ActionResult方法总结
1.返回ViewResult视图结果,将视图呈现给网页 2. 返回PartialViewResult部分视图结果,主要用于返回部分视图内容 3. 返回ContentResult用户定义的内容类型 4. ...
- 1组-Alpha冲刺-5/6
一.基本情况 队名:震震带着六菜鸟 组长博客:https://www.cnblogs.com/Klein-Wang/p/15564206.html 小组人数:7人 二.冲刺概况汇报 王业震 过去两天完 ...
- shell脚本训练
*注:shell中的/data目录为自创练习目录 1,编写脚本systeminfo.sh,显示当前主机系统信息.包括主机名,IPV4地址,操作系统版本,内核版本,cpu型号,内存大小,硬盘大小 结果: ...
- [bzoj1101]Zap
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 50001 4 int t,n,m,k,ans,mu[N],vis ...
- Vulnhub-Empire: Breakout题解
Vulnhub-Empire: Breakout题解 这是Empire系列的靶机Breakout,地址:Vulnhub-EMPIRE: BREAKOUT 目标扫描 使用arp-scan 命令识别靶机I ...
- 软虹sdk基本使用
虹软SDK的简单使用 Java实现人脸识别,但是又不会自己实现算法,找SDK时发现了虹软.虹软SDK具有免费.识别率高等优点,然后到网上搜这个SDK的教程,没搜到,就自己探索,发现它自带的官方文档其实 ...
- SQLite3 约束和简单命令
安装sqlite3,配置环境变量. 1.打开数据库 sqlite3.exe db_name.db 2.常用命令 .tables 查看所有表 .headers on 设置显示表头 .schema tab ...
- 【6】蛋白质组学鉴定定量软件之MaxQuant
目录 1.简介 2.下载安装 3.配置与运行 4.结果 5.Perseus后处理 6.小结 1.简介 2016年,德国马普所的Cox和蛋白质组学领域巨擘Matthias Mann合作开发了MaxQua ...
- Oracle、MySQL关机操作步骤
一.Oracle数据库单机关机(eg:LEAP系统) 先关闭使用数据库的应用系统,再关闭数据库 关闭数据库执行以下命令 1.关闭Oracle数据库监听器:(使用操作系统下管理Oracle的账户,关闭监 ...
- grep -r
今晚改脚本 我发现了一个很有趣的事情,一共56个配置文件 1 # 注意:对一些参数一致的多个文件可以用此方法 2 # grep -r 查找文件内容,其中PARALLEL=2就是我要替换的内容 3 4 ...