在之前的博客《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.这里就不再贴图。