注:源码等等的我不会全然公开的,此篇文章写出来为大家的网络编程或者课程设计提供一定的思路..

好,本次我们须要完毕的任务是:

完毕两台主机通过中间主机的数据通信(网络层)

  • 添加基于IP地址的转发功能
  • 添加网络层封装

事实上最基本的就是基于IP地址的转发功能,网络层的封装事实上我们在0基础功能中就已经做好了。

首先,实验的思路是A通过中间主机B向C发送数据。那么B则作为一个路由器,B要监听两个网卡,一个网卡发来的数据通过还有一个网卡发出去。

示意图例如以下:

A--------->B1===B2------------>C

从图上能够看出,B主机的两个网卡数据互通,A和B1则处于一个局域网内,B2和C处于还有一个局域网内。

就比方这样,如今室友A在用有线上网,我的电脑B也在用有线上网,我们的有线处在同一局域网,我的电脑B同一时候散着一个无线网,我的手机C又连接到了这个无线上。

那么要实现A到C的数据传送,即模拟室友A要发送数据到我的手机C,那么流程则是这种:

室友A在有线局域网发送数据到我的网卡B1,B1将数据通过网卡B2转发到无线局域网,通过无线局域网到达我的手机C。

A的发送要构建一个帧,目的MAC地址为B1,目的IP为C。B则要开启两个网卡,B1监听接收数据,B2网卡则要用ARP协议扫描所在无线局域网内的IP和MAC,B获取到了A发来的帧之后,解析它的IP地址和MAC地址,匹配刚才扫描得到的IP和MAC相应表,将源MAC换成B2网卡MAC,目的MAC换成C的MAC,IP不变,数据data不变。构建新帧之后发送出去。

好啦,思路大体就是这样。

须要三个程序,一个是发送,一个路由,一个接收。所以一共三个程序要同一时候执行起来执行。

以上是我的大体思路,如有错误,还请指正。现已用代码实现完成。

代码暂不公开,仅仅提供部分重点代码解析:

一、发送端

事实上发送端和0基础功能的发送差点儿相同

个人编写的交互流程例如以下:

IP地址:121.250.216.221   MAC地址:3c970e4b56d6con:127-------------------------------------------
IP地址:121.250.216.227   MAC地址:089e01b948f4con:128-------------------------------------------
IP地址:121.250.216.228   MAC地址:10bf48705aeecon:129获取MAC地址完成,请输入你要发送对方的IP地址:
192.168.1.3
请输入你要发送的内容:
im cqc
要发送的内容:im cqc

详细代码不再解析,同上一篇0基础功能。

二、路由端

首先要开启两个网卡,声明两个网卡对象和处理器

pcap_if_t  *d,*d2;                 //选中的网络适配器
pcap_t *adhandle,*adhandle2;           //捕捉实例,是pcap_open返回的对象,adhandle是用来发送数据,adhandle2是用来接收数据

一个用来接收一个用来发送,这里定义了adhandle是用来发送,adhandle2是用来接收数据。

那么打开适配器就在main方法中,提前打开两个网卡

int num;printf("请输入你要转发数据的网卡代号:\n");//让用户选择选择哪个适配器进行转发scanf_s("%d",&num);//跳转到选中的适配器for(d=alldevs, i=0; i< num-1 ; d=d->next, i++);//执行到此处说明用户的输入是合法的,找到发送数据网卡if((adhandle = pcap_open(d->name,     //设备名称65535,       //存放数据包的内容长度PCAP_OPENFLAG_PROMISCUOUS,  //混杂模式1000,           //超时时间NULL,          //远程验证errbuf         //错误缓冲)) == NULL){//打开适配器失败,打印错误并释放适配器列表fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);// 释放设备列表 pcap_freealldevs(alldevs);return -1;}int num2;printf("请输入你要接收数据的网卡代号:");//让用户选择用哪个网卡来收数据scanf_s("%d",&num2);//用户输入的数字超出合理范围//跳转到选中的适配器for(d2=alldevs, i=0; i< num2-1 ; d2=d2->next, i++);//执行到此处说明用户的输入是合法的if((adhandle2 = pcap_open(d2->name,     //设备名称65535,       //存放数据包的内容长度PCAP_OPENFLAG_PROMISCUOUS,  //混杂模式1000,           //超时时间NULL,          //远程验证errbuf         //错误缓冲)) == NULL){//打开适配器失败,打印错误并释放适配器列表fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n", d2->name);

接下来用用于发送的handle处理器来扫描它的局域网IP,获得局域网内的MAC地址,记录在一个表中,存放IP和MAC的相应关系。

