【QT】TCP通信(QTcpServer 和 QTcpSocket)

news2024/9/22 10:00:03

目录

  • 1. TCP通信概述
  • 2. QTcpServer
    • 2.1 公共函数
    • 2.2 信号
    • 2.3 保护函数
  • 3. QTcpSocket
    • 3.1 公共函数
    • 3.2 信号
  • 4. 代码示例
    • 4.1 服务器端
      • MainWindow.h
      • MainWindow.cpp
    • 4.2 客户端
      • MainWindow.h
      • MainWindow.cpp
    • 4.3 界面显示

1. TCP通信概述

TCP是一种被大多数Internet网络协议(如HTTP)用于数据传输的低级网络协议,它是可靠的、面向流、面向连接的传输协议,特别适合于连续数据传输。

TCP通信必须先建立TCP连接,分为服务器端和客户端。
Qt提供QTcpServer类和QTcpSocket类用于建立TCP通信。
服务器端必须使用QTcpServer用于端口监听,建立服务器;QTcpSocket用于建立连接后使用套接字进行通信。

2. QTcpServer

QTcpServer继承于QObject

2.1 公共函数

void close() 关闭服务器,停止网络监听

bool listen(address, port) 在给定IP地址和端口上开始监听,若成功返回true。address = QHostAddress addr(IP)

bool isListening() 返回true表示服务器处于监听状态

QTcpSocket* nextPendingConnection() 返回下一个等待接入的连接。套接字是作为服务器的子节点创建的,这意味着当QTcpServer对象被销毁时,它将被自动删除。在使用完对象后显式地删除它,以避免浪费内存。如果在没有挂起连接时调用此函数,则返回0。注意:返回的QTcpSocket对象不能从其他线程使用。如果您想使用来自另一个线程的传入连接,则需要重写incomingConnection()。

QHostAddress serverAddress() 若服务器处于监听状态,返回服务器地址

quint16 serverPort() 若服务器处于监听状态,返回服务器监听端口

bool waitForNewConnection() 以阻塞的方式等待新的连接

2.2 信号

void acceptError(QAbstractSocket::SocketError socketError) 当接受一个新的连接发生错误时发射此信号,参数socketError描述了错误信息

void newConnection() 当有新的连接时发射此信号

2.3 保护函数

void incomingConnection(qintptr socketDescriptor) 当有新的连接可用时,QTcpServer内部调用此函数,创建一个QTcpServer对象,添加到内部可用新连接列表,然后发射newConnection()信号。用户若从QTcpServer继承定义类,可以重定义此函数,但必须调用addPendingConnection()。qintptr根据系统类型不同而不同,32位为qint32,64位为qint64。

void addPendingConnection(QTcpSocket* socket) 由incomingConnection()调用,将创建的QTcpSocket添加到内部新可用连接列表。

3. QTcpSocket

QTcpSocket是从QIODevice间接继承的

QIODevice -> QAbstractSocket -> QTcpSocket

QTcpSocket类除了构造和析构,其他函数都是从QAbstractSocket继承或重定义的

3.1 公共函数

void connectToHost(QHostAddress& address, quint16 port) 以异步方式连接到指定IP地址和端口的TCP服务器,连接成功会发送connected()信号

void disconnectFromHost() 断开socket,关闭成功后会发射disconnected()信号

bool waitForConnected() 等待直到建立socket连接

bool waitForDisconnected() 等待直到断开socket连接

QHostAddress localAddress() 返回本socket的地址

quint16 localPort 返回本socket的端口

QHostAddress peerAddress() 在已连接状态下,返回对方socket的地址

QString peerName() 返回connectToHost()连接到的对方的主机名

quint16 peerPort() 在已连接状态下,返回对方socket的端口

qint64 readBufferSize() 返回内部读取缓冲区的大小,该大小决定了read()和realAll()函数能读出的数据的大小

void setReadBufferSize(qint64 size) 设置内部读取缓冲区的大小

qint64 bytesAvailable() 返回需要读取的缓冲区的数据的字节数

bool canReadLine() 如果有行数据要从socket缓冲区读取,则返回true

SocketState state() 返回socket当前的状态

3.2 信号

void connected() connectToHost()成功连接到服务器后会发射此信号

void disconnected() 断开socket连接后会发射此信号

void error(QAbstractSocket::SocketError socketError) 当socket发生错误时会发射此信号

