用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,谢谢!

1 0
原创粉丝点击