这个表能够用结构体数组来保存,比方能够这样:

struct ip_mac_list{IpAddress ip;unsigned char mac[6];
};
ip_mac_list  list[256];                       //存储IP和MAC地址的相应表

那么以上便是准备工作,我们完毕了两个网卡的打开,发送网卡扫描获取局域网MAC,接下来便是最重要的监听加转发。

那么这个怎办?那就开一个新线程。

让我们声明一个新的路由线程。

DWORD WINAPI RouteThread(LPVOID lpParameter);

那么线程要接收进来什么參数呢?

首先必需要的是两个网卡处理器,在main方法中已经做好初始化的adhandle和adhandle2,另外还有alldevs,能够持有这个指针来释放设备列表,出现错误时释放资源并退出。

0基础功能中声明过了

struct sparam sp;
struct gparam gp;

这两个就是发送ARP线程和接收ARP线程中的两个參数,那么仿照这个功能,我们定义一个新的结构体

struct rparam{pcap_t *adhandle_rec;pcap_t *adhandle_send;pcap_if_t  * alldevs;       //全部网络适配器
};

在main方法中把它来初始化赋值

rp.adhandle_send = adhandle;rp.adhandle_rec = adhandle2;rp.alldevs = alldevs;

当做參数传入这个线程

routethread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) RouteThread, &rp,0, NULL);

当中第四个參数就是传递了这个结构体进去。注意这个语句最好不要直接放在main方法中直接调用,能够在所有获取完MAC地址之后再开启这个线程。

那么接下来就说一下这个线程都干了些什么,仅仅简略说一下核心部分。

首先开启了这个线程之后会一直都在运行,那么就能够增加

while((res = pcap_next_ex(adhandle2,&header,&pkt_data))>=0)

这种while推断语句来一直监听数据包的接收,然后解析数据。

ethernet =  (EthernetHeader *)(pkt_data);for(int i=0;i<6;i++){sou_mac[i] = ethernet->SourMAC[i];}for(int i=0;i<6;i++){des_mac[i] = ethernet->DestMAC[i];}// 获得IP数据包头部的位置ip = (IpHeader *) (pkt_data +14);    //14为以太网帧头部长度//获得TCP头部的位置ip_len = (ip->Version_HLen & 0xf) *4;tcp = (TcpHeader *)((u_char *)ip+ip_len);data = (char *)((u_char *)tcp+20);printf("data:%s\n",data);printf("ip:");printf("%d.%d.%d.%d -> %d.%d.%d.%d\n",ip->SourceAddr.byte1,ip->SourceAddr.byte2,ip->SourceAddr.byte3,ip->SourceAddr.byte4,ip->DestinationAddr.byte1,ip->DestinationAddr.byte2,ip->DestinationAddr.byte3,ip->DestinationAddr.byte4);printf("sou_mac:%02x-%02x-%02x-%02x-%02x-%02x\n", sou_mac[0], sou_mac[1], sou_mac[2],sou_mac[3], sou_mac[4], sou_mac[5]);printf("des_mac:%02x-%02x-%02x-%02x-%02x-%02x\n", des_mac[0], des_mac[1], des_mac[2],des_mac[3], des_mac[4], des_mac[5]);

然后接下来每接收到一个数据,就进行构建新的帧转发出去,目的MAC先匹配list表,假设list没有找到,那么我让他指定了一个mac,比方广播MAC。源MAC地址则赋值网卡的MAC地址。

注意,传统以太网中数据长度为45-1500,那么我在构建前把解析出的data作了下推断长度再构建,由于我已经把sendbuffer声明为一个固定长度了,为了防止越界,我先进行一个长度推断。