void hostFound() 调用connectToHost()找到主机后会发射此信号

void stateChanged(QAbstractSocket::SocketState socketState) 当socket状态发生变化时会发射此信号,参数socketState表示socket当前的状态

void readyRead() 当缓冲区有新数据需要读取时会发射此信号,在此信号的槽函数里读取缓冲区的数据

4. 代码示例

4.1 服务器端

使用Qt网络模块,需要在配置文件.pro中添加:

Qt += network

MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QAction>
#include <QCloseEvent>
#include <QComboBox>
#include <QGridLayout>
#include <QHostInfo>
#include <QLabel>
#include <QLineEdit>
#include <QMainWindow>
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QSpinBox>
#include <QTcpServer>
#include <QTcpSocket>

namespace Ui {
    class MainWindow;
}

class MainWindow : public QMainWindow {
    Q_OBJECT

public:
    explicit MainWindow(QWidget* parent = 0);
    ~MainWindow();

protected:
    void closeEvent(QCloseEvent* event);

private slots:
    //工具栏按钮
    void slotActStartTriggered();
    void slotActStopTriggered();
    void slotActClearTriggered();
    void slotActQuitTriggered();

    //界面按钮
    void slotBtnSendClicked();

    //自定义槽函数
    void slotNewConnection();       // QTcpServer的newConnection()信号
    void slotClientConnected();     //客户端socket连接
    void slotClientDisconnected();  //客户端socket断开连接
    void slotSocketStateChange(QAbstractSocket::SocketState socketState);
    void slotSocketReadyRead();  //读取socket传入的数据

private:
    Ui::MainWindow* ui;

    QAction* m_pActStartListen;
    QAction* m_pActStopListen;
    QAction* m_pActClearText;
    QAction* m_pActQuit;
    QWidget* m_pCentralWidget;
    QGridLayout* m_pGridLayout;
    QLabel* m_pLabel1;
    QComboBox* m_pComBoxIP;
    QLabel* m_pLabel2;
    QSpinBox* m_pSpinBoxPort;
    QLineEdit* m_pLineEdit;
    QPushButton* m_pBtnSend;
    QPlainTextEdit* m_pPlainText;
    QLabel* m_pLabListenStatus;  //状态栏标签
    QLabel* m_pLabSocketState;   //状态栏标签
    QTcpServer* m_pTcpServer;    // TCP服务器
    QTcpSocket* m_pTcpSocket;    // TCP通信的socket

    QString getLocalIP();  //获取本机IP地址
};

#endif  // MAINWINDOW_H

MainWindow.cpp

#include "mainwindow.h"

