Qt 网络编程实战

一.获取主机的网络信息

  1. 需要添加network模块
cpp 复制代码
QT   += core gui network
  1. 主要涉及的类分析

QHostInfo类

  • QHostInfo::localHostName() 获取本地的主机名
  • QHostInfo::fromName(const QString &) 获取指定主机的主机信息 addresses接口

QNetworkInterface类

  • QNetworkInterface::allAddresses() 网卡所有的IP地址
  1. 项目效果
  1. 源码如下
cpp 复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include<QHostInfo>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:

    //获取主机名
    void GetHostName();

    //获取IPV4
    void GetIPV4();

    //获取IPV6
    void GetIPV6();

    //清除文本内容
    void Clear_Text();

    //获取指定域名的IP地址
    void Get_DefineHost();
    void lookedUp(QHostInfo hostInfo);

    //主机所有网卡IP地址
    void GetAlladdresses();

    //所有网卡的网络信息
    void GetNetWorkInfo();

private:
    void InitConnectSignals();

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
cpp 复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QHostInfo>
#include<QNetworkInterface>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setWindowTitle("主机的网络信息");
    InitConnectSignals();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::GetHostName()
{
    //获取当前的主机名
    QString hostname = QHostInfo::localHostName();
    ui->textEdit->append("当前的主机名为:"+hostname);

}

void MainWindow::GetIPV4()
{
    //获取当前的主机名
    QString hostname = QHostInfo::localHostName();

    //获取指定主机的主机信息
    QHostInfo hostinformation = QHostInfo::fromName(hostname);

    //Returns the list of IP addresses associated with hostName().
    //This list may be empty.
    //返回与hostName()相关联的IP地址列表。
    //该列表可能为空。
    QList<QHostAddress> addrlist = hostinformation.addresses();
    ui->textEdit->append("本机的IPV4地址如下:");

    if(!addrlist.isEmpty())
    {
        //for遍历输出
        for(int i=0;i<addrlist.size();i++)
        {
            //protocol是协议的意思
            if(addrlist[i].protocol()==QAbstractSocket::IPv4Protocol)
            {
                ui->textEdit->append(addrlist[i].toString());
            }
        }
    }

}

void MainWindow::GetIPV6()
{
    //获取当前的主机名
    QString hostname = QHostInfo::localHostName();

    //获取指定主机的主机信息
    QHostInfo hostinformation = QHostInfo::fromName(hostname);

    //Returns the list of IP addresses associated with hostName().
    //This list may be empty.
    //返回与hostName()相关联的IP地址列表。
    //该列表可能为空。
    QList<QHostAddress> addrlist = hostinformation.addresses();
    ui->textEdit->append("本机的IPV6地址如下:");

    if(!addrlist.isEmpty())
    {
        //for遍历输出
        for(int i=0;i<addrlist.size();i++)
        {
            //protocol是协议的意思
            if(addrlist[i].protocol()==QAbstractSocket::IPv6Protocol)
            {
                ui->textEdit->append(addrlist[i].toString());
            }
        }
    }
}

void MainWindow::Clear_Text()
{
    ui->textEdit->clear();
}

void MainWindow::Get_DefineHost()
{
    //获取lineEdit上的域名
    QString hostName =ui->lineEdit->text();
    if(hostName.isEmpty())
    {
        return ;
    }

    //获取域名的IPV4信息
    /*
     查找与主机名name相关联的IP地址,并返回用于查找的ID。当查找结果准备好时,
将使用QHostInfo参数调用receiver中的slot或信号成员。然后可以检查QHostInfo对象以获得查找结果。
      */
    //lookupHost(const QString &name, QObject *context, Func1 slot)
    QHostInfo::lookupHost(hostName,this,SLOT(lookedUp(QHostInfo)));
}