//下面開始构建帧发送//首先推断data最大值小于1500if(strlen(data)<1500){//目的MACBYTE send_destmac[6];bool findMac = false;for(int c = 0;c<con;c++){if(ip->DestinationAddr.byte1 ==  list[c].ip.byte1&&ip->DestinationAddr.byte2 == list[c].ip.byte2&&ip->DestinationAddr.byte3 == list[c].ip.byte3&&ip->DestinationAddr.byte4 == list[c].ip.byte4){printf("Find its MAC!\n");findMac = true;send_destmac[0] = list[c].mac[0];   send_destmac[1] = list[c].mac[1];send_destmac[2] = list[c].mac[2];send_destmac[3] = list[c].mac[3];send_destmac[4] = list[c].mac[4];send_destmac[5] = list[c].mac[5];}}if(!findMac){send_destmac[0] = 0xff;   send_destmac[1] = 0xff;   send_destmac[2] = 0xff;   send_destmac[3] = 0xff;   send_destmac[4] = 0xff;   send_destmac[5] = 0xff;   }printf("destmac:%02x-%02x-%02x-%02x-%02x-%02x\n",send_destmac[0],send_destmac[1],send_destmac[2],send_destmac[3],send_destmac[4],send_destmac[5]);memcpy(send_ethernet.DestMAC, send_destmac, 6);//源MAC地址BYTE send_hostmac[6];//源MAC地址send_hostmac[0] = local_mac[0];     //赋值本地MAC地址send_hostmac[1] = local_mac[1];send_hostmac[2] = local_mac[2];send_hostmac[3] = local_mac[3];send_hostmac[4] = local_mac[4];send_hostmac[5] = local_mac[5];//赋值源MAC地址memcpy(send_ethernet.SourMAC, send_hostmac, 6);send_ethernet.EthType = htons(0x0800);//赋值SendBuffermemcpy(&SendBuffer, &send_ethernet, sizeof(struct EthernetHeader));

以上仅仅是赋值了帧头,至于IP头,TCP头,数据的赋值就參照0基础功能的来赋值吧,不要忘了校验和的检验。好,大体上就是这样,接受来数据包并转发出去的原理就是这样。

三、接收

不用多改,就是0基础功能中的接收,在此写一写小小的优化措施,防止接收到过多的数据帧而造成不断乱蹦,导致你看不到接收的东西。

在打印的时候加一个过滤就好了。部分代码例如以下:

在main方法中提示用户输入要接收的IP地址

printf("请输入要接收的IP地址,输入0.0.0.0代表所有接收,请输入\n");bool receiveAll = false;u_int ip1,ip2,ip3,ip4;bool legal = false;while(!legal){scanf_s("%d.%d.%d.%d",&ip1,&ip2,&ip3,&ip4);if(ip1==0&&ip2==0&&ip3==0&&ip4==0){receiveAll = true;legal = true;break;}if(ip1<0||ip1>255||ip2<0||ip2>255||ip3<0||ip3>255||ip4<1||ip4>254){legal = false;printf("对不起,IP输入不合法,请又一次输入:\n");}else{legal = true;}}

打印时的推断

if(receiveAll||(ip->SourceAddr.byte1==ip1&&ip->SourceAddr.byte2==ip2&&ip->SourceAddr.byte3==ip3&&ip->SourceAddr.byte4==ip4)){printf("%d.%d.%d.%d.%d -> %d.%d.%d.%d.%d\n",ip->SourceAddr.byte1,ip->SourceAddr.byte2,ip->SourceAddr.byte3,ip->SourceAddr.byte4,sport,ip->DestinationAddr.byte1,ip->DestinationAddr.byte2,ip->DestinationAddr.byte3,ip->DestinationAddr.byte4,dport);printf("sou_mac:%02x-%02x-%02x-%02x-%02x-%02x\n", sou_mac[0], sou_mac[1], sou_mac[2],sou_mac[3], sou_mac[4], sou_mac[5]);printf("des_mac:%02x-%02x-%02x-%02x-%02x-%02x\n", des_mac[0], des_mac[1], des_mac[2],des_mac[3], des_mac[4], des_mac[5]);printf("%s\n",data);printf("-----------------------------------------------------\n");}

好,代码就先放送这么多,详细的实现仅仅要有了思路我相信肯定不难,如有问题,欢迎与我交流。

我的邮箱 1016903103@qq.com



转载于:https://www.cnblogs.com/mfrbuaa/p/4060226.html

