用MFC实现的路由分组转发模拟系统
来源:互联网 发布:位图制作软件 编辑:程序博客网 时间:2024/06/07 02:10
系统设计
网络拓扑
三个主机,三个路由(两两相通)
每个主机有一个IP地址和一个端口号。
每个路由维护一个路由表,表中存有到达特定网络终端(目标主机)的路径(下一(设备(路由))跳(的地址)),如下图所示。
此系统每个路由的路由表皆为事先设定,上图为路由1的路由表,规定到达目标主机Host3和Host2需要经过路由2,自身与Host1直接相连。
系统原理
分组转发原理
IP数据包也叫IP报文分组,传输在ISO网络7层结构中的网络层,它由IP报文头和IP报文用户数据组成,IP报文头的长度一般在20到60个字节之间,而一个IP分组的最大长度则不能超过65535个字节。 下图为IP分组的报文头格式,报文头的前20个字节是固定的,后面的可变。
可以看到报文头中包含一个32位源IP地址与一个32位目的IP地址,路由器通过取得报文头中的目的IP地址与路由表中存有的目的主机地址进行匹配找出对应的路径(下一跳),然后将数据包转发至找到的下一跳。匹配不成功则将数据报丢弃。
比如主机1(Host1)发出一个分组,与主机1直接相连的路由1收到这个分组,取得分组中的目的主机为Host3,通过路由表知道下一跳为路由2,所以路由1将把分组转发给路由2。同理,路由2收到路由1发来的分组,提取目的主机,再由路由表获得下一跳为路由3,于是将分组转发为路由3,路由3收到分组后直接发给与自身直接相连的主机3。
socket编程
步骤
1.#include <winsock2.h> //socket通信,系统头文件
2.加载套接字库
WSADATA wsaData; //存储被WSAStartup函数调用后返回的Windows sockets数据if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){ MessageBox("WSAStartup ERROR!");}
3.创建套接字
//创建套接字UDP连接,AF_INET表示使用IPV4协议,SOCK_DGRAM是无连接报文类型,默认UDP协议if ((m_sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET){ MessageBox("ERROR: Create Server Socket Error!"); exit(-1);}
4.绑定套接字到一个IP地址和一个端口上
struct sockaddr_in serAddr;serAddr.sin_family = AF_INET;serAddr.sin_port = htons(setting->m_routerPort);(serAddr.sin_addr).s_addr = htonl(INADDR_ANY);if((bind(m_sock, (LPSOCKADDR)&serAddr, sizeof(serAddr))) == SOCKET_ERROR){ MessageBox("ERROR: Bind Socket Error!"); exit(-1);}
struct sockaddr 是一个通用地址结构,这是为了统一地址结构的表示方法,统一接口函数,使不同的地址结构可以被bind() , connect() 等函数调用;struct sockaddr_in中的in 表示internet,就是网络地址。
sin_family指代协议族,在socket编程中只能是AF_INET。
sin_port存储端口号(使用网络字节顺序)。
sin_addr存储IP地址。
s_addr按照网络字节顺序存储IP地址。
htonl()将主机的无符号长整形数转换成网络字节顺序。
htons()将主机的无符号短整形数转换成网络字节顺序。
ps:网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。
5.创建并运行一个线程循环从(已连接)套接口上接收数据。
AfxBeginThread(RecvThread, this, THREAD_PRIORITY_NORMAL, 0, 0, NULL);
- 路由的RecvThread函数
UINT RecvThread(LPVOID lpParm){ CRouterDlg *dlg = (CRouterDlg*)lpParm; char gcInBuffer[1027]; int length; int size = sizeof(sockaddr_in); CString strReceive, tempStr; while (1) { if ((length = recvfrom(dlg->m_sock, gcInBuffer, 1024, 0, (struct sockaddr *)&dlg->m_desAddr, &size))>0) { CString strReceive; gcInBuffer[length] = '\0'; strReceive += gcInBuffer; CString temp; dlg->m_message.GetWindowTextA(temp); CString originalHost; CString data; CString desHost; AfxExtractSubString(originalHost, strReceive, 0, '/'); AfxExtractSubString(data, strReceive, 1, '/'); AfxExtractSubString(desHost, strReceive, 2, '/'); temp += originalHost; temp += "->"; temp += desHost; temp += ":"; temp += data; temp += "\r\n"; dlg->m_message.SetWindowText(temp); unsigned short aimPort; if (desHost.Compare(dlg->setting->m_host) == 0) aimPort = dlg->setting->m_hostPort; else if(desHost.Compare(dlg->setting->routing1.aimHost)==0) aimPort = dlg->setting->routing1.nextHopPort; else aimPort = dlg->setting->routing2.nextHopPort; struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(aimPort); addr.sin_addr.s_addr = inet_addr(dlg->setting->m_routerIP); sendto(dlg->m_sock, strReceive, strlen(strReceive), 0, (struct sockaddr*)&addr, sizeof(sockaddr)); } } return 0;}
“数据包”中原始地址,数据,目的地址简单地使用’/’进行分隔,有固定的位置但没有固定的长度,实际请见开头的报文头格式图,源IP地址,目的IP地址及数据都有固定的位置及长度,并不需要使用分隔符。
- 主机的RecvThread函数
UINT RecvThread(LPVOID lpParm){ CHostDlg *dlg = (CHostDlg*)lpParm; char gcInBuffer[1027]; int length; int size = sizeof(sockaddr_in); while (1) { if ((length = recvfrom(dlg->m_sock, gcInBuffer, 1024, 0, (struct sockaddr *)&dlg->m_routerAddr, &size))>0) { CString strReceive; gcInBuffer[length] = '\0'; strReceive += gcInBuffer; CString temp; dlg->m_message.GetWindowTextA(temp); temp += "收到来自"; CString originalHost; CString data; AfxExtractSubString(originalHost, strReceive, 0, '/'); AfxExtractSubString(data, strReceive, 1, '/'); temp += " "; temp += originalHost; temp += " "; temp += "的数据"; temp += " "; temp += data; temp += "\r\n"; dlg->m_message.SetWindowText(temp); } } return 0;}
运行效果
Host1发送消息“Hi”到Host2
Host1发送消息“Hello”到Host3
Host2发送消息“Bye”到Host1
Host3发送消息“Bye”到Host1
源码
源码已上传请点击 CSDN资源或Github下载
运行前请先找到setting.txt并打开对系统进行配置(一到6行依次是主机1到3,路由1到3的IP地址,第七行为当前主机或路由序号,取值1到3)
代码有问题的地方以及说错的地方还请指出,谢谢!
有任何不解的地方请留言,很乐意为您解答。
欢迎转载,但请附上原地址http://blog.csdn.net/jiaxingzheng/article/details/44262551,谢谢!
- 用MFC实现的路由分组转发模拟系统
- 路由分组转发算法
- 模拟网络通信中存储转发的分组交换算法
- iptables实现路由转发
- 【网络基础】路由表,分组转发算法
- 路由模拟——路由算法2的实现
- 用IP转发和Route路由实现跨网段互访
- QT-Socket编程之路由分组转发仿真
- 转发和路由的区别
- linux系统打开路由转发功能
- 开启Linux系统路由转发功能 实现多网段电脑共享上网
- 路由模拟——路由算法1的实现(路由基因类)
- 路由模拟——路由算法1的实现(路由计算类1)
- 路由模拟——路由算法1的实现(路由计算类2)
- 路由模拟——路由算法1的实现(路由计算类1)
- 转发分组
- Asp.net 路由系统的实现
- Javascript 实现一个原生的路由系统
- linux内核之旅---"hello world"模块
- 2014年总结
- Flume把命令执行结果写入平面文件
- android studio 项目 转换成eclipse
- d3力场Force Layout
- 用MFC实现的路由分组转发模拟系统
- Winforms: DataGridView的显示刷新问题
- del_substr,C和指针,第六章第二题解答
- listview中radiobutton的选择问题
- Quartz.NET学习系列(四)--- 数据注入
- 进程和线程有什么区别
- struts是什么
- 滚动到页面底端,加载新内容——例子
- Class 找出一个整形数组中的元素的最大值