在之前的博客《QT-Socket编程之模拟TCP五层协议解/封装》中已经提到过基本的关于QTsocket编程实现的简易C/S模型,此次博客内容是建立在C/S基础上的。
内容:
模拟实现网络中路由器进行分组转发的过程。至少模拟3个路由器,3个路由器两两相通。对应的网络拓扑为下图所示:
基本思路:
主机一与路由器一之间构成相互的CS模型,即H1可以向R1发起连接请求(此时H1作为客户端,R1作为服务器),反之,R1也可以向H1发起连接请求(此时R1作为客户端,而H1作为服务器);主机二与路由器二,主机三与路由器三也是类似。
利用套接字编程实现三对CS模型,每对模型包含一台主机,一个路由器,它们都具备接收与发送消息的能力,其中路由器需要维护一个简易的路由表,以便消息的存储转发
由于每对CS都是类似的,这里只贴出一对代码:
H1:
- Host1.h
#ifndef HOST1_H
#define HOST1_H
#include <QMainWindow>
#include <QtNetwork>
#include <QMessageBox>
#include <QString>
#include <QDebug>
namespace Ui {
class Host1;
}
class Host1 : public QMainWindow
{
Q_OBJECT
public:
explicit Host1(QWidget *parent = 0);
~Host1();
private slots:
void on_Quit_clicked();
void on_Host2_clicked();
void on_Host3_clicked();
void on_connect_clicked();
void not_Connected();
void on_Send_clicked();
void NewListen();
void acceptConnection();
void revdata();
private:
Ui::Host1 *ui;
QTcpSocket *H1_Socket;
QTcpServer *H1_Server;
};
#endif // HOST1_H
- Host1.cpp
#include "host1.h"
#include "ui_host1.h"
#define H1_Ip "127.0.0.1"
#define H2_Ip "127.0.1.1"
#define H3_Ip "127.0.2.1"
#define R1_Ip "127.0.0.11"
#define R2_Ip "127.0.1.11"
#define R3_Ip "127.0.2.11"
#define H1_Port 1111
#define H2_Port 1112
#define H3_Port 1113
#define R1_Port 11111
#define R2_Port 11112
#define R3_Port 11113
QString HSource_ip; //声明发送方IP
QString HS_Text;//声明发送的消息
QString Hdestiny_ip; // 声明目的IP
qint16 Hdestiny_port; //声明主机目的端口
QString HRSource_ip;
QString HR_Text;
QString HRdestiny_ip;
qint16 HRdestiny_port;
int H1choose =1;//初始化选项,选择目的主机,“2”为H2,“3”为H3
Host1::Host1(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Host1)
{
ui->setupUi(this);
this->H1_Socket = new QTcpSocket(this);//实例化一个套接字对象
this->H1_Server = new QTcpServer(this);//实例化 H1 Sever
ui->SendEdit->setEnabled(false);//当未连接路由器时设置发送按钮,发送框以及显示窗口为不可用
ui->Send->setEnabled(false);
ui->Send_text->setEnabled(false);
NewListen();
}
void Host1::NewListen()
{
//监听是否有客户端来访,且对任何来访者监听,端口为R1_Port
if(!H1_Server->listen(QHostAddress(H1_Ip),H1_Port))
{
return;
}
else
{
connect(H1_Server, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
}
}
void Host1::acceptConnection()
{
H1_Socket = H1_Server->nextPendingConnection();
connect(H1_Socket, SIGNAL(readyRead()), this, SLOT(revdata()));
}
void Host1::revdata()
{
//接受信息
QByteArray datas = H1_Socket->readAll();
HR_Text = QString(datas).section("/",1,1);
HRdestiny_ip = QString(datas).section("/",2,2);
HRdestiny_port = QString(datas).section("/",3,3).toInt();
HRSource_ip = QString(datas).section("/",4,4);
ui->Destiny->setText(HRdestiny_ip);
ui->Source->setText(HRSource_ip);
QString FromHost;
if(HRSource_ip == H2_Ip)
FromHost = "H2->H1:";
if(HRSource_ip == H3_Ip)
FromHost = "H3->H1:";
ui->Receive->setText(FromHost + HR_Text);
}
Host1::~Host1()
{
delete ui;
}
void Host1::on_Quit_clicked()
{
this->close();
}
void Host1::on_Host2_clicked()
{
if(H1choose == 3)
{
ui->Host3->setCheckState(Qt::Unchecked);
}
Hdestiny_ip = H2_Ip;
Hdestiny_port = H2_Port;
H1choose = 2;
}
void Host1::on_Host3_clicked()
{
if(H1choose == 2)
{
ui->Host2->setCheckState(Qt::Unchecked);
}
Hdestiny_ip = H3_Ip;
Hdestiny_port = H3_Port;
H1choose = 3;
}
void Host1::not_Connected()
{
QMessageBox::information(this,"连接状态","H1连接失败!!");
}
void Host1::on_connect_clicked()
{
H1_Socket->abort();//断开所有连接
H1_Socket->connectToHost(QHostAddress(H1_Ip),R1_Port); //原本地址应该为R1_IP,为了连接本机,用H1_IP“127.0.0.1”
bool connected = H1_Socket->waitForConnected();
if(!connected){
not_Connected();//连接失败,提示
}
else//连接成功将发送按钮和发送框以及显示框置为可用
{
ui->SendEdit->setEnabled(true);
ui->Send->setEnabled(true);
ui->Send_text->setEnabled(true);
}
}
void Host1::on_Send_clicked()
{
/*//获取本机计算机名称
QString localHostName = QHostInfo::localHostName();
QString localip;
QHostInfo info=QHostInfo::fromName(localHostName);
foreach(QHostAddress address,info.addresses())
{
if(address.protocol()==QAbstractSocket::IPv4Protocol)
localip = address.toString(); //输出IPV4的地址
}
ui->Source->setText(localip);*/
HSource_ip = H1_Ip;
HS_Text = ui->Send_text->text();
QString Pre_H1 = "H1->";
QString Des_Host;
if(H1choose == 2)
{
Des_Host = "H2:";
//ui->Destiny->setText(H2_Ip);
}
else if(H1choose == 3)
{
Des_Host = "H3:";
//ui->Destiny->setText(H3_Ip);
}
QString H1toX = Pre_H1 + Des_Host + HS_Text;
ui->SendEdit->setText(H1toX);//显示发送的数据
QString Buf=QString("/%1/%2/%3/%4").arg(HS_Text).arg(Hdestiny_ip).arg(Hdestiny_port).arg(HSource_ip);
H1_Socket->write(Buf.toUtf8().data());//发送到路由器
ui->Send_text->setText("");//清空发送框数据
H1_Socket->waitForBytesWritten();
H1_Socket->disconnectFromHost();
H1_Socket->waitForDisconnected();
}
注意代码开始处对三台主机和路由器的IP地址,端口号进行了静态的定义,模拟环境只有一台电脑,因此定义的内容实际上只用到了它们的端口号以及本机IP“127.0.0.1”,后面的Router1也是如此。
- Router1.h
#ifndef ROUTER1_H
#define ROUTER1_H
#include <QMainWindow>
#include <QtNetwork>
#include <QMessageBox>
#include <QString>
#include <QDebug>
namespace Ui {
class Router1;
}
class Router1 : public QMainWindow
{
Q_OBJECT
public:
explicit Router1(QWidget *parent = 0);
~Router1();
private slots:
void init();
void NewListen();
void acceptConnection();
void revdata();
void TransMsg();
//int RetPort();
private:
Ui::Router1 *ui;
QTcpSocket *R1_Socket;
QTcpServer *R1_Server;
};
#endif // ROUTER1_H
- Router1.cpp
#include "router1.h"
#include "ui_router1.h"
#define H1_Ip "127.0.0.1"
#define H2_Ip "127.0.1.1"
#define H3_Ip "127.0.2.1"
#define R1_Ip "127.0.0.11"
#define R2_Ip "127.0.1.11"
#define R3_Ip "127.0.2.11"
#define H1_Port 1111
#define H2_Port 1112
#define H3_Port 1113
#define R1_Port 11111
#define R2_Port 11112
#define R3_Port 11113
struct RTable //路由表
{
QString NextJump;
QString Des_IP;
};
RTable r_table[3];//定义3个路由表项
QString R_Text;
QString Rdestiny_ip;
QString RSource_Ip;
qint16 Rdestiny_port;
qint16 RSend_Port;
Router1::Router1(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Router1)
{
ui->setupUi(this);
this->R1_Server = new QTcpServer(this);
this->R1_Socket = new QTcpSocket(this);
init();
//R1_Socket->bind(R1_Port);
NewListen();
}
void Router1::NewListen()
{
//监听是否有客户端来访,且对任何来访者监听,端口为R1_Port
if(!R1_Server->listen(QHostAddress::Any,R1_Port))
{
QMessageBox::warning(this, "监听", "R1监听失败!");
return;
}
else
{
connect(R1_Server, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
}
}
void Router1::TransMsg()
{
//转发消息
R1_Socket->abort();//断开所有连接
//if(Rdestiny_ip == r_table1.Des_IP)
R1_Socket->connectToHost(QHostAddress(H1_Ip),RSend_Port);
QString Buf=QString("/%1/%2/%3/%4").arg(R_Text).arg(Rdestiny_ip).arg(Rdestiny_port).arg(RSource_Ip);
R1_Socket->write(Buf.toUtf8().data());//发送到路由器
//ui->Send_text->setText("");//清空发送框数据
R1_Socket->waitForBytesWritten();
R1_Socket->disconnectFromHost();
R1_Socket->waitForDisconnected();
}
void Router1::acceptConnection()
{
R1_Socket = R1_Server->nextPendingConnection();
connect(R1_Socket, SIGNAL(readyRead()), this, SLOT(revdata()));
}
Router1::~Router1()
{
delete ui;
}
int RetPort1(RTable next)
{
if(next.NextJump == "R2")
return R2_Port;
if(next.NextJump == "R3")
return R3_Port;
return 0;
}
void Router1::init()
{
//初始化路由器1 有到路由器2的路由记录
r_table[2].Des_IP = H2_Ip; //从主机1到主机2(H2,R2)
r_table[2].NextJump = "R2";
//r_table1.Des_IP = H3_Ip; //从主机1到主机3(H3,R3)
//r_table1.NextJump = "R3";
r_table[0].Des_IP = H3_Ip; // 从主机1到主机3(H3,R2)
r_table[0].NextJump = "R2";
r_table[1].Des_IP = H1_Ip;
r_table[1].NextJump = "";
QString next = " NextRouter:";
QString des = " Destination:";
ui->R_Table->append(des + r_table[0].Des_IP + next + r_table[0].NextJump);
ui->R_Table->append(des + r_table[1].Des_IP + next + r_table[1].NextJump);
}
void Router1::revdata()
{
//接受信息
QByteArray datas = R1_Socket->readAll();
R_Text = QString(datas).section("/",1,1);
Rdestiny_ip = QString(datas).section("/",2,2);
Rdestiny_port = QString(datas).section("/",3,3).toInt();
RSource_Ip = QString(datas).section("/",4,4);
ui->Destiny->setText(Rdestiny_ip);
ui->Source->setText(RSource_Ip);
//查找路由表项并转发
QString trans = "Trans To: ";
QString nextjump;
QString record = "Record: ";
QString next = " NextRouter:";
QString des = " Destination:";
int Found1 = 0;
for(int i=0; i<3; i++)
{
if(Rdestiny_ip == r_table[i].Des_IP )
{
nextjump = r_table[i].NextJump;
if(r_table[i].Des_IP == H1_Ip && r_table[i].NextJump == ""){
RSend_Port = H1_Port;
nextjump = "H1";
}
else if(r_table[i].Des_IP == H2_Ip && RetPort1(r_table[i]) == H2_Port)
RSend_Port = R2_Port;
else if(r_table[i].Des_IP == H2_Ip && RetPort1(r_table[i]) != H2_Port)
RSend_Port = RetPort1(r_table[i]);
else if(r_table[i].Des_IP == H3_Ip && RetPort1(r_table[i]) == H3_Port)
RSend_Port = R3_Port;
else if(r_table[i].Des_IP == H3_Ip && RetPort1(r_table[i]) != H3_Port)
RSend_Port = RetPort1(r_table[i]);
ui->R_Trans->setText(trans + nextjump);
ui->R_Record->setText(record + des + r_table[i].Des_IP + next + r_table[i].NextJump);
Found1 = 1;
}
}
if(Found1 == 0)
QMessageBox::warning(this, "转发", "R1转发失败!");
TransMsg();
}
路由算法有几种,常见的有RIP协议以及OSPF协议,大家可以自行下去了解。
代码运行截图:
同理:当H1发送消息给H3时,则H1发送给R1,R1通过查找路由表记录将信息转发到指定路由器如R2,然后R2又查找其路由表记录,将其转发给R3,最后由R3转发给H3.这里就不再贴图。