Winpcap网络编程十之Winpcap实战,两台主机通过中间主机通信相关推荐

  1. Muduo 网络编程示例之五: 测量两台机器的网络延迟

    Muduo 网络编程示例之五: 测量两台机器的网络延迟 陈硕 (giantchen_AT_gmail) Blog.csdn.net/Solstice  t.sina.com.cn/giantchen ...

  2. Winpcap网络编程九之Winpcap实战,ARP协议获得MAC表及主机通信

    大家好,本次我们需要完成的任务是: 完成两台主机之间的数据通信(数据链路层) 仿真ARP协议获得网段内主机的MAC表 使用帧完成两台主机的通信(Hello! I'm -) 声明:本文章的目的是为大家的 ...

  3. WinPcap网络编程入门——0. 环境配置及系列介绍

    WinPcap网络编程入门--0. 环境配置及系列介绍 系列教程章节直达: Winpcap网络编程入门--1. 获取设备列表: 说明:本教程适用于网络编程开发人员入门,将从底层分析 WinPcap 的 ...

  4. WinPcap网络编程入门——1. 获取设备列表

    WinPcap网络编程入门--1. 获取设备列表 系列教程章节直达: Winpcap网络编程入门--1. 获取设备列表: 上节中我们简单介绍了 WinPcap 的相关资料,配置好了开发环境,现在就让我 ...

  5. 丰炜vb0和两台变频器的modbus rtu通信程序

    丰炜vb0和两台变频器的modbus rtu通信程序 史上最好用的plc modbus rtu通讯程序, 通讯效果如视频,控制,反馈无延迟,真心好用,别的变频器支持modbus rtu一样可以使用. ...

  6. 丰炜vb0和两台变频器的modbus rtu通信程序 史上最好用的plc modbus rtu通讯程序, 通讯效果如视频,控制,反馈无延迟

    丰炜vb0和两台变频器的modbus rtu通信程序 史上最好用的plc modbus rtu通讯程序, 通讯效果如视频,控制,反馈无延迟,真心好用,别的变频器支持modbus rtu一样可以使用id ...

  7. Python编程:从入门到实践+爬虫开发与项目实战+网络编程基础+项目开发实战

    给还在苦苦自学Python的小伙伴们分享一波学习教程~有了它们,至少能节省50%的时间,少走一半的弯路. 书不在多,而在于精~ <Python编程:从入门到实践>豆瓣评分9.2 本书是针对 ...

  8. Java【网络编程1】使用 UDP 的 Socket API 实现客户端服务器通信(保姆级教学, 附代码)

    文章目录 前言 一.认识 Socket(套接字), TCP 协议和 UDP 协议 1, 什么是 Socket(套接字) 2, 浅谈 TCP 协议和 UDP 协议的区别和特点 二.基于 UDP 协议的 ...

  9. 计算机双网口怎么共享网络,win7电脑怎么共享上网 两台电脑共享上网操作方法...

    电脑共享上网指的是多台设备通过共享网络资源,进行互联网访问的方式,在生活当中我们通常会碰到只有一根网线想让两台电脑或者多台电脑同时上网,那么win7电脑怎么共享上网呢?今天为大家分享两台电脑共享上网的 ...

最新文章

  1. linux diff diffstat组合使用示例
  2. DEEPIN 20安装镜像进入LIVE模式
  3. fastai学习:05_pet_breeds Questionnaire
  4. 此计算机无法设置密码,电脑该怎么设置密码
  5. bool转nsnumber ios_iOS开发之NSDecimalNumber的使用,货币计算/精确数值计算/保留位数等...
  6. android仿优酷菜单,Android编程实现仿优酷旋转菜单效果(附demo源码)
  7. 日志收集系统Elasticsearch,Fluentd , Kibana
  8. springboot定时备份MYSQL_spring boot 定时备份数据库
  9. VGG16系列I: 基于Tensorflow代码
  10. win10系统mysql重新配置密码
  11. Java+spring基于ssm的基于SSM的高校奖学金助学金管理系统
  12. 老有“美女”加你微信?大学生“艳遇”,结果吓一跳...
  13. 找到所有数组中消失的数字_【一点资讯】千万程序员的呼声:面试如何拿到大厂Offer?这份阅读量超过11W+的算法刷题宝典请你原地查收 www.yidianzixun.com...
  14. 日常运维工作的知识体系
  15. 海康监控视频无插件开发3.2版本运行demo
  16. ComicEnhancerPro 系列教程十八:JPG文件长度与质量
  17. PRML读书笔记(一)
  18. 【C++】limits头文件 numeric_limits
  19. 使用多张图片做帧动画的性能优化
  20. 模拟电子技术基础笔记(4)——晶体三极管

热门文章

  1. 图结构练习——BFS——从起始点到目标点的最短步数
  2. golang中的条件变量
  3. 台湾积体电路制造公司(简称为台积电(TSMC))的28nm LP、HPM、HPC、HPC+四种不同处理器工艺版本的区别?
  4. 串口初始化结构体和固件库讲解
  5. Python爬虫入门教程 33-100 《海王》评论数据抓取 scrapy
  6. ruby 正则表达式 使用正负预查
  7. Centos7安装mariadb galera cluster数据库集群 详解
  8. IOS开发笔记(Swift):UITableView表格视图的静态使用
  9. Linux运维工程师必备技能
  10. how to force opened by browser