Qt: TCP通信演示案例

一、演示案例一

  • 本案例中,服务端与客户端对数据的读取都是“行读取”

服务端

  • 第一步:创建一个基于QMainWindow的窗体,类名采用默认值,然后设计窗体

image.png

第二步:类的定义

//省略了系统自定义的代码,只列出了自己定义的代码
 
#include <QTcpServer>
#include <QTcpSocket>
#include <QHostInfo>
 
class MainWindow : public QMainWindow
{
private:
    QLabel *LabListen;     //状态栏标签,显示监听状态
    QLabel *LabSocketState;//状态栏标签,显示连接状态
    QTcpServer *tcpServer; //TCP服务端对象
    QTcpSocket *tcpSocket; //TCP通信的Socket
    QString getLocalIP();  //获取本机的IP地址
protected:
    void closeEvent(QCloseEvent *event);
private slots:
    //一系列自定义槽函数
    void onNewConnection(); //QTcpServer的newConnection()信号
    void onSocketStateChange(QAbstractSocket::SocketState socketState);
    void onClientConnected();  //客户端连接
    void onClientDisconneced();//客户端断开连接
    void onSocketReadyRead(); //读取socket传入的数据
};

第三步:构造函数

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
 
    //设置spin可以输入的最大值
    ui->spinPort->setMaximum(65585);
    LabListen=new QLabel(QStringLiteral("监听状态:"));
    LabSocketState=new QLabel(QStringLiteral("Socket状态:"));
    LabListen->setMinimumWidth(150);
    LabSocketState->setMinimumWidth(200);
    ui->statusBar->addWidget(LabListen);
    ui->statusBar->addWidget(LabSocketState);
    
    //调用自定义函数获取本机IP,并将IP设置为窗体标题和comboBox的项
    QString localIP=getLocalIP();
    this->setWindowTitle(this->windowTitle()+QStringLiteral("---本机IP:")+localIP);;
    ui->comboIP->addItem(localIP);
    
    //创建QTcpServer,并将QTcpServer的newConnection信号与自定义槽函数绑定
    tcpServer=new QTcpServer(this);
    connect(tcpServer,SIGNAL(newConnection()),this,SLOT(onNewConnection()));

第四步:自定义函数getLocalIP的实现

QString MainWindow::getLocalIP()
{
    QString hostName=QHostInfo::localHostName(); //获取主机名
    QHostInfo hostInfo=QHostInfo::fromName(hostName); //通过主机名获取主机信息
    QString localIP="";
    QList<QHostAddress> addList=hostInfo.addresses(); //通过主机信息获取主机地址列表
    if(!addList.isEmpty())
    {
        for(int i=0;i<addList.count();i++)
        {
            QHostAddress aHost=addList.at(i);
            if(QAbstractSocket::IPv4Protocol==aHost.protocol())
            {
                localIP=aHost.toString();
                break;
            }
        }
        return localIP;
    }
}

第五步:开始监听按钮相应函数

image.png

void MainWindow::on_actStart_triggered()
{
    //获取IP和端口
    QString IP=ui->comboIP->currentText();
    quint16 port=ui->spinPort->value();
    
    //为服务端提供一个地址和端口,并开始监听
    QHostAddress addr(IP);
    tcpServer->listen(addr,port);
    
    //设置组件的内容和状态
    ui->plainTextEdit->appendPlainText(QStringLiteral("开始监听..."));
    ui->plainTextEdit->appendPlainText(QStringLiteral("服务器地址...")+tcpServer->serverAddress().toString());
    ui->plainTextEdit->appendPlainText(QStringLiteral("服务器端口...")+QString::number(tcpServer->serverPort()));
    ui->actStart->setEnabled(false);
    ui->actStop->setEnabled(true);
    LabListen->setText(QStringLiteral("监听状态:正在监听..."));
}

第六步:实现自定义的onNewConnection槽函数,当有客户端连接进来时执行该槽函数

void MainWindow::onNewConnection()
{
    tcpSocket=tcpServer->nextPendingConnection();//获取连接客户端的socket套接字
    
    //客户端socket连接建立时发射connected信号
    connect(tcpSocket,SIGNAL(connected()),this,SLOT(onClientConnected()));
    onClientConnected();
    
    //客户端socket连接断开时发射disconnected信号
    connect(tcpSocket,SIGNAL(disconnected()),this,SLOT(onClientDisconneced()));
    
    //本程序的socket状态变化时发生stateChanged信号
    connect(tcpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
    
    //本程序的socket的读取缓冲区有新数据时发送此信号
    connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));
}

