Qt Creator 网络编程----Socket客户端&服务端

发布于:2025-05-09 ⋅ 阅读:(3) ⋅ 点赞:(0)

1、在Qt项目中的.pro中添加 network模块,用于Socket网络编程使用

QT       += network

2、相关Tcp网络通信协议头文件

#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QtNetwork/QHostAddress>

3、Qt socket运行实例。关于IP地址和Port端口号的设置:服务器代码设置的是服务器IP地址和Port端口号,客户端设置的是需要连接的服务器地址和端口号。所以不管是服务器还是客户端都设置的是服务器地址和端口号。

服务器端widget.h代码:
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>

#include <QTableView>
#include <QStandardItemModel>
#include <QHeaderView>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

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

private:
    // 初始化server操作
    void initServer();
    // close server
    void closeServer();
    // 更新当前状态
    void updateState();

    // 设置数据表格tableView
    void setTableView();

private:
    Ui::Widget *ui;
    //server用于监听端口,获取新的tcp连接的描述符
     QTcpServer *server;
     //存储已连接的socket对象
     QList<QTcpSocket*> clientList;
     // 设置表格模式
     QStandardItemModel *model;
};
#endif // WIDGET_H

服务器端widget.cpp代码:

#include "widget.h"
#include "ui_widget.h"

#include<QtNetwork/QHostAddress>
#include <QMessageBox>

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

    // 设置tableList
    setTableView();

    setWindowTitle("Server");
    initServer();
    ui->btnListen->setStyleSheet("color: rgb(0, 100, 100); background-color: rgb(255, 100, 50);");
}

Widget::~Widget()
{
    // 关闭server
    closeServer();
    delete ui;
}

void Widget::initServer()
{
    // 创建Server对象
    server = new QTcpServer(this);

    //点击监听按钮,开始监听
        connect(ui->btnListen,&QPushButton::clicked,[this]{
            //判断当前是否已开启,是则close,否则listen
            if(server->isListening()){
                //server->close();
                closeServer();
                //关闭server后恢复界面状态
                ui->btnListen->setText("Listen");
                ui->btnListen->setStyleSheet("color: rgb(0, 100, 100); background-color: rgb(255, 100, 50);");
                ui->editAddress->setEnabled(true);
                ui->editPort->setEnabled(true);
            }
            else
            {
                //从界面上读取ip和端口
                //可以使用 QHostAddress::Any 监听所有地址的对应端口

                const QString address_text=ui->editAddress->text();
                const QHostAddress address=(address_text=="Any")
                        ?QHostAddress::Any
                       :QHostAddress(address_text);
                const unsigned short port=ui->editPort->text().toUShort();
                //开始监听,并判断是否成功
                if(server->listen(address,port)){
                    //连接成功就修改界面按钮提示,以及地址栏不可编辑
                    ui->btnListen->setText("Close");
                    ui->btnListen->setStyleSheet("color: rgb(100, 100, 100); background-color: rgb(0, 255, 0);");
                    ui->editAddress->setEnabled(false);
                    ui->editPort->setEnabled(false);
                }
                else
                    QMessageBox::information(NULL, "提示", "服务器ip设置错误", QMessageBox::Yes);

            }
            updateState();
        });

        //监听到新的客户端连接请求
           connect(server,&QTcpServer::newConnection,this,[this]{
               //如果有新的连接就取出
               while(server->hasPendingConnections())
               {
                   //nextPendingConnection返回下一个挂起的连接作为已连接的QTcpSocket对象
                   //套接字是作为服务器的子级创建的,这意味着销毁QTcpServer对象时会自动删除该套接字。
                   //最好在完成处理后显式删除该对象,以避免浪费内存。
                   //返回的QTcpSocket对象不能从另一个线程使用,如有需要可重写incomingConnection().
                   QTcpSocket *socket=server->nextPendingConnection();
                   clientList.append(socket);
                   ui->textRecv->setTextColor(QColor(0,200,0));
           //      ui->textRecv->clear();
                   ui->textRecv->append(QString("[%1:%2] Soket Connected")
                                        .arg(socket->peerAddress().toString())
                                        .arg(socket->peerPort()));

                   //关联相关操作的信号槽
                   //收到数据,触发readyRead
                   connect(socket,&QTcpSocket::readyRead,[this,socket]{
                       //没有可读的数据就返回
                       if(socket->bytesAvailable()<=0)
                           return;
                       //注意收发两端文本要使用对应的编解码
                    QByteArray data = socket->readAll();
                    QStandardItem *item;
                    quint16 comb;
                    // 棉结数值
                    comb  = (uchar)data[10]<<8 | (uchar)data[11];
                    item = new QStandardItem(QString::number(comb));
                    model->setItem(0, 1, item);
                    // 短粗1
                    comb = (uchar)data[12]<<8 | (uchar)data[13];
                    item = new QStandardItem(QString::number(comb));
                    model->setItem(1, 1, item);
                    // 短粗2
                    comb = (uchar)data[14]<<8 | (uchar)data[15];
                    item = new QStandardItem(QString::number(comb));
                    model->setItem(2, 1, item);
                    // 长粗
                    comb = (uchar)data[16]<<8 | (uchar)data[17];
                    item = new QStandardItem(QString::number(comb));
                    model->setItem(3, 1, item);

                    // 细节1
                    comb = (uchar)data[18]<<8 | (uchar)data[19];
                    item = new QStandardItem(QString::number(comb));
                    model->setItem(0, 3, item);
                    // 细节2
                    comb = (uchar)data[20]<<8 | (uchar)data[21];
                    item = new QStandardItem(QString::number(comb));
                    model->setItem(1, 3, item);
                    // 特殊纱疵

                    comb = (uchar)data[22]<<8 | (uchar)data[23];
                    item = new QStandardItem(QString::number(comb));
                    model->setItem(2, 3, item);
                    // 纱支变异

                    comb  = (uchar)data[24]<<8 | (uchar)data[25];
                    item = new QStandardItem(QString::number(comb));
                    model->setItem(3, 3, item);

                    // 纱疵总数
                    comb = (uchar)data[26]<<8 | (uchar)data[27];
                    item = new QStandardItem(QString::number(comb));
                    model->setItem(0, 5, item);
                    // 十万米纱疵数
                    comb = (uchar)data[28]<<8 | (uchar)data[29];
                    item = new QStandardItem(QString::number(comb*0.1,'f',1));
                    model->setItem(1, 5, item);
                    // 产量
                    quint16 comb2;
                    quint32 comb12;
                    comb = (uchar)data[30]<<8 | (uchar)data[31];
                    comb2 = (uchar)data[32]<<8 | (uchar)data[33];
                    comb12 = comb*65536 + comb2;
                    item = new QStandardItem(QString::number(comb12*0.0001,'f',4));
                    model->setItem(2, 5, item);
                    // 效率
                    comb = (uchar)data[34]<<8 | (uchar)data[35];
                    item = new QStandardItem(QString::number(comb*0.01,'f',2));
                    model->setItem(3, 5, item);

                    // 驱动单元连接个数
                    comb = (uchar)data[36]<<8 | (uchar)data[37];
                    ui->driveEdit->setText(QString::number(comb));

                    // 处理单元连接个数
                    comb = (uchar)data[38]<<8 | (uchar)data[39];
                    ui->processEdit->setText(QString::number(comb));



/*
                     const QString recv_text=QString::fromUtf8(socket->readAll());    // 读取客户端发送的内容
                       ui->textRecv->clear();
                       ui->textRecv->append(QString("[%1:%2]")                          // 在textEdit写入客户端ip和port
                                            .arg(socket->peerAddress().toString())
                                            .arg(socket->peerPort()));
*/
                //       ui->textRecv->append(recv_text);                                 // 在textEdit吸入客户端发送内容

                   });

                      //错误信息
                   connect(socket, static_cast<void(QAbstractSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error),
                                       [this,socket](QAbstractSocket::SocketError)
                   {
               //       ui->textRecv->clear();
                        ui->textRecv->append(QString("[%1:%2] Soket Error:%3")
                                             .arg(socket->peerAddress().toString())
                                             .arg(socket->peerPort())
                                             .arg(socket->errorString()));
                   });
                   //连接断开,销毁socket对象,这是为了开关server时socket正确释放
                            connect(socket,&QTcpSocket::disconnected,[this,socket]{
                                socket->deleteLater();
                                clientList.removeOne(socket);
                                ui->textRecv->setTextColor(QColor(200,0,0));
                        //      ui->textRecv->clear();
                                ui->textRecv->append(QString("[%1:%2] Soket Disonnected")
                                                     .arg(socket->peerAddress().toString())
                                                     .arg(socket->peerPort()));

                                updateState();
                            });
                        }
                        updateState();
                    });
}
void Widget::closeServer()
{
    //停止服务
       server->close();
       for(QTcpSocket * socket:clientList)
       {
           //断开与客户端的连接
           socket->disconnectFromHost();
           if(socket->state()!=QAbstractSocket::UnconnectedState){
               socket->abort();
           }
       }
}