#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);
    this->setWindowTitle(QStringLiteral("TCP服务端"));
    ui->menuBar->hide();

    //工具栏
    ui->mainToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);  //设置工具栏显示风格(图标在上文字在下)
    m_pActStartListen = new QAction(QIcon(":/new/prefix1/res/开始.png"), QStringLiteral("开始监听"), this);
    m_pActStopListen = new QAction(QIcon(":/new/prefix1/res/暂停.png"), QStringLiteral("停止监听"), this);
    m_pActClearText = new QAction(QIcon(":/new/prefix1/res/清空数据.png"), QStringLiteral("清空文本框"), this);
    m_pActQuit = new QAction(QIcon(":/new/prefix1/res/退出.png"), QStringLiteral("退出"), this);
    ui->mainToolBar->addAction(m_pActStartListen);
    ui->mainToolBar->addAction(m_pActStopListen);
    ui->mainToolBar->addAction(m_pActClearText);
    ui->mainToolBar->addSeparator();
    ui->mainToolBar->addAction(m_pActQuit);

    //界面布局
    m_pCentralWidget = new QWidget(this);
    m_pGridLayout = new QGridLayout(m_pCentralWidget);
    m_pLabel1 = new QLabel(QStringLiteral("监听地址"), m_pCentralWidget);  //父对象为新的widget
    // label对象被析构了,所以不会在界面显示。label1需要作为成员变量才可显示或者用指针
    //    QLabel label1;
    //    label1.setText("监听地址");
    m_pComBoxIP = new QComboBox(m_pCentralWidget);
    // m_pComBoxIP->addItem("192.168.1.104");  // QComboBox设置默认项必须添加项
    //必须先add项才能操作下面
    //    comBoxAddr->setCurrentIndex(0);
    //    comBoxAddr->setCurrentText("192.168.1.104");
    m_pLabel2 = new QLabel(QStringLiteral("监听端口"), m_pCentralWidget);
    m_pSpinBoxPort = new QSpinBox(m_pCentralWidget);
    m_pSpinBoxPort->setMinimum(1);
    m_pSpinBoxPort->setMaximum(65535);  //端口范围1~65535
    m_pLineEdit = new QLineEdit(m_pCentralWidget);
    m_pBtnSend = new QPushButton(QStringLiteral("发送消息"), m_pCentralWidget);
    m_pPlainText = new QPlainTextEdit(m_pCentralWidget);
    m_pGridLayout->addWidget(m_pLabel1, 0, 0, Qt::AlignRight);
    // GLayout->addWidget(&label1, 0, 0, Qt::AlignRight);  //对象可以用&的方式
    m_pGridLayout->addWidget(m_pComBoxIP, 0, 1, 1, 2);
    m_pGridLayout->addWidget(m_pLabel2, 0, 3, Qt::AlignRight);
    m_pGridLayout->addWidget(m_pSpinBoxPort, 0, 4);
    m_pGridLayout->addWidget(m_pLineEdit, 1, 0, 1, 4);
    m_pGridLayout->addWidget(m_pBtnSend, 1, 4);
    m_pGridLayout->addWidget(m_pPlainText, 2, 0, 5, 5);
    this->setCentralWidget(m_pCentralWidget);

    //状态栏
    m_pLabListenStatus = new QLabel(QStringLiteral("监听状态:"));
    m_pLabListenStatus->setMinimumWidth(150);
    ui->statusBar->addWidget(m_pLabListenStatus);
    m_pLabSocketState = new QLabel(QStringLiteral("Socket状态:"));
    m_pLabSocketState->setMinimumWidth(200);
    ui->statusBar->addWidget(m_pLabSocketState);

    QString localIP = getLocalIP();
    this->setWindowTitle(this->windowTitle() + "---本机IP:" + localIP);
    m_pComBoxIP->addItem(localIP);

    m_pTcpServer = new QTcpServer(this);
    connect(m_pTcpServer, &QTcpServer::newConnection, this, &MainWindow::slotNewConnection);

    connect(m_pActStartListen, &QAction::triggered, this, &MainWindow::slotActStartTriggered);
    connect(m_pActStopListen, &QAction::triggered, this, &MainWindow::slotActStopTriggered);
    connect(m_pBtnSend, &QPushButton::clicked, this, &MainWindow::slotBtnSendClicked);
    connect(m_pActClearText, &QAction::triggered, this, &MainWindow::slotActClearTriggered);
    connect(m_pActQuit, &QAction::triggered, this, &MainWindow::slotActQuitTriggered);
}

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

void MainWindow::closeEvent(QCloseEvent* event) {
	//关闭窗口时停止监听
    if (m_pTcpServer->isListening())
        m_pTcpServer->close();  //停止网络监听
    QMessageBox::StandardButton button = QMessageBox::question(this, QStringLiteral(""), "是否退出?");
    if (button == QMessageBox::StandardButton::Yes) {
        event->accept();
    } else {
        event->ignore();
    }
}

void MainWindow::slotActStartTriggered() {
    //开始监听
    QString IP = m_pComBoxIP->currentText();
    quint16 port = m_pSpinBoxPort->value();
    QHostAddress addr(IP);
    m_pTcpServer->listen(addr, port);  //开始监听
    // m_pTcpServer->listen(QHostAddress::LocalHost, port);  //监听本机上某个端口
    // QHostAddress::LocalHost :IPv4本地主机地址。等价于QHostAddress("127.0.0.1")
    m_pPlainText->appendPlainText("**开始监听...");
    m_pPlainText->appendPlainText("**服务器地址:" + m_pTcpServer->serverAddress().toString());
    m_pPlainText->appendPlainText("**服务器端口:" + QString::number(m_pTcpServer->serverPort()));
    m_pActStartListen->setEnabled(false);  //开始之后不能再开始
    m_pActStopListen->setEnabled(true);
    m_pLabListenStatus->setText(QStringLiteral("监听状态:正在监听"));
}