第七步:实现在onNewConnection槽函数中调用的另一些自定义槽函数(onSocketReadyRead在下面介绍)

void MainWindow::onClientConnected()
{
    //打印客户端的信息
    ui->plainTextEdit->appendPlainText(QStringLiteral("client socket connected"));
    ui->plainTextEdit->appendPlainText(QStringLiteral("peer address:")+tcpSocket->peerAddress().toString());
    ui->plainTextEdit->appendPlainText(QStringLiteral("peer port:")+QString::number(tcpSocket->peerPort()));
}
void MainWindow::onClientDisconneced()
{
    //打印信息
    ui->plainTextEdit->appendPlainText("client socket disconnected");
    //稍后删除本次会话的socket
    tcpSocket->deleteLater();
}
void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState)
{
    //socket的各种状态
    switch (socketState) {
        case QAbstractSocket::UnconnectedState:
            LabSocketState->setText(QStringLiteral("socket状态:UnconnectedState"));
            break;
        case QAbstractSocket::HostLookupState:
            LabSocketState->setText(QStringLiteral("socket状态:HostLookupState"));
            break;
        case QAbstractSocket::ConnectedState:
            LabSocketState->setText(QStringLiteral("socket状态:ConnectedState"));
            break;
        case QAbstractSocket::BoundState:
            LabSocketState->setText(QStringLiteral("socket状态:BoundState"));
            break;
        case QAbstractSocket::ClosingState:
            LabSocketState->setText(QStringLiteral("socket状态:ClosingState"));
            break;
        case QAbstractSocket::ListeningState:
            LabSocketState->setText(QStringLiteral("socket状态:ListeningState"));
            break;
    }
}

第八步:停止监听按钮响应函数


image.png

void MainWindow::on_actStop_triggered()
{
    //如果服务端处于监听状态,断开连接
    if(tcpServer->isListening())
    {
        tcpServer->close();
        ui->actStart->setEnabled(true);
        ui->actStop->setEnabled(false);
        LabListen->setText(QStringLiteral("监听状态:已停止监听"));
    }
}

第九步:发送消息按钮的相应函数

void MainWindow::on_btnSend_clicked()
{
    //获取输入的内容
    QString msg=ui->editMsg->text();
 
    ui->plainTextEdit->appendPlainText(QStringLiteral("[out]:")+msg);
    ui->editMsg->clear();
    ui->editMsg->setFocus();
 
    //将数据转为字节数组,并在结尾加上换行符,然后发送一行数据出去
    QByteArray str=msg.toUtf8();
    str.append('\n');
    tcpSocket->write(str);
}

第十步:自定义onSocketReadyRead槽函数的实现(当缓冲区中有数据可以读取时,执行该槽函数)

void MainWindow::onSocketReadyRead()
{
    //如果有一行数据可以读取,那么读取一行数据
    while(tcpSocket->canReadLine())
    {
        ui->plainTextEdit->appendPlainText(QStringLiteral("[in]:")+tcpSocket->readLine());
    }
}

客户端

  • 第一步:创建一个基于QMainWindow的窗体,类名采用默认值,然后设计窗体

image.png

第二步:类的定义

////省略了系统自定义的代码,只列出了自己定义的代码
#include <QLabel>
#include <QTcpServer>
#include <QTcpSocket>
#include <QHostInfo>
 
class MainWindow : public QMainWindow
{
private:
    QLabel *LabSocketState;//状态栏标签,显示连接状态
    QTcpSocket *tcpClient; //TCP通信的Socket
    QString getLocalIP();  //获取本机的IP地址
protected:
    void closeEvent(QCloseEvent *event);
private slots:
    //一系列自定义槽函数
    void onConnection();
    void onDisconnection();
    void onSocketStateChange(QAbstractSocket::SocketState socketState);
    void onSocketReadyRead(); //读取socket传入的数据
};