void Widget::updateState()
{
    //将当前server地址和端口、客户端连接数写在标题栏
      if(server->isListening())
      {
          setWindowTitle(QString("Server[%1:%2] connections:%3")
                         .arg(server->serverAddress().toString())
                         .arg(server->serverPort())
                         .arg(clientList.count()));

      }
      else
      {
          setWindowTitle("Server");
      }
}

void Widget::setTableView()
{
    // 创建模型 - 5行6列
    model = new  QStandardItemModel(4,6);

    // 设置表头
     model->setHorizontalHeaderLabels({"类型","个数", "类型","个数","统计","Value"});
     ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);     // tableView只读
     ui->tableView->horizontalHeader()->setStyleSheet("QHeaderView::section{background:rgb(150,150,150);}");   // tableView设置head颜色
     ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);  // tableView自适应
     ui->tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
     // 填充数据
    QStandardItem *item;
    item = new QStandardItem(QString("棉结"));
    model->setItem(0, 0, item);
    item = new QStandardItem(QString("短粗1"));
    model->setItem(1, 0, item);
    item = new QStandardItem(QString("短粗2"));
    model->setItem(2, 0, item);
    item = new QStandardItem(QString("长粗"));
    model->setItem(3, 0, item);

    item = new QStandardItem(QString("细节1"));
    model->setItem(0, 2, item);
    item = new QStandardItem(QString("细节2"));
    model->setItem(1, 2, item);
    item = new QStandardItem(QString("特殊纱疵"));
    model->setItem(2, 2, item);
    item = new QStandardItem(QString("纱支变异"));
    model->setItem(3, 2, item);

    item = new QStandardItem(QString("纱疵总数"));
    model->setItem(0, 4, item);
    item = new QStandardItem(QString("纱疵数[十万米]"));
    model->setItem(1, 4, item);
    item = new QStandardItem(QString("产量[千克]"));
    model->setItem(2, 4, item);
    item = new QStandardItem(QString("效率(%)"));
    model->setItem(3, 4, item);

    ui->tableView->verticalHeader()->setVisible(false);
    ui->tableView->setModel(model);
}
客户端用NetAssist.exe工具即可,设置如下:

1)选择协议类型:Tcp Client 

2)远程主机地址:服务器地址(我的本机地址:192.168.0.187)

3)远程主机端口:服务器端口号(我的本机端口号:8080)


网站公告

今日签到

点亮在社区的每一天
去签到