void MainWindow::slotActStopTriggered() {
    //停止监听
    if (m_pTcpServer->isListening()) {
        m_pTcpServer->close();
        m_pActStartListen->setEnabled(true);
        m_pActStopListen->setEnabled(false);
        m_pLabListenStatus->setText("监听状态:已停止监听");
    }
}

void MainWindow::slotActClearTriggered() { m_pPlainText->clear(); }

void MainWindow::slotActQuitTriggered() { this->close(); }

void MainWindow::slotBtnSendClicked() {
    //发送一行字符串,以换行符结束
    QString msg = m_pLineEdit->text();
    m_pPlainText->appendPlainText("[out]: " + msg);
    m_pLineEdit->clear();
    m_pLineEdit->setFocus();  // 发送完设置焦点

    //字符串的传递一般都要转换为UTF-8格式,编译器不同可能导致乱码,UFTF-8格式是统一的格式,每个编译器都会带,防止乱码
    QByteArray str = msg.toUtf8();  //返回字符串的UTF-8格式
    str.append('\n');
    m_pTcpSocket->write(str);
}

void MainWindow::slotNewConnection() {
    m_pTcpSocket = m_pTcpServer->nextPendingConnection();  //获取socket
    connect(m_pTcpSocket, &QTcpSocket::connected, this, &MainWindow::slotClientConnected);
    // slotClientConnected();
    connect(m_pTcpSocket, &QTcpSocket::disconnected, this, &MainWindow::slotClientDisconnected);
    connect(m_pTcpSocket, &QTcpSocket::stateChanged, this, &MainWindow::slotSocketStateChange);
    slotSocketStateChange(m_pTcpSocket->state());
    connect(m_pTcpSocket, &QTcpSocket::readyRead, this, &MainWindow::slotSocketReadyRead);
}

void MainWindow::slotClientConnected() {
    //客户端接入时
    m_pPlainText->appendPlainText("**client socket connected");
    m_pPlainText->appendPlainText("**peer address: " + m_pTcpSocket->peerAddress().toString());  //对端的地址
    m_pPlainText->appendPlainText("**peer port: " + QString::number(m_pTcpSocket->peerPort()));  //对端的端口
}

void MainWindow::slotClientDisconnected() {
    //客户端断开连接时
    m_pPlainText->appendPlainText("**client socket disconnected");
    m_pTcpSocket->deleteLater();
}

void MainWindow::slotSocketStateChange(QAbstractSocket::SocketState socketState) {
    // socket状态变化时
    switch (socketState) {
        case QAbstractSocket::UnconnectedState: m_pLabSocketState->setText("socket状态:UnconnectedSate"); break;
        case QAbstractSocket::HostLookupState: m_pLabSocketState->setText("socket状态:HostLookupState"); break;
        case QAbstractSocket::ConnectingState: m_pLabSocketState->setText("socket状态:ConnectingState"); break;
        case QAbstractSocket::ConnectedState: m_pLabSocketState->setText("socket状态:ConnectedState"); break;
        case QAbstractSocket::BoundState: m_pLabSocketState->setText("socket状态:BoundState"); break;
        case QAbstractSocket::ClosingState: m_pLabSocketState->setText("socket状态:ClosingState"); break;
        case QAbstractSocket::ListeningState: m_pLabSocketState->setText("socket状态:ListeningState"); break;
    }
}

void MainWindow::slotSocketReadyRead() {
    //读取缓冲区行文本
    while (m_pTcpSocket->canReadLine()) {
        m_pPlainText->appendPlainText("[in]: " + m_pTcpSocket->readLine());
    }
}

QString MainWindow::getLocalIP() {
    //获取本机IPv4地址
    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;
}

4.2 客户端

使用Qt网络模块,需要在配置文件.pro中添加:

Qt += network

MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QAction>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QHostInfo>
#include <QLabel>
#include <QLineEdit>
#include <QMainWindow>
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QSpinBox>
#include <QTcpSocket>

namespace Ui {
    class MainWindow;
}

class MainWindow : public QMainWindow {
    Q_OBJECT

public:
    explicit MainWindow(QWidget* parent = 0);
    ~MainWindow();

protected:
    void closeEvent(QCloseEvent* event);

private slots:
    //工具栏按钮
    void slotActConnectTriggered();
    void slotActDisConnectTriggered();
    void slotActClearTriggered();
    void slotActQuitTriggered();

    //界面按钮
    void slotBtnSendClicked();

