QT之TCP/IP通讯

TCP/IP是面向连接的,需要区分服务器和客户端;
服务器端

1.使用QTcpServer类

cpp 复制代码
// 创建服务
int iServerPort = readIniFileByKay("TCP", "ServerPort").toInt();
if(iServerPort == 0){
    iServerPort = 8080;
}
mTcpServer = new QTcpServer(this);
if (mTcpServer == NULL) {
    outPutMsg(QtDebugMsg, "mTcpServer 服务器启动失败 mTcpServer=NULL");
    return;
}
if (!mTcpServer->listen(QHostAddress::Any, iServerPort))
{
    outPutMsg(QtDebugMsg, "mTcpServer 服务器启动失败");
}
else {
    outPutMsg(QtDebugMsg, "mTcpServer 服务器启动成功");
}
connect(mTcpServer, &QTcpServer::newConnection, this, &CacheCar::newConnection_slot); 

2.客户端连接服务器时会调用到槽函数newConnection_slot中:

cpp 复制代码
// 客户端连接
void CacheCar::newConnection_slot() {
    outPutMsg(QtDebugMsg, "newConnection_slot 有客户端申请连接");
    //获取最新连接的客户端套接字
    //[virtual] QTcpSocket *QTcpServer::nextPendingConnection()
    QTcpSocket* s = mTcpServer->nextPendingConnection();
    clientList.push_back(s); //将获取的套接字存放到客户端容器中 QList<QTcpSocket*> clientList;
    connect(s, &QTcpSocket::readyRead, this, &CacheCar::readyRead_slot);
}

3.当建立连接的客户端发来信息时,会调用槽函数readyRead_slot

cpp 复制代码
// 客户端发来的数据
void  CacheCar::readyRead_slot() {
    //删除客户端链表中的无效客户端套接字
    for (int i = 0; i < clientList.count(); i++)
    {
        //返回值:客户端状态,如果是0,表示无连接
        if (clientList[i]->state() == 0)
        {
            clientList.removeAt(i);     //将下标为i的客户端移除
        }
    }

    //遍历所有客户端,查看是哪个客户端发来数据
    for (int i = 0; i < clientList.count(); i++)
    {
        if (clientList[i]->bytesAvailable() != 0)
        {
            QByteArray msg = clientList[i]->readAll();

            QString msgStr = QString::fromUtf8(msg);
            outPutMsg(QtDebugMsg, "CacheCar::readyRead_slot msgStr: " + msgStr);
            QStringList list = msgStr.split("||");
            if (list.size()<2) {
                return;
            }
            QString barCode = list[1];
            outPutMsg(QtDebugMsg, "CacheCar::readyRead_slot barCode: " + barCode);
            if (barCode.size() < 8) {
                return;
            }
            else {
                int index = list[0].toInt();
                // 1.收到扫码信息,向服务端发起请求,同步等用来获取窗户信息 2.组装测量请求,并发送给PLC
                if (!getHttpWindowInfoRequest(index, barCode, index)) {
                    outPutMsg(QtDebugMsg, "CacheCar::readyRead_slot 提交窗户入库请求失败 barCode: " + barCode);
                }
            }
        }
    }
}

客户端:

1.创建连接

cpp 复制代码
// PLC
mSocketPlc = new QTcpSocket(this);
if (mSocketPlc == NULL) {
    return;
}
QString plcIP = readIniFileByKay("TCP", "PLCIP");
QString plcPortStr = readIniFileByKay("TCP", "PLCPort");
int plcPort = plcPortStr.toInt();
mSocketPlc->connectToHost(plcIP, plcPort);
connect(mSocketPlc, &QTcpSocket::errorOccurred, this, &CacheCar::onTcpError_plc);
connect(mSocketPlc, &QTcpSocket::connected, this, &CacheCar::onTcpConnected_plc);
connect(mSocketPlc, &QTcpSocket::disconnected, this, &CacheCar::onTcpDisconnected_plc);
connect(mSocketPlc, &QTcpSocket::readyRead, this, &CacheCar::onReadData_plc);

2.连接报错或断开连接后,自动发起重连

cpp 复制代码
// PLC服务
void CacheCar::onTcpError_plc(QAbstractSocket::SocketError socketError) {
    outPutMsg(QtDebugMsg, "CacheCar::onTcpError_plc Connected to host ");
    outPutMsg(QtDebugMsg, "ERROR: "+ mSocketPlc->errorString());
    if (socketError == QAbstractSocket::HostNotFoundError || socketError == QAbstractSocket::ConnectionRefusedError) {
        QString plcIP = readIniFileByKay("TCP", "PLCIP");
        int plcPort = readIniFileByKay("TCP", "PLCPort").toInt();
        outPutMsg(QtDebugMsg, "Trying to reconnect...IP:" + plcIP + " port:"+ QString::number(plcPort));
        mSocketPlc->connectToHost(plcIP, plcPort);
    }
}
void CacheCar::onTcpConnected_plc() {
    outPutMsg(QtDebugMsg, "CacheCar::onTcpConnected_plc Connected to host ");
}

void CacheCar::onTcpDisconnected_plc() {
    outPutMsg(QtDebugMsg, "CacheCar::onTcpDisconnected_plc Disconnected from host ");
    QString plcIP = readIniFileByKay("TCP", "PLCIP");
    int plcPort = readIniFileByKay("TCP", "PLCPort").toInt();
    outPutMsg(QtDebugMsg, "Trying to reconnect...IP:" + plcIP + " port:" + QString::number(plcPort));
    mSocketPlc->connectToHost(plcIP, plcPort);
}