第三步:构造函数

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
 
    //设置spin可以输入的最大值
    ui->spinPort->setMaximum(65585);
    
    //创建socket对象
    tcpClient=new QTcpSocket(this);
    
    LabSocketState=new QLabel(QStringLiteral("Socket状态:"));
    LabSocketState->setMinimumWidth(250);
    ui->statusBar->addWidget(LabSocketState);
 
    //获取自定义函数获取本机IP,并将IP设置为窗体标题和comboBox的项
    QString localIP=getLocalIP();
    this->setWindowTitle(this->windowTitle()+QStringLiteral("---本机IP:")+localIP);;
    ui->comboIP->addItem(localIP);
 
    //绑定一些列信号与槽
    connect(tcpClient,SIGNAL(connected()),this,SLOT(onConnection()));
    connect(tcpClient,SIGNAL(disconnected()),this,SLOT(onDisconnection()));
    connect(tcpClient,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
    connect(tcpClient,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));
}

第四步:自定义getLocalIP函数的实现

QString MainWindow::getLocalIP()
{
    QString hostName=QHostInfo::localHostName(); //获取主机名
    QHostInfo hostInfo=QHostInfo::fromName(hostName); //通过主机名获取主机信息
    QString localIP="";
    QList<QHostAddress> addList=hostInfo.addresses(); //通过主机信息获取主机地址列表
    if(!addList.isEmpty())
    {
        for(int i=0;i<addList.count();i++)
        {
            QHostAddress aHost=addList.at(i);
            if(QAbstractSocket::IPv4Protocol==aHost.protocol())
            {
                localIP=aHost.toString();
                break;
            }
        }
        return localIP;
    }
}

第五步:连接到服务器按钮的相应函数

void MainWindow::on_actConnect_triggered()
{
    //获取IP和端口,并连接到服务端
    QString addr=ui->comboIP->currentText();
    quint16 port=ui->spinPort->value();
    tcpClient->connectToHost(addr,port);
}

第六步:断开连接按钮的相应函数

void MainWindow::on_actDisconnect_triggered()
{
    if(tcpClient->state()==QAbstractSocket::ConnectedState)
        tcpClient->disconnectFromHost();
}

第七步:自定义onConnection、onDisconnection槽函数

void MainWindow::onConnection()
{
    //打印信息
    ui->plainTextEdit->appendPlainText(QStringLiteral("已连接到服务器"));
    ui->plainTextEdit->appendPlainText(QStringLiteral("peer address:")+tcpClient->peerAddress().toString());
    ui->plainTextEdit->appendPlainText(QStringLiteral("peer port:")+QString::number(tcpClient->peerPort()));
 
    ui->actConnect->setEnabled(false);
    ui->actDisconnect->setEnabled(true);
}
void MainWindow::onDisconnection()
{
    ui->plainTextEdit->appendPlainText(QStringLiteral("已断开与服务器的连接"));
    ui->actConnect->setEnabled(true);
    ui->actDisconnect->setEnabled(false);
}

第八步:自定义onSocketReadyRead、onSocketStateChange槽函数的实现

void MainWindow::onSocketReadyRead()
{
    //如果有一行数据可以读取,就读取
    while(tcpClient->canReadLine())
        ui->plainTextEdit->appendPlainText(QStringLiteral("[in]:")+tcpClient->readLine());
}
void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState)
{
    //socket的各种状态
    switch (socketState) {
        case QAbstractSocket::UnconnectedState:
            LabSocketState->setText(QStringLiteral("socket状态:UnconnectedState"));
            break;
        case QAbstractSocket::HostLookupState:
            LabSocketState->setText(QStringLiteral("socket状态:HostLookupState"));
            break;
        case QAbstractSocket::ConnectedState:
            LabSocketState->setText(QStringLiteral("socket状态:ConnectedState"));
            break;
        case QAbstractSocket::BoundState:
            LabSocketState->setText(QStringLiteral("socket状态:BoundState"));
            break;
        case QAbstractSocket::ClosingState:
            LabSocketState->setText(QStringLiteral("socket状态:ClosingState"));
            break;
        case QAbstractSocket::ListeningState:
            LabSocketState->setText(QStringLiteral("socket状态:ListeningState"));
            break;
    }
}

第九步:发送信息按钮的响应函数

void MainWindow::on_btnSend_clicked()
{
    //获取输入的内容
    QString msg=ui->editMsg->text();
 
    ui->plainTextEdit->appendPlainText(QStringLiteral("[out]:")+msg);
    ui->editMsg->clear();
    ui->editMsg->setFocus();
 
    //将数据转为字节数组,并在结尾加上换行符,然后发送一行数据出去
    QByteArray str=msg.toUtf8();
    str.append('\n');
    tcpClient->write(str);
}