    //自定义槽函数
    void slotConnected();
    void slotDisconnected();
    void slotSocketStateChange(QAbstractSocket::SocketState socketState);
    void slotSocketReadyRead();

private:
    Ui::MainWindow* ui;

    QAction* m_pActConnectServer;
    QAction* m_pActDisconnect;
    QAction* m_pActClear;
    QAction* m_pActQuit;
    QWidget* m_pCentralWidget;
    QLabel* m_pLabel1;
    QLabel* m_pLabel2;
    QLineEdit* m_pLineEditIP;
    QSpinBox* m_pSpinBoxPort;
    QLineEdit* m_pLineEdit;
    QPushButton* m_pBtnSend;
    QPlainTextEdit* m_pPlainText;
    QLabel* m_pLabSocketState;
    QTcpSocket* m_pTcpClient;

    QString getLocalIP();
    void loadStyleFile();
};

#endif  // MAINWINDOW_H

MainWindow.cpp

#include "mainwindow.h"

#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);
    this->setWindowTitle(QStringLiteral("TCP客户端"));
    ui->menuBar->hide();

    // QSS样式
    loadStyleFile();

    //工具栏
    ui->mainToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    ui->mainToolBar->setMinimumHeight(50);
    m_pActConnectServer = new QAction(QIcon(":/new/prefix1/res/链接.png"), QStringLiteral("连接服务器"), this);
    m_pActDisconnect = new QAction(QIcon(":/new/prefix1/res/断开链接.png"), QStringLiteral("断开连接"), this);
    m_pActClear = new QAction(QIcon(":/new/prefix1/res/清空数据.png"), QStringLiteral("清空文本框"), this);
    m_pActQuit = new QAction(QIcon(":/new/prefix1/res/退出.png"), QStringLiteral("退出"), this);
    ui->mainToolBar->addAction(m_pActConnectServer);
    ui->mainToolBar->addAction(m_pActDisconnect);
    ui->mainToolBar->addAction(m_pActClear);
    ui->mainToolBar->addSeparator();
    ui->mainToolBar->addAction(m_pActQuit);

    //布局
    m_pCentralWidget = new QWidget(this);
    m_pLabel1 = new QLabel(QStringLiteral("服务器地址"), m_pCentralWidget);
    m_pLabel2 = new QLabel(QStringLiteral("端口"), m_pCentralWidget);
    m_pLineEditIP = new QLineEdit(m_pCentralWidget);
    m_pSpinBoxPort = new QSpinBox(m_pCentralWidget);
    m_pSpinBoxPort->setMinimum(1);
    m_pSpinBoxPort->setMaximum(65535);
    QHBoxLayout* HLay1 = new QHBoxLayout();
    HLay1->addWidget(m_pLabel1, 2);
    HLay1->addWidget(m_pLineEditIP, 6);
    HLay1->addWidget(m_pLabel2, 2, Qt::AlignRight);
    HLay1->addWidget(m_pSpinBoxPort, 3);
    m_pLineEdit = new QLineEdit(m_pCentralWidget);
    m_pBtnSend = new QPushButton(QStringLiteral("发送消息"), m_pCentralWidget);
    QHBoxLayout* HLay2 = new QHBoxLayout();
    HLay2->addWidget(m_pLineEdit, 10);
    HLay2->addWidget(m_pBtnSend, 2);
    m_pPlainText = new QPlainTextEdit(m_pCentralWidget);
    QGridLayout* GridLayout = new QGridLayout(m_pCentralWidget);
    GridLayout->addLayout(HLay1, 0, 0);
    GridLayout->addLayout(HLay2, 1, 0);
    GridLayout->addWidget(m_pPlainText);
    this->setCentralWidget(m_pCentralWidget);

    //状态栏
    m_pLabSocketState = new QLabel(this);
    m_pLabSocketState->setText(QStringLiteral("socket状态:"));
    ui->statusBar->addWidget(m_pLabSocketState);
    m_pLabSocketState->setMinimumWidth(150);

    QString localIP = getLocalIP();
    this->setWindowTitle(this->windowTitle() + "---本机IP:" + localIP);
    m_pLineEditIP->setText(localIP);

    m_pTcpClient = new QTcpSocket(this);
    connect(m_pActConnectServer, &QAction::triggered, this, &MainWindow::slotActConnectTriggered);
    connect(m_pActDisconnect, &QAction::triggered, this, &MainWindow::slotActDisConnectTriggered);
    connect(m_pActClear, &QAction::triggered, this, &MainWindow::slotActClearTriggered);
    connect(m_pActQuit, &QAction::triggered, this, &MainWindow::slotActQuitTriggered);
    connect(m_pBtnSend, &QPushButton::clicked, this, &MainWindow::slotBtnSendClicked);
    connect(m_pTcpClient, &QTcpSocket::connected, this, &MainWindow::slotConnected);
    connect(m_pTcpClient, &QTcpSocket::disconnected, this, &MainWindow::slotDisconnected);
    connect(m_pTcpClient, &QTcpSocket::stateChanged, this, &MainWindow::slotSocketStateChange);
    connect(m_pTcpClient, &QTcpSocket::readyRead, this, &MainWindow::slotSocketReadyRead);
}

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