void MainWindow::lookedUp(QHostInfo hostInfo)
{
    //当前的主机名
    QString hostName=hostInfo.hostName();
    ui->textEdit->append("当前域名为:"+hostName+" IPV4信息如下:");

    QList<QHostAddress> addrlist = hostInfo.addresses();

    if(!addrlist.isEmpty())
    {
        for(int i=0;i<addrlist.count();i++)
        {
            if(addrlist[i].protocol()==QAbstractSocket::IPv4Protocol)
            {
                ui->textEdit->append(addrlist[i].toString());
            }
        }
    }

    /*
      常见的错误
QObject::connect: Incompatible sender/receiver arguments
        QHostInfoResult::resultsReady(QHostInfo) --> MainWindow::lookedUp(QHostInfo&)

        信号和槽的参数不匹配

     */
}

void MainWindow::GetAlladdresses()
{
    //获取网卡的所有IP信息
    ui->textEdit->append("所有网卡的IPV4信息如下:");
    QList<QHostAddress>addrlist = QNetworkInterface::allAddresses();
    if(!addrlist.isEmpty())
    {
        for(int i=0;i<addrlist.count();i++)
        {
            if(addrlist[i].protocol()==QAbstractSocket::IPv4Protocol)
            {
                ui->textEdit->append(addrlist[i].toString());
            }
        }
    }

}

void MainWindow::GetNetWorkInfo()
{
    //获取所有网卡的网络信息
    QList<QNetworkInterface>list =QNetworkInterface::allInterfaces();

    for(int i=0;i<list.count();i++)
    {
        QNetworkInterface interface = list[i];
        if(!interface.isValid())
        {
            continue;
        }

        ui->textEdit->append("设备名称:"+interface.humanReadableName());
        ui->textEdit->append("硬件地址:"+interface.hardwareAddress());


        //一个网卡,可以有多个IP地址
        QList<QNetworkAddressEntry> list2 = interface.addressEntries();
        for(int j=0; j<list2.count(); j++)
        {
              QNetworkAddressEntry entry = list2[j];
              ui->textEdit->append("\tIP地址:" + entry.ip().toString());
              ui->textEdit->append("\t子网掩码:" + entry.netmask().toString());
              ui->textEdit->append("\t广播地址: " + entry.broadcast().toString() + "\n");

         }
    }

}

void MainWindow::InitConnectSignals()
{
    //void pressed();
    connect(ui->pushButton_HostName,SIGNAL(pressed()),this,SLOT(GetHostName()));

    connect(ui->pushButton_IPV4,SIGNAL(pressed()),this,SLOT(GetIPV4()));

    connect(ui->pushButton_IPV6,SIGNAL(pressed()),this,SLOT(GetIPV6()));

    connect(ui->pushButton_Clear,SIGNAL(pressed()),this,SLOT(Clear_Text()));

    connect(ui->pushButton_DefineHost,SIGNAL(pressed()),this,SLOT(Get_DefineHost()));

    connect(ui->pushButton_Alladdresses,SIGNAL(pressed()),this,SLOT(GetAlladdresses()));

    connect(ui->pushButton_Network,SIGNAL(pressed()),this,SLOT(GetNetWorkInfo()));
}

二.TCP编程

1. server编程(服务器端实现)

  • 项目效果

  • 客户端(使用的是调试助手)

  • 项目源码

cpp 复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QLabel>
#include<QHostInfo>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    InitUI();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::InitUI()
{
    //加状态栏中添加控件
    label_first=new QLabel();
    label_second=new QLabel();
    label_third=new QLabel();
    label_four=new QLabel();

    label_first->setText("监听状态:");
    label_third->setText("套接字状态:");

    label_first->setMaximumWidth(80);
    label_third->setMaximumWidth(80);

    label_second->setMinimumWidth(100);
    label_four->setMinimumWidth(100);

    ui->statusbar->addWidget(label_first);
    ui->statusbar->addWidget(label_second);
    ui->statusbar->addWidget(label_third);
    ui->statusbar->addWidget(label_four);

    //fromName 返回指定主机的主机信息
    QHostInfo hostInfo = QHostInfo::fromName(QHostInfo::localHostName());
    QList<QHostAddress> addrlist = hostInfo.addresses();

    if(!addrlist.isEmpty())
    {
        for(int i=0;i<addrlist.count();i++)
        {
            QHostAddress address = addrlist[i];
            if(address.protocol()==QAbstractSocket::IPv4Protocol)
            {
                ui->comboBox->addItem(address.toString());
            }
        }
    }

    //本机的环回网卡的IP 127.0.0.1
    //用来做网络测试的
    ui->comboBox->addItem("127.0.0.1");

    //处理tcp通信
    tcpserver=new QTcpServer(this);

    //发现新信号就触发onNewConnection
    connect(tcpserver,SIGNAL(newConnection()),this,SLOT(onNewConnection()));





}


