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