void MainWindow::closeEvent(QCloseEvent* event) {
	//关闭之前断开连接
    if (m_pTcpClient->state() == QAbstractSocket::ConnectedState)
        m_pTcpClient->disconnectFromHost();
    QMessageBox::StandardButton button = QMessageBox::question(this, QStringLiteral(""), "是否退出?");
    if (button == QMessageBox::StandardButton::Yes) {
        event->accept();
    } else {
        event->ignore();
    }
}
void MainWindow::slotActConnectTriggered() {
    //连接到服务器按钮
    QString addr = m_pLineEditIP->text();
    quint16 port = m_pSpinBoxPort->value();
    m_pTcpClient->connectToHost(addr, port);
}

void MainWindow::slotActDisConnectTriggered() {
    //断开连接按钮
    if (m_pTcpClient->state() == QAbstractSocket::ConnectedState) {
        m_pTcpClient->disconnectFromHost();
    }
}

void MainWindow::slotActClearTriggered() { m_pPlainText->clear(); }

void MainWindow::slotActQuitTriggered() { this->close(); }

void MainWindow::slotBtnSendClicked() {
    //发送数据
    QString msg = m_pLineEdit->text();
    m_pPlainText->appendPlainText("[out]: " + msg);
    m_pLineEdit->clear();
    m_pLineEdit->setFocus();
    QByteArray str = msg.toUtf8();
    str.append('\n');
    m_pTcpClient->write(str);
}

void MainWindow::slotConnected() {
    // Connected()信号槽函数
    m_pPlainText->appendPlainText("**已连接到服务器");
    m_pPlainText->appendPlainText("**peer address: " + m_pTcpClient->peerAddress().toString());
    m_pPlainText->appendPlainText("**peer port: " + QString::number(m_pTcpClient->peerPort()));
    m_pActConnectServer->setEnabled(false);
    m_pActDisconnect->setEnabled(true);
}

void MainWindow::slotDisconnected() {
    // Disconnected()信号槽函数
    m_pPlainText->appendPlainText("**已断开与服务器的连接");
    m_pActConnectServer->setEnabled(true);
    m_pActDisconnect->setEnabled(false);
}

void MainWindow::slotSocketStateChange(QAbstractSocket::SocketState socketState) {
    switch (socketState) {
        case QAbstractSocket::UnconnectedState: m_pLabSocketState->setText("socket状态:UnconnectedSate"); break;
        case QAbstractSocket::HostLookupState: m_pLabSocketState->setText("socket状态:HostLookupState"); break;
        case QAbstractSocket::ConnectingState: m_pLabSocketState->setText("socket状态:ConnectingState"); break;
        case QAbstractSocket::ConnectedState: m_pLabSocketState->setText("socket状态:ConnectedState"); break;
        case QAbstractSocket::BoundState: m_pLabSocketState->setText("socket状态:BoundState"); break;
        case QAbstractSocket::ClosingState: m_pLabSocketState->setText("socket状态:ClosingState"); break;
        case QAbstractSocket::ListeningState: m_pLabSocketState->setText("socket状态:ListeningState"); break;
    }
}

void MainWindow::slotSocketReadyRead() {
    while (m_pTcpClient->canReadLine()) {
        m_pPlainText->appendPlainText("[in]: " + m_pTcpClient->readLine());
    }
}