void MainWindow::on_actionListen_triggered()
{
    QString ip =ui->comboBox->currentText();

    //unsigned short
    quint16 port = ui->spinBox->value();
    QHostAddress addr(ip);
    //开始监听
    tcpserver->listen(addr,port);


    label_second->setText("已开始监听");
    ui->textEdit->append("开始监听,等待客户端发起连接...");

    ui->textEdit->append("服务器地址:"+tcpserver->serverAddress().toString());
    ui->textEdit->append("服务器端口:"+QString::number(tcpserver->serverPort()));

    //更改控件
    ui->actionStop->setEnabled(true);
    ui->actionListen->setEnabled(false);


}

void MainWindow::on_actionStop_triggered()
{


    //停止监听
    if(tcpserver->isListening())
    {
        tcpserver->close();
        ui->actionListen->setEnabled(true);
        ui->actionStop->setEnabled(false);
        label_second->setText("已停止监听");
    }

}

void MainWindow::on_actionClear_triggered()
{
    label_second->clear();
    label_four->clear();
    ui->textEdit->clear();
}

void MainWindow::onNewConnection()
{
    //一旦有新的连接
    //难以理解的概念:套接字,相当于客户端套接字,每当有客户端发起请求,服务器就会分配一个套接字
    //去接待

    tcpsocket=tcpserver->nextPendingConnection();//取出要服务,相当于是分配一个服务人员

    //连接成功会触发的
    connect(tcpsocket,SIGNAL(connected()),this,SLOT(onConnected()));


    //当有数据可以读的时候,我们就读数据,(使用信号槽)
    connect(tcpsocket,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));


    connect(tcpsocket,SIGNAL(disconnected()),this,SLOT(onDisconnected()));

    // void stateChanged(QAbstractSocket::SocketState);
    connect(tcpsocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),
            this,SLOT(onSocketStateChanged(QAbstractSocket::SocketState)));

    onSocketStateChanged(tcpsocket->state());//先更新一下状态

}

void MainWindow::onSocketReadyRead()
{
    //有数据可读时
    while(tcpsocket->canReadLine())
    {
        ui->textEdit->append("[收到]"+tcpsocket->readLine());
    }

}

void MainWindow::onConnected()
{
    //刚连入成功
    ui->textEdit->append("\n客户端请求已经接入!");

    //获取客户端的地址信息
    ui->textEdit->append("客户端地址:"+tcpsocket->peerAddress().toString());

    ui->textEdit->append("客户端端口号:"+QString::number(tcpsocket->peerPort()));




}

void MainWindow::onDisconnected()
{
    ui->textEdit->append("\n客户端已断开连接");
    tcpsocket->deleteLater();//以后在删除,这样更安全
}

void MainWindow::onSocketStateChanged(QAbstractSocket::SocketState status)
{
    /*
        UnconnectedState,
        HostLookupState,
        ConnectingState,
        ConnectedState,
        BoundState,
        ListeningState,
        ClosingState
     */
    //套接字状态发生变化
    switch(status)
    {
        case QAbstractSocket::UnconnectedState:
        label_four->setText("未连接");
        break;

        case QAbstractSocket::HostLookupState:
        label_four->setText("正在查找主机名");
        break;

        case QAbstractSocket::ConnectingState:
        label_four->setText("正在连接");
        break;

        case QAbstractSocket::ConnectedState:
        label_four->setText("已连接");
        break;

        case QAbstractSocket::BoundState:
        label_four->setText("已绑定到IP地址和端口号");
        break;

        case QAbstractSocket::ListeningState:
        label_four->setText("正在监听");
        break;

        case QAbstractSocket::ClosingState:
        label_four->setText("准备关闭");
        break;
    }

}

