Qt Socket 客户端

ExTcpClient.pro

QT       += core gui network

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = ExTcpClient
TEMPLATE = app

# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

CONFIG += c++11

SOURCES += \
        main.cpp \
        ExTcpClient.cpp

HEADERS += \
        ExTcpClient.h

FORMS += \
        ExTcpClient.ui

macx {
ICON = images/icon.icns
}

unix:!macx{
# linux only
}

win32 {
RC_ICONS = images/icon.ico
}

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

DISTFILES +=

RESOURCES += \
    resource.qrc

ExTcpClient.h

#ifndef EXTCPCLIENT_H
#define EXTCPCLIENT_H

#include <QMainWindow>

#include <QLabel>
// QTcpSocket类提供一个TCP套接字
#include <QTcpSocket>
// QHostInfo类为主机名查找提供了静态函数
#include <QHostInfo>

namespace Ui {
class ExTcpClient;
}

class ExTcpClient : public QMainWindow
{
    Q_OBJECT

public:
    // 构造函数
    explicit ExTcpClient(QWidget *parent = nullptr);
    // 析构函数
    ~ExTcpClient();

private:
    // 获取本本机 IP
    QString getLocalIp();

protected:
    // 关闭窗口时候停止监听(重写父类函数)
    void closeEvent(QCloseEvent *event);

// UI 定义的槽函数
private slots:
    // 请求连接到服务器
    void on_actConnect_triggered();
    // 断开与服务器的连接
    void on_actDisconnect_triggered();
    // 清除内容
    void on_actClear_triggered();
    // 退出程序
    void on_actQuit_triggered();
    // 发送文本消息
    void on_btnSend_clicked();

    // 自定义的槽函数
    // 链接成功
    void onConnected();
    // 关闭链接
    void onDisconnected();
    // 从socket读取传入的数据
    void onSocketReadyRead();
    // 链接状态改变时
    void onSocketStateChange(QAbstractSocket::SocketState socketState);

private:
    // 窗口指针句柄
    Ui::ExTcpClient *ui;
    // 标签指针句柄
    QLabel* m_labSocket;
    // TCP套接字指针句柄
    QTcpSocket* m_tcpSocket;
};

#endif // EXTCPCLIENT_H

ExTcpClient.cpp

#include "ExTcpClient.h"
#include "ui_ExTcpClient.h"

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

    // 为标签指针句柄赋值,是一个指针
    m_labSocket = new QLabel("socket状态:");

    // 此属性保存中线的宽度,缺省值为0
    m_labSocket->setMidLineWidth(150);

    // 向窗口状态栏中添加标签
    ui->statusBar->addWidget(m_labSocket);

    // 创建一个 Socket 对象
    m_tcpSocket = new QTcpSocket(this);

    // 获取本地IP地址
    QString localIp = getLocalIp();

    // 设置窗口标题
    this->setWindowTitle(windowTitle() + "----本机IP:" + localIp);

    // 将本地IP地址添加到下拉选择框中
    ui->comboBox->addItem(localIp);

    // 这个信号在connectToHost()被调用并且连接已经成功建立之后发出
    connect(m_tcpSocket, SIGNAL(connected()), this, SLOT(onConnected()));

    // 该信号在套接字断开连接时发出
    connect(m_tcpSocket, SIGNAL(disconnected()), this, SLOT(onDisconnected()));

    // 每当QAbstractSocket的状态发生变化时,就会发出这个信号。socketState参数是新的状态
    connect(m_tcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChange(QAbstractSocket::SocketState)));

    // 每当从设备当前读取通道中读取新数据时,该信号就会发出一次。它只会在有新数据可用时再次发出,例如当新的网络数据负载到达您的网络套接字时,或者当一个新的数据块被附加到您的设备时
    connect(m_tcpSocket, SIGNAL(readyRead()), this, SLOT(onSocketReadyRead()));
}

// 析构函数
ExTcpClient::~ExTcpClient()
{
    delete ui;
}