3.服务器发的信息调用onReadData_plc槽函数

cpp 复制代码
// 读取信息的槽函数
void CacheCar::onReadData_plc() {
    if (mSocketPlc == NULL) {
        return;
    }
    if (mSocketPlc->bytesAvailable() > 0) {
        QByteArray data = mSocketPlc->readAll(); // 读取所有可用数据
        QString messageStr = QString::fromUtf8(data);
        outPutMsg(QtDebugMsg, "CacheCar::onReadData_plc messageStr: " + messageStr);
        if (messageStr.size() < 8) {
            return;
        }
        else {
            // 解析响应,区分是测量返回、还是收到任务的响应、还是完成任务的响应
            QStringList msgList = messageStr.split(",");
            if (msgList.size() < 2) {
                return;
            }
            int funcId = msgList[0].toInt();
            if (funcId == 101) { // 测宽高
                if (msgList.size() < 5) {
                    return;
                }
                QString barcodeRev = msgList[1];
               
                for (int i = 0; i < windowList.size(); i++) {
                    if (windowList[i].barCode.compare(barcodeRev) != 0) {
                        continue;
                    }
                    // 处理该信息
                    windowList[i].boxW = msgList[3].toDouble()/1000.0;
                    windowList[i].boxH = msgList[4].toDouble()/1000.0;
                     if(true/*校验窗户尺寸合适*/) {
                         windowList[i].flag = 1;
                     }
                     else {
                         // 调用服务器接口报错
                         QString msgStr = getLanguageStr("窗户信息不一致,无法入库", 
                             "The window information is inconsistent and cannot be stored in the warehouse", 
                             "Niespójne informacje o oknie, nie można go zapisać"); 
                         errorFeedBack(windowList[i].channelId, msgStr,1);
                     }
                     break;
                }
                QString boxWStr = "", boxHStr = "";
                boxWStr = QString::number(msgList[3].toDouble()/1000.0);
                boxHStr = QString::number(msgList[4].toDouble()/1000.0);
                int channelNo = msgList[2].toInt();
                if (channelNo == 1) {
                    ui.auto_lineEdit_channelW_1->setText(boxWStr);
                    ui.auto_lineEdit_channelH_1->setText(boxHStr);
                    setLableBackGroundColor(ui.auto_label_status2_channel_1, "测量完成", GREEN);
                } else if (channelNo == 2) {
                    ui.auto_lineEdit_channelW_2->setText(boxWStr);
                    ui.auto_lineEdit_channelH_2->setText(boxHStr);
                    setLableBackGroundColor(ui.auto_label_status2_channel_2, "测量完成", GREEN);
                }else if (channelNo == 3) {
                    ui.auto_lineEdit_channelW_3->setText(boxWStr);
                    ui.auto_lineEdit_channelH_3->setText(boxHStr);
                    setLableBackGroundColor(ui.auto_label_status2_channel_3, "测量完成", GREEN);
                }
                else if (channelNo == 4) {
                    ui.auto_lineEdit_channelW_4->setText(boxWStr);
                    ui.auto_lineEdit_channelH_4->setText(boxHStr);
                    setLableBackGroundColor(ui.auto_label_status2_channel_4, "测量完成", GREEN);
                }
                setChancelStatus(channelNo, getLanguageStr("测量完成", "Complete", "Zakończone"), GREEN);
                outPutMsg(QtDebugMsg, "CacheCar::onReadData_plc 测宽高: " + msgList[1]);
            } else if (funcId == 102) { // 入库出库任务返回
                for (int i = 0; i < windowList.size(); i++) {
                    if (windowList[i].commandId == msgList[1].toInt()) {
                        windowList.removeAt(i--); // 任务已下发给PLC移除
                        break;
                    }
                }
                updateWaitTaskList(); // 更新任务
                outPutMsg(QtDebugMsg, "CacheCar::onReadData_plc 入库出库任务返回: " + msgList[1]);
            }
            else if (funcId == 103) { // 入库出库完成返回
                endFeedBack(msgList[1].toInt());
                outPutMsg(QtDebugMsg, "CacheCar::onReadData_plc 入库出库任务返回: " + msgList[1]);
            }
        }
    }
}
相关推荐
zero15972 小时前
Python 8天极速入门笔记(大模型工程师专用):第三篇-列表与字典(Python核心数据结构,大模型必备)
开发语言·python·ai编程
书到用时方恨少!2 小时前
Python 零基础入门系列(十一):模块和包
开发语言·python
Lenyiin2 小时前
深度剖析 C 语言标准IO库:stdio 实现原理与实战指南
c语言·开发语言
阿kun要赚马内2 小时前
Python面向对象编程:封装性
开发语言·python
郝学胜-神的一滴2 小时前
巧解括号序列分解问题:栈思想的轻量实现
开发语言·数据结构·c++·算法·面试
代码改善世界2 小时前
【C++初阶】string类(一):从基础到实战
开发语言·c++
计算机安禾2 小时前
【数据结构与算法】第15篇:队列(二):链式队列的实现与应用
c语言·开发语言·数据结构·c++·学习·算法·visual studio