QString MainWindow::getLocalIP() {
    QString hostName = QHostInfo::localHostName();
    QHostInfo hostInfo = QHostInfo::fromName(hostName);
    QString localIP = "";
    QList<QHostAddress> addrList = hostInfo.addresses();
    if (!addrList.isEmpty()) {
        for (int i = 0; i < addrList.size(); i++) {
            QHostAddress aHost = addrList.at(i);
            if (aHost.protocol() == QAbstractSocket::IPv4Protocol) {
                localIP = aHost.toString();
                break;
            }
        }
    }
    return localIP;
}

/* 添加QSS样式 */
void MainWindow::loadStyleFile() {
    QFile file(":/new/prefix1/sytle/style.css");
    file.open(QFile::ReadOnly);
    QString styleSheet = tr(file.readAll());
    this->setStyleSheet(styleSheet);
    file.close();
}

4.3 界面显示

在这里插入图片描述
客户端界面使用了QSS

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/341715.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【设计模式】我终于读懂了代理模式。。。

&#x1f466;代理模式的基本介绍 1)代理模式&#xff1a;为一个对象提供一个替身&#xff0c;以控制对这个对象的访问。即通过代理对象访问目标对象,这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。 2)被代理的对象可以是远程对象、创建…

SharpImpersonation:一款基于令牌和Shellcode注入的用户模拟工具

关于SharpImpersonation SharpImpersonation是一款功能强大的用户模拟工具&#xff0c;该工具基于令牌机制和Shellcode注入技术实现其功能&#xff0c;可以帮助广大研究人员更好地对组织内部的网络环境和系统安全进行分析和测试。 该工具基于 Tokenvator的代码库实现其功能&a…

webpack、vite、vue-cli、create-vue 的区别

webpack、vite、vue-cli、create-vue 的区别 首先说结论 Rollup更适合打包库&#xff0c;webpack更适合打包项目应用&#xff0c;vite基于rollup实现了热更新也适合打包项目。 功能工具工具脚手架vue-clicreate-vue构建项目vite打包代码webpackrollup 脚手架:用于初始化&#…

icon-font的使用

先登录阿里巴巴图标矢量库官网iconfont-阿里巴巴矢量图标库在官网挑选需要的图标点击图标购物车并且点击下载代码下载好之后将文件夹里面的css&#xff0c;ttf文件放置到你的项目当中并且点击里面的html文件这里面有详细的用法&#xff0c;这里我是用的时font class方法‘方法二…

【macOS】mac电脑M2芯片安装Homebrew 最简单的方法

一 Homebrew的安装 打开终端&#xff0c;复制如下命令&#xff0c;按回车执行 M芯片和Intel芯片均可 中途可能需要你手动输入密码&#xff0c;输入完成回车即可&#xff08;密码不可见 选择中科大或者清华镜像源 /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/Hom…

MyBatis案例 | 使用映射配置文件实现CRUD操作——修改数据

本专栏主要是记录学习完JavaSE后学习JavaWeb部分的一些知识点总结以及遇到的一些问题等&#xff0c;如果刚开始学习Java的小伙伴可以点击下方连接查看专栏 本专栏地址&#xff1a;&#x1f525;JavaWeb Java入门篇&#xff1a; &#x1f525;Java基础学习篇 Java进阶学习篇&…

简单易懂、无线产品上市认证:进网许可证、入网证、CTA认证

简单易懂、无线产品上市认证:进网许可证、入网证、CTA认证 通信产品想在中国市场进行销售,一般需要通过以下三种认证 : CCC强制认证、TA型号核准(SRRC)、NAL进网许可(CTA) 今天我们来详细了解一下NAL(Network Access License)工信部信息通信管理局的相关要求。 那么…

【软考】系统集成项目管理工程师(二十一)项目收尾管理

1. 项目验收2. 项目总结3. 系统维护4. 项目后评价补充:人员转移和资源遣散广义的系统集成项目收尾管理工作通常包含四类典型的工作:项目验收工作、项目总结工作、系统维护工作 以及 项目后评价工作,此外项目团队成员的后续工作也应在收尾管理时妥善安排;狭义的系统集成项目…

C++程序中执行abort等操作导致没有生成dump文件的问题案例分析

目录 1、概述 2、查看C运行时函数abort的内部实现 3、开源库jsoncpp中调用abort的代码场景说明 4、开源库WebRTC中调用abort的代码场景说明 5、项目问题实例分析 5.1、问题说明 5.2、进一步分析 5.3、动态申请内存失败的可能原因分析 6、最后 VC常用功能开发汇总&…