// 获取本地IP地址
QString ExTcpClient::getLocalIp()
{
    // QHostInfo类为主机名查找提供了静态函数
    // 返回此机器的主机名(如果配置了)。请注意,主机名不能保证是全局唯一的,特别是如果它们是自动配置的。此函数不保证返回的主机名是完全限定域名(FQDN)。为此,使用fromName()将返回的名称解析为FQDN
    QString hostName = QHostInfo::localHostName();
    // 查找给定主机名的IP地址。函数在查找过程中阻塞,这意味着程序的执行被暂停,直到查找结果准备好。返回查找QHostInfo对象的结果。
    // 如果你传递一个文字IP地址给name而不是主机名,QHostInfo将搜索IP的域名(即,QHostInfo将执行反向查找)。如果成功,返回的QHostInfo将包含已解析的域名和主机名的IP地址。
    QHostInfo hostInfo = QHostInfo::fromName(hostName);
    // 将带有文本的新段落追加到文本编辑的末尾
    ui->plainTextEdit->appendPlainText("本机名称:" + hostName);
    // 声明一个字符串变量
    QString localIp;

    // 循环此IP地址列表
    foreach (QHostAddress addr, hostInfo.addresses()) {
        // 筛选出 IPv4 地址出来
        if (QAbstractSocket::IPv4Protocol == addr.protocol()) {

            localIp = addr.toString();

            break;
        }
    }
    // 返回本地IP地址
    return localIp;
}

// 重写关闭窗口时候停止监听
void ExTcpClient::closeEvent(QCloseEvent *event)
{
    // 如果 QTcpSocket 是链接状态
    if (m_tcpSocket->state() == QAbstractSocket::ConnectedState)
        // 试图关闭套接字。如果有挂起的数据等待写入,QAbstractSocket将进入ClosingState并等待,直到所有数据都被写入。最终,它将进入UnconnectedState并发出disconnected()信号
        m_tcpSocket->disconnectFromHost();

    // 设置事件对象的accept标志,相当于调用setAccepted(true)。
    // 设置accept参数表示事件接收者需要该事件。不需要的事件可能会传播到父小部件。
    event->accept();
}

// 链接成功
void ExTcpClient::onConnected()
{
    // 将带有文本的新段落追加到文本编辑的末尾
    ui->plainTextEdit->appendPlainText("已经连接到服务器\n客户端套接字连接\n对等(peer)地址:" + m_tcpSocket->peerAddress().toString()
                                       + "    对等(peer)端口:" +  QString::number(m_tcpSocket->peerPort()));
    // 禁用链接按钮
    ui->actConnect->setEnabled(false);
    // 开启关闭按钮
    ui->actDisconnect->setEnabled(true);
}

// 关闭链接
void ExTcpClient::onDisconnected()
{
    // 将带有文本的新段落追加到文本编辑的末尾
    ui->plainTextEdit->appendPlainText("已经断开与服务器的连接\n");
    // 启用链接按钮
    ui->actConnect->setEnabled(true);
    // 禁用关闭按钮
    ui->actDisconnect->setEnabled(false);
}

// 准备读取 Socket 内容
void ExTcpClient::onSocketReadyRead()
{
    // 判断缓存中是否还有消息内容
    while (m_tcpSocket->canReadLine()) {
        // 读取,并追加到文本编辑的末尾
        ui->plainTextEdit->appendPlainText("[服务器:]" + m_tcpSocket->readLine());
    }
}

// Socket 状态改变
void ExTcpClient::onSocketStateChange(QAbstractSocket::SocketState socketState)
{
    switch (socketState) {
    // 套接字未连接
    case QAbstractSocket::UnconnectedState:
        m_labSocket->setText("socket状态:UnconnectedState");
        break;
    // 套接字正在执行主机名查找
    case QAbstractSocket::HostLookupState:
        m_labSocket->setText("socket状态:HostLookupState");
        break;
    // 套接字已开始建立连接
    case QAbstractSocket::ConnectingState:
        m_labSocket->setText("socket状态:ConnectingState");
        break;
    // 建立连接
    case QAbstractSocket::ConnectedState:
        m_labSocket->setText("socket状态:ConnectedState");
        break;
    // 套接字绑定到一个地址和端口
    case QAbstractSocket::BoundState:
        m_labSocket->setText("socket状态:BoundState");
        break;
    // 套接字即将关闭(数据可能仍在等待写入)
    case QAbstractSocket::ClosingState:
        m_labSocket->setText("socket状态:ClosingState");
        break;
    // 监听状态
    case QAbstractSocket::ListeningState:
        m_labSocket->setText("socket状态:ListeningState");
        break;
    default:
        m_labSocket->setText("socket状态:其他未知状态...");
        break;
    }
}

// 点击链接到服务器时触发的函数
void ExTcpClient::on_actConnect_triggered()
{
    // 获取IP地址
    QString addr = ui->comboBox->currentText();
    // 获取端口号
    quint16 port = ui->spinBox->value();
    // 发起链接请求
    m_tcpSocket->connectToHost(addr, port);
}