void MainWindow::on_pushButton_clicked()
{
    //发送一行字符串
    QString msg = ui->lineEdit->text();

    //utf编码,QByteArray在功能上很像字符数组
    QByteArray str=msg.toUtf8();
    str.append('\n');

    //服务器向客户端发送数据
    tcpsocket->write(str);

}

void MainWindow::on_actionQuit_triggered()
{
    close();
}

2. 客户端实现

  • 效果展示

  • 源码如下

cpp 复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    //创建了客户端的套接字对象
    tcpsocket=new QTcpSocket(this);

    //连接成功会触发的
    connect(tcpsocket,SIGNAL(connected()),this,SLOT(onConnected()));


    //当有数据可以读的时候,我们就读数据,(使用信号槽)
    connect(tcpsocket,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));


    connect(tcpsocket,SIGNAL(disconnected()),this,SLOT(onDisconnected()));

    // void stateChanged(QAbstractSocket::SocketState);
    connect(tcpsocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),
            this,SLOT(onSocketStateChanged(QAbstractSocket::SocketState)));

    ui->actionConnect->setEnabled(true);
    ui->actionDisconnect->setEnabled(false);

}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::onConnected()
{
    //peerAddress对等地址
    //打印服务器的地址
    ui->plainTextEdit->appendPlainText("已连接到服务器!");
    ui->plainTextEdit->appendPlainText("---服务器地址:"+
            tcpsocket->peerAddress().toString());

    ui->plainTextEdit->appendPlainText("---服务器端口:"+
                                       QString::number(tcpsocket->peerPort()));
}

void MainWindow::onDisconnected()
{
    ui->plainTextEdit->appendPlainText("服务器的连接已经断开");
    ui->actionConnect->setEnabled(true);
    ui->actionDisconnect->setEnabled(false);
}

void MainWindow::onSocketStateChanged(QAbstractSocket::SocketState status)
{
    //这和服务器端的判断一样的
}

void MainWindow::onSocketReadyRead()
{
    while(tcpsocket->canReadLine())
    {
        ui->plainTextEdit->appendPlainText("[收到]"+tcpsocket->readLine());
    }
}


void MainWindow::on_actionConnect_triggered()
{
    QString ip = ui->lineEditIP->text();
    quint16 port=ui->spinBoxPort->value();

    tcpsocket->connectToHost(ip,port);
}

void MainWindow::on_actionDisconnect_triggered()
{
    if(tcpsocket->state()==QAbstractSocket::ConnectedState)
    {
        //从主机那断开
        tcpsocket->disconnectFromHost();
    }
}

void MainWindow::closeEvent(QCloseEvent *event)
{

    if(tcpsocket->state()==QAbstractSocket::ConnectedState)
    {
        //从主机那断开
        tcpsocket->disconnectFromHost();
    }

    event->accept();//不调用这个窗口就不会关闭

    //event->ignore();这个窗口就不关闭
}

void MainWindow::on_actionClear_triggered()
{
    ui->plainTextEdit->clear();
}

void MainWindow::on_actionQuit_triggered()
{
    close();
}

void MainWindow::on_pushButton_clicked()
{
    QString msg = ui->lineEditSend->text();
    QByteArray str=msg.toUtf8();
    str.append('\n');

    tcpsocket->write(str);

    ui->plainTextEdit->appendPlainText("[发送]"+msg);
    ui->lineEditSend->clear();
    ui->lineEditSend->setFocus();//设置光标聚焦
}
相关推荐
奋斗的小花生1 小时前
c++ 多态性
开发语言·c++
魔道不误砍柴功1 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
闲晨1 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
_.Switch1 小时前
高级Python自动化运维:容器安全与网络策略的深度解析
运维·网络·python·安全·自动化·devops
qq_254674411 小时前
工作流初始错误 泛微提交流程提示_泛微协同办公平台E-cology8.0版本后台维护手册(11)–系统参数设置
网络
JokerSZ.1 小时前
【基于LSM的ELF文件安全模块设计】参考
运维·网络·安全
老猿讲编程1 小时前
一个例子来说明Ada语言的实时性支持
开发语言·ada
Chrikk2 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*2 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go