演示效果:

image.png

二、演示案例二

  • 本案例中,服务端与客户端对数据的读取都是“字节读取”

客户端

image.png

//客户端类
class Widget : public QWidget
{
    Q_OBJECT
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
private:
    Ui::Widget *ui;
    QTcpSocket* clientSocket;
    bool isConnect;//一个标志,用于判断是否连接服务器
private slots:
    void connectServerSlot();
    void isConnectSlot();
    void isDisConnectSlot();
    void recvDataFromServerSlot();
    void sendDataToServerSlot();
};
Widget::Widget(QWidget *parent) :
    QWidget(parent),isConnect(false),//初始化连接表示为false
    ui(new Ui::Widget)
{
    ui->setupUi(this);
 
    clientSocket=new QTcpSocket(this);
    connect(ui->conServerBtn,SIGNAL(clicked()),this,SLOT(connectServerSlot()));//点击按钮连接/断开服务器
    connect(clientSocket,SIGNAL(connected()),this,SLOT(isConnectSlot()));//连接成功后相应
    connect(clientSocket,SIGNAL(disconnected()),this,SLOT(isDisConnectSlot()));//如果没有连接相应
    connect(ui->sendMsgBtn,SIGNAL(clicked()),this,SLOT(sendDataToServerSlot()));//点击按钮发送消息
}
 
Widget::~Widget()
{
    if(clientSocket->isOpen())
        clientSocket->close();//关闭套接字
    delete clientSocket;//释放套接字
    delete ui;
}
 
void Widget::connectServerSlot()
{
    if(!isConnect)//如果当前未连接
    {
        QString ServerIp=ui->serverIpLEdit->text();//得到服务器IP
        QString Port=ui->portLEdit->text();//得到服务器端口号
        clientSocket->connectToHost(QHostAddress(ServerIp),Port.toInt());//通过IP与端口号连接服务器
        if(clientSocket->waitForConnected(1000))//如果等待一秒内连接成功
        {
            QMessageBox::information(this,QStringLiteral("提示"),QStringLiteral("连接服务器成功"));
            isConnect=true;
        }
        else//如果连接超时
        {
            clientSocket->disconnect();//套接字不连接
            QMessageBox::warning(this,QStringLiteral("提示"),QStringLiteral("连接服务器失败"));
        }
    }
    else//如果当前连接
    {
        clientSocket->close();//关闭套接字
        QMessageBox::information(this,QStringLiteral("提示"),QStringLiteral("断开服务器成功"));
        isConnect=false;
    }
}
 
void Widget::isDisConnectSlot()
{
    ui->conServerBtn->setText(QStringLiteral("连接服务器"));
}
 
void Widget::isConnectSlot()
{
    ui->conServerBtn->setText(QStringLiteral("断开服务器"));
    //连接成功后,查看是否有数据发送过来
    connect(clientSocket,SIGNAL(readyRead()),this,SLOT(recvDataFromServerSlot()));
}
 
void Widget::recvDataFromServerSlot()//接受数据
{
    QByteArray recvData=clientSocket->readAll();//以字节类型得到数据
    ui->recvTBrowser->append(QStringLiteral("服务器:")+QString::fromLocal8Bit(recvData));
    ui->recvSizeTBrowser->setText(QString::number(recvData.size()));
}
 
void Widget::sendDataToServerSlot()//发送数据出去
{
    QString sendData=ui->sendTEdit->toPlainText();
    if(clientSocket->isOpen())//如果套接字处于打开状态
    {
        clientSocket->write(sendData.toLocal8Bit());//写数据
        ui->recvTBrowser->append(QStringLiteral("客户端:")+sendData);
        ui->sendSizeTBrowser->setText(QString::number(sendData.toLocal8Bit().size()));
        ui->sendTEdit->clear();
    }
}

服务端

image.png