计算机网络(第三版) 胡亮 课后习题第二章答案

计算机网络&#xff08;第三版&#xff09; 胡亮 课后习题第二章答案 1、数据通信系统由哪些部分组成&#xff1f; 信源、发送设备、传输设备、接受设备&#xff0c;信宿 2、数据通信应该解决的主要问题有哪些&#xff1f; 提高传输系统的利用率接口&#xff0c;编码和同步交换…

C++类基础(十二)

运算符重载&#xff08;终&#xff09; ● 类型转换运算符 – 函数声明为 operator type() const – 与单参数构造函数一样&#xff0c;都引入了一种类型转换方式 struct Str {Str(int p): val(p){}operator int() const //重载类型转换运算符: 没有显示声明返回类型&#xff…

百趣代谢组学分享,补充α-酮酸的低蛋白饮食对肾脏具有保护作用

文章标题&#xff1a;Reno-Protective Effect of Low Protein Diet Supplemented With α-Ketoacid Through Gut Microbiota and Fecal Metabolism in 5/6 Nephrectomized Mice 发表期刊&#xff1a;Frontiers in Nutrition 影响因子&#xff1a;6.59 作者单位&#xff1a;…

opencv调取摄像头录制

大家好&#xff0c;我是csdn的博主&#xff1a;lqj_本人 这是我的个人博客主页&#xff1a; lqj_本人的博客_CSDN博客-微信小程序,前端,python领域博主lqj_本人擅长微信小程序,前端,python,等方面的知识https://blog.csdn.net/lbcyllqj?spm1011.2415.3001.5343哔哩哔哩欢迎关注…

月薪11k!从财务专员到软件测试工程师,成都校区小哥哥用三个月实现转行换岗

好久没和大家分享学员的转行经历了&#xff0c;或许在一些人看来他们的故事与自己无关&#xff0c;但同样也能引起一些人的共鸣&#xff0c;可以帮助到那些陷于就业焦虑的同学找到目标和方向。相仿的年龄、相同的职业、相似的压力…在转行软件测试追求更好生活的路上&#xff0…

Python - 文件基础操作

目录 文件的读取 open()打开函数 read类型 read()方法 readlines()方法 readline()方法 for循环读取文件行 close() 关闭文件对象 with open 语法 文件的写入 文件的追加 文件的读取 操作 功能 文件对象 open(file, mode, encoding) 打开文件获得文件对象 文件…

C语言学习笔记(六): 探索函数与变量

函数的定义 形参和实参 在定义函数时函数名后面括号中的变量名称为“形式参数”&#xff08;简称“形参”&#xff09;或“虚拟参数”。 在主调函数中调用一个函数时&#xff0c;函数名后面括号中的参数称为“实际参数”&#xff08;简称“实参”&#xff09;。 当函数被调用…

独自开:提供创业机会、享受平台分红、推出新颖赚钱副业

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; 前言 独自开&#xff1a;一款聚焦软件定制开发&#xff0c;独立、自主、开放平台 独创分层标准化平台架构,满足系统不断生长的个性化需求多端一键部署前端业务交互与展…

KMP算法详解

注意&#xff1a;PC阅读效果更佳&#xff0c;建议阅读的同时完成代码实践加深理解一、问题描述指定文本串&#xff1a;aabaabaaf和模式串&#xff1a;aabaaf使用KMP算法判断模式串是否在文本串中出现过&#xff1f;假定模式串的长度小于文本串二、思路解析BF算法的问题是&#…

【pytorch安装】conda安装pytorch无法安装cpu版本(完整解决过程)

问题描述 在安装pytorch过程中&#xff0c;发现最后验证torch时总是返回结果为False&#xff0c;结果翻上去发现自己安装的是cpu版本的。 然后又通过conda去更换不同版本尝试&#xff0c;发现都是cpu版本的。 问题分析 通过conda安装pytorch是从源中搜索匹配指令中的文件&am…

@Validated注解不生效问题汇总

Validated注解不生效问题汇总 文章目录Validated注解不生效问题汇总背景&#xff1a;一&#xff1a;可能原因原因1&#xff1a;原因2&#xff1a;原因3&#xff1a;原因4&#xff1a;二&#xff1a;补充全局异常对validation的处理背景&#xff1a; 项目框架应用的是validatio…