QT的socket服务端连接

对于一个联网的设备,socket长连接再熟悉不过了。那么下位机的demo待我整理后给大家分享出来。

环境:

  • 下位机:
    • GD32F303(工作中用的)
      • CAT14G模块配合工作使用
      • CAT1配置为socket连接

    • ESP32S3(个人的小爱好,做了一个小手表)
      • wifi连接,http配网
      • socket连接

  • 上位机:
    • QT(平时改动比较大,所以界面很丑,大家不要介意哈)
    • 串口连接
    • socket连接

那么我们开始吧

1、下位机

GD32F303+CAT1模组

请参考:CAT1模组AT指令自动配置

通过AT指令配置CAT1 4G模组icon-default.png?t=N7T8https://zhuanlan.zhihu.com/p/646779417

ESP32S3

后期整理好啦再更新到这

2、上位机

首先你需要有这个界面

本地IP地址

用于socket连接

本地端口

用于socket连接

侦听按钮

触发侦听事件

消息发送接收

那么开始愉快的码代码吧!

项目中的.pro文件中需要添加这个哦

QT += network

初始化socke

//头文件要加上这三个
#include <QTcpserver>
#include <QTcpSocket>
#include <QNetworkInterface>
​
void MainWindow::vsocketserverInit(void){
    //客户端
    socket = new QTcpSocket();
    //服务端
    server = new QTcpServer();
    //UDP可以广播的,实际没有使用
    m_pUdpSocket = new QUdpSocket();
​
    // 获取本地的IP
    for (int i = 0; i < QNetworkInterface().allAddresses().length(); ++i) {
        ui->comboBoxData_Socket_Server_IP->addItem(QNetworkInterface().allAddresses().at(i).toString());
    }
    ui->comboBoxData_Socket_Server_IP->setCurrentText("127.0.0.1");
    ui->comboBoxData_Socket_Server_COM->addItem("8090");
    ui->comboBoxData_Socket_Server_Select_Mode->addItem("指定模式");
    ui->comboBoxData_Socket_Server_Select_Mode->addItem("广播模式");
​
    QPalette pe;
    pe.setColor(QPalette::WindowText, Qt::blue);
    ui->label_Socket_Server_Status->setPalette(pe);
    ui->label_Socket_Server_Status->setText("服务器未打开");
​
    // 关联客户端连接信号newConnection
    connect(server, &QTcpServer::newConnection, this, &MainWindow::server_New_Connect);
​
    //创建一个查询的定时器
    GetControllerID_Timer = new QTimer;
​
    connect(GetControllerID_Timer, &QTimer::timeout,this,&MainWindow::vsocketGetControllerID_Send_timer);
    GetControllerID_Timer->stop();
}

侦听按键

void MainWindow::on_pushButton_Socket_Server_Connect_clicked()
{
    if (ui->pushButton_Socket_Server_Connect->text() == tr("侦听")) {
        // 从输入端获取端口号
        int port = ui->comboBoxData_Socket_Server_COM->currentText().toInt();
​
        // 侦听指定的端口
        if(!server->listen(QHostAddress::Any, port)) {
            // 若出错,则输出错误信息
            QMessageBox::information(this, tr("错误"), server->errorString(), QMessageBox::Yes);
            return;
        }
        else {
            // 修改按键文字
            ui->pushButton_Socket_Server_Connect->setText("取消侦听");
            QPalette pe;
            pe.setColor(QPalette::WindowText, Qt::red);
            ui->label_Socket_Server_Status->setPalette(pe);
            ui->label_Socket_Server_Status->setText("服务器运行中...");
        }
​
        //-----------------------------启动FRPS-----------------------------
        //这里我是自己搭建了内网穿透,将本地端口映射到服务器的公网上,相当于可以联网控制了
        bfrpsstart(true);
    }
    else {
        // 如果正在连接......
        if(socket->state() == QAbstractSocket::ConnectedState) {
            // 关闭连接
            socket->disconnectFromHost();
        }
        // 取消侦听
        server->close();
        // 修改按键文字
        ui->pushButton_Socket_Server_Connect->setText("侦听");
        QPalette pe;
        pe.setColor(QPalette::WindowText, Qt::blue);
        ui->label_Socket_Server_Status->setPalette(pe);
        ui->label_Socket_Server_Status->setText("服务器未打开");
​
        ui->tableWidget_Socket_Server->clear();
        //-----------------------------关闭FRPS-----------------------------
        bfrpsstart(false);
    }
}

新客户端连接