//服务端类
class Widget : public QWidget
{
    Q_OBJECT
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
private:
    Ui::Widget *ui;
    QTcpServer* tcpServer;//服务
    QTcpSocket* tcpServerSocket;//套接字
private:
    void showHostAddress();
private slots:
    void listenSlot();
    void newConSlot();
    void recvDataFromClientSlot();
    void sendDataToClientSlot();
};
Widget::Widget(QWidget *parent) :
    QWidget(parent),ui(new Ui::Widget)
{
    ui->setupUi(this);
 
    showHostAddress();//打印主机网络地址和端口
    tcpServer=new QTcpServer(this);
 
    connect(ui->listenBtn,SIGNAL(clicked()),this,SLOT(listenSlot())); //点击按钮开启加监听
    connect(tcpServer,SIGNAL(newConnection()),this,SLOT(newConSlot()));//如果外界有客户端连接
    connect(ui->sendMsgBtn,SIGNAL(clicked()),this,SLOT(sendDataToClientSlot()));//点击按钮发送数据
}
 
void Widget::showHostAddress()
{
    //allAddresses:得到所有网络地址
    QList<QHostAddress> addressList=QNetworkInterface::allAddresses();
    foreach(QHostAddress address,addressList)//遍历网络地址
    {
        if(address.isNull())//为空退出
            continue;
        QAbstractSocket::NetworkLayerProtocol protocol=address.protocol();//得到网络地址类型
        if(protocol!=QAbstractSocket::IPv4Protocol)//此处设置为判断是否为IPV4网络地址
            continue;
        ui->ipComBox->addItem(address.toString());//如果是IPV4,添加进组合框
    }
}
 
void Widget::listenSlot()//监听
{
    if(!tcpServer->isListening())//如果不处于监听状态,开启监听
    {
        //如果监听成功,参数:1.服务器IP地址 2.服务器端口号
        if(tcpServer->listen(QHostAddress(ui->ipComBox->currentText()),\
                          ui->portLEdit->text().toInt()))
        {
            QMessageBox::information(this,QStringLiteral("提示"),QStringLiteral("启动监听成功"));
            ui->listenBtn->setText(QStringLiteral("关闭监听"));
        }
        else
        {
            QMessageBox::warning(this,QStringLiteral("提示"),QStringLiteral("启动监听失败"));
        }
    }
    else//如果处于监听状态,关闭监听
    {
        tcpServer->close();//关闭服务
        tcpServerSocket->close();//关闭套接字
        ui->clientTBrowser->clear();
        ui->recvTBrowser->clear();
        ui->recvSizeTBrowser->clear();
        ui->sendSizeTBrowser->clear();
        ui->listenBtn->setText(QStringLiteral("开启监听"));
    }
}
 
void Widget::newConSlot()
{
    tcpServerSocket=tcpServer->nextPendingConnection();//得到传入的客户套接字
    QString clientName=tcpServerSocket->peerName();//得到客户端的名称
    QHostAddress clientAddress=tcpServerSocket->peerAddress();//得到客户端的网络地址
    qint64 clientPort=tcpServerSocket->peerPort();//得到客户端的端口
 
    ui->clientTBrowser->append(QStringLiteral("客户端名:")+clientName+"\n");
    ui->clientTBrowser->append(QStringLiteral("客户端IP:")+clientAddress.toString()+"\n");
    ui->clientTBrowser->append(QStringLiteral("客户端端口:")+QString::number(clientPort)+"\n");
    connect(tcpServerSocket,SIGNAL(readyRead()),this,SLOT(recvDataFromClientSlot()));//等待外界数据传入
}
 
void Widget::recvDataFromClientSlot()
{
    QByteArray recvData=tcpServerSocket->readAll();//读取所有数据
    ui->recvTBrowser->append(QStringLiteral("客户端:")+QString::fromLocal8Bit(recvData));
    ui->recvSizeTBrowser->setText(QString::number(recvData.size()));
}
 
void Widget::sendDataToClientSlot()
{
    QString sendData=ui->sendTEdit->toPlainText();
    tcpServerSocket->write(sendData.toLocal8Bit());//向外发送数据
    ui->recvTBrowser->append(QStringLiteral("服务端:")+sendData);
    ui->sendSizeTBrowser->setText(QString::number(sendData.toLocal8Bit().size()));
    ui->sendTEdit->clear();
}

演示效果

image.png


————————————————

版权声明:本文为CSDN博主「江南、董少」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接:

https://blog.csdn.net/qq_41453285/article/details/100137925



本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:
  • 评论列表:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2025年4月    »
123456
78910111213
14151617181920
21222324252627
282930
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864