// 点击关闭链接时触发的函数
void ExTcpClient::on_actDisconnect_triggered()
{
    // 判断当前是否还在链接状态
    if(m_tcpSocket->state() == QAbstractSocket::ConnectedState)
        // 断开链接
        m_tcpSocket->disconnectFromHost();
}

// 点击清空内容时触发的函数
void ExTcpClient::on_actClear_triggered()
{
    // 清空文本编辑器中的内容
    ui->plainTextEdit->clear();
}

// 点击退出程序时触发的函数
void ExTcpClient::on_actQuit_triggered()
{
    // 关闭程序
    close();
}

// 点击发送消息时触发的函数
void ExTcpClient::on_btnSend_clicked()
{
    // 获取需要发送的消息内容
    QString msg = ui->lineEdit->text();
    // 将发送的消息内容追加到文本编辑器中显示
    ui->plainTextEdit->appendPlainText("[客户端:]" + msg);
    // 清空文本编辑器中的内容
    ui->lineEdit->clear();
    // 消息输入框获取焦点
    ui->lineEdit->setFocus();
    // 将消息内容设置成 UTF8 编码格式
    QByteArray str = msg.toUtf8();
    // 向消息尾部追加换行符
    str.append('\n');
    // 将消息写入到 Socket 缓存区,带 Socket 发送消息
    m_tcpSocket->write(str);
}

main.cpp

#include "ExTcpClient.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    ExTcpClient w;
    w.show();

    return a.exec();
}

ExTcpClient.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>ExTcpClient</class>
 <widget class="QMainWindow" name="ExTcpClient">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>561</width>
    <height>353</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>ExTcpClient</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout">
      <item>
       <spacer name="horizontalSpacer">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>40</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
       <widget class="QLabel" name="labIP">
        <property name="text">
         <string>服务器地址:</string>
        </property>
        <property name="alignment">
         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QComboBox" name="comboBox">
        <property name="editable">
         <bool>true</bool>
        </property>
        <property name="currentText">
         <string>127.0.0.1</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QLabel" name="labPort">
        <property name="text">
         <string>端口:</string>
        </property>
        <property name="alignment">
         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QSpinBox" name="spinBox">
        <property name="maximum">
         <number>65000</number>
        </property>
        <property name="value">
         <number>10000</number>
        </property>
       </widget>
      </item>
     </layout>
    </item>
    <item>
     <widget class="QPlainTextEdit" name="plainTextEdit"/>
    </item>
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout_2">
      <item>
       <widget class="QLineEdit" name="lineEdit"/>
      </item>
      <item>
       <widget class="QPushButton" name="btnSend">
        <property name="text">
         <string>发送</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>561</width>
     <height>25</height>
    </rect>
   </property>
  </widget>
  <widget class="QToolBar" name="mainToolBar">
   <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
   <addaction name="actConnect"/>
   <addaction name="actDisconnect"/>
   <addaction name="actClear"/>
   <addaction name="actQuit"/>
  </widget>
  <widget class="QStatusBar" name="statusBar"/>
  <action name="actConnect">
   <property name="icon">
    <iconset resource="resource.qrc">
     <normaloff>:/images/Image4.png</normaloff>:/images/Image4.png</iconset>
   </property>
   <property name="text">
    <string>请求连接</string>
   </property>
   <property name="toolTip">
    <string>请求连接到服务器</string>
   </property>
  </action>
  <action name="actDisconnect">
   <property name="icon">
    <iconset resource="resource.qrc">
     <normaloff>:/images/Image5.png</normaloff>:/images/Image5.png</iconset>
   </property>
   <property name="text">
    <string>断开连接</string>
   </property>
   <property name="toolTip">
    <string>断开与服务器的连接</string>
   </property>
  </action>
  <action name="actClear">
   <property name="icon">
    <iconset resource="resource.qrc">
     <normaloff>:/images/Image1.png</normaloff>:/images/Image1.png</iconset>
   </property>
   <property name="text">
    <string>清空</string>
   </property>
   <property name="toolTip">
    <string>清空编辑框文本</string>
   </property>
  </action>
  <action name="actQuit">
   <property name="icon">
    <iconset resource="resource.qrc">
     <normaloff>:/images/Image2.jpg</normaloff>:/images/Image2.jpg</iconset>
   </property>
   <property name="text">
    <string>退出</string>
   </property>
   <property name="toolTip">
    <string>退出程序</string>
   </property>
  </action>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <resources>
  <include location="resource.qrc"/>
 </resources>
 <connections/>
</ui>

源代码

https://www.guangdujs.com/read/qt230729/date-2023.07.31.11.46.35

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小π

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

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

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

打赏作者

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

抵扣说明:

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

余额充值