//连接
void MainWindow::server_New_Connect()
{
    // 获取客户端连接
    socket = server->nextPendingConnection();
    clientSocket.append(socket);
​
    // 把连接到的客户端添加入tableWidget中
    int currentRow = ui->tableWidget_Socket_Server->rowCount();
    ui->tableWidget_Socket_Server->insertRow(currentRow);
​
    QTableWidgetItem *item = new QTableWidgetItem();
    QTableWidgetItem *item_2 = new QTableWidgetItem();
    QTableWidgetItem *item_3 = new QTableWidgetItem();
    QTableWidgetItem *item_4 = new QTableWidgetItem();
    QTableWidgetItem *item_5 = new QTableWidgetItem();
​
    item->setText(tr("%1").arg(QString::number(ui->tableWidget_Socket_Server->rowCount())));
    item_2->setText(clientSocket[currentRow]->peerAddress().toString().mid(7));
    item_3->setText(QString::number(clientSocket[currentRow]->peerPort()));
    item_4->setText("00000000");
    item_5->setText("在线");
​
    ui->tableWidget_Socket_Server->setItem(currentRow, 0, item);
    ui->tableWidget_Socket_Server->setItem(currentRow, 1, item_2);
    ui->tableWidget_Socket_Server->setItem(currentRow, 2, item_3);
    ui->tableWidget_Socket_Server->setItem(currentRow, 3, item_4);
    ui->tableWidget_Socket_Server->setItem(currentRow, 4, item_5);
​
    // 连接QTcpSocket的信号槽,以读取新数据
    connect(socket, SIGNAL(readyRead()), this, SLOT(Socket_Server_Recieve_Data()));
    connect(socket, SIGNAL(disconnected()), this, SLOT(Socket_Server_Dis_Connected()));
​
    //--------------------------根据自己的需求添加------------------------------
    //连接上之后发配置时间,我这里是业务需要
    QString checktimedata = "000000000000000000000000";
    checktimedata.append(checktime());
    checktimedata.append(qstrCrcCalc(checktimedata));
    checktimedata.append("FE");
    checktimedata.insert(0,"FB");
    vSocket_Server_Send(checktimedata);
    //----------------------------------------------------------------------
}
​
//取消连接
void MainWindow::Socket_Server_Dis_Connected()
{
    // 遍历寻找断开连接的是哪一个客户端
    for(int i = 0; i < clientSocket.length(); ++i) {
        //判断连接状态
        if(clientSocket[i]->state() == QAbstractSocket::UnconnectedState)
        {
            // 删除存储在tableWidget中的该客户端信息
            for (int j = 0; j < ui->tableWidget_Socket_Server->rowCount(); ++j) {
                //判断IP是否和显示数据的IP一致
                if (clientSocket[i]->peerAddress().toString().mid(7) == ui->tableWidget_Socket_Server->item(j, 1)->text()) {
                    //判断端口是否一致,一致说明断开连接的就是这个客户端
                    QString socketport = QString::number(clientSocket[i]->peerPort());
                    if(socketport==ui->tableWidget_Socket_Server->item(j, 2)->text()){
                        ui->tableWidget_Socket_Server->removeRow(j);
                    }
                }
            }
            // 删除存储在clientSocket列表中的客户端信息
            clientSocket[i]->destroyed();
            clientSocket.removeAt(i);
        }
    }
}

处理发送

//发送,我这里有自己的业务需求,根据控制器的ID来找到所对应的IP和端口,去发送
void MainWindow::vSocket_Server_Send_Flechazo(QString sendstr)
{
    QString data = sendstr;
    if (data.isEmpty()) {
        QMessageBox::information(this, "提示", "请输入发送内容!", QMessageBox::Yes);
    }
    else {
        if(ui->comboBoxData_Socket_Server_Select_Mode->currentText()=="指定模式"){
​
            //获取控制器ID
            QString ControllerID = ui->comboBox_ControlID->currentText();
            //找控制器对应的端口
            QString socketPort="";
            QString socketIP="";
            for(int i=0;i<ui->tableWidget_Socket_Server->rowCount();i++){
                if(ControllerID == ui->tableWidget_Socket_Server->item(i, 3)->text()){
                    socketPort = ui->tableWidget_Socket_Server->item(i, 2)->text();
                    socketIP = ui->tableWidget_Socket_Server->item(i, 1)->text();
                    break;
                }
            }
            if(socketPort==""){
                QMessageBox::information(this, tr("提示消息"), tr("没有找到该控制器对应的socket端口!"), QMessageBox::Ok);
                return;
            }
            //拿到端口后去找,因为服务器分配的端口是唯一的,所以就不判断IP啦,懒 
            for (int i = 0; i < clientSocket.length(); ++i) {
                if (QString::number(clientSocket[i]->peerPort()) == socketPort) {
                    //以ASCII码形式发送文本框内容
                    clientSocket[i]->write(qstring_to_qbytearray(data));
​
                    //显示数据流
                    QString timeStrLine="["+QDateTime::currentDateTime().toString("hh:mm:ss")+"][发送]:  ";
                    QString content_l = "<span style=\" color:red;\">"+timeStrLine +data+"\n\r</span>";
                    ui->textBrowser->append(content_l);
                }
            }
​
        }else if(ui->comboBoxData_Socket_Server_Select_Mode->currentText()=="广播模式"){
            for (int i = 0; i < clientSocket.length(); ++i) {
                    //以ASCII码形式发送文本框内容
                    clientSocket[i]->write(qstring_to_qbytearray(data));
            }
        }
    }
}

处理接收

//接收
void MainWindow::Socket_Server_Recieve_Data()
{
    // 由于readyRead信号并未提供SocketDecriptor,所以需要遍历所有客户端
    for (int i = 0; i < clientSocket.length(); ++i) {
        // 读取缓冲区数据
        QByteArray buffer = clientSocket[i]->readAll();
        if(buffer.isEmpty()) {
            continue;
        }
​
        static QString IP_Port, IP_Port_Pre;
        IP_Port = tr("[%1:%2]:").arg(clientSocket[i]->peerAddress().toString().mid(7)).arg(clientSocket[i]->peerPort());
​
        // 若此次消息的地址与上次不同,则需显示此次消息的客户端地址
        if (IP_Port != IP_Port_Pre) {
            ui->textEdit_Socket_Server_RecvData->append(IP_Port);
        }
​
        //---------------------根据需要来处理接收-------------------------------
        //buffer
        
        //---------------------根据需要来处理接收-------------------------------
​
        // 更新ip_port
        IP_Port_Pre = IP_Port;
    }
}

那么到这基本就结束了!

有了socket,自己的小产品才有了灵魂好吧。随时随地,都可以远程控制它了!

关于frp内网穿透[将本地端口映射到服务器]

教程请看云服务器搭建内网穿透

云服务器搭建Frps实现内网穿透icon-default.png?t=N7T8https://zhuanlan.zhihu.com/p/635462920

QT对应的部分后期会整理成《QT一键启动frpc内网穿透》

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FlechazoCLF

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值