欢迎光临
我们一直在努力

【图文详解】TCP 通信实战:Qt Creator 下的客户端与服务器实现(附 C++ 完整代码)

在这里插入图片描述

💪图像算法工程师 | 深耕并挚爱的图像处理💪 💪🧑‍💻🔍✨📈 持续为你点亮技术灯塔,专栏最新动态如下👇: 📝《图像去噪》 📝《超分辨率重建》 📝《语义分割》 📝《风格迁移》 📝《目标检测》 📝《图像增强》 📝《模型优化》 📝《模型实战部署》 📝《图像配准融合》 📝《数据集》 📝《高效助手》 📝《C++》 📝《Qt》


在这里插入图片描述

目录

  • 一、源码包准备
  • 二、客户端
    • 2.1 界面布局设计
    • 2.2 槽函数实现
      • 2.2.1 关闭按钮
      • 2.2.2 连接按钮
      • 2.2.3 断开按钮
      • 2.2.4 清除接收按钮
      • 2.2.5 发送按钮
    • 2.3 客户端代码
      • 2.3.1 头文件
      • 2.3.2 源文件
      • 2.3.3 可执行文件
  • 三、服务器端
    • 3.1 界面布局设计
    • 3.2 槽函数实现
      • 3.2.1 获取客户端连接
      • 3.2.2 断开客户端连接
      • 3.2.3 清除接收内容按钮
      • 3.2.4 发送信息
    • 3.3 服务器端代码
      • 3.3.1 头文件
      • 3.3.2 源文件
      • 3.3.3 可执行文件
  • 四、效果展示
  • 五、总结

一、源码包准备

本教材基于完整源码工程(含头文件、源文件),分两个工程项目开发客户端和服务器端,下载后即可直接编译运行。配套教程的源码包获取方式已在文章结尾提供,扫码到「视觉研坊」中回复关键字:QtTCP通信,自动获取下载链接。下载解压后的工程文件见下:

在这里插入图片描述

二、客户端

2.1 界面布局设计

界面布局主要用到控件有:Label、TextBrowser、PushButton、LineEdit和TextEdit,如下:

在这里插入图片描述

2.2 槽函数实现

2.2.1 关闭按钮

关闭按钮主要用于直接退出,其槽函数实现见下:

void Widget::on_cancelButton_clicked()
{
this->close();
}

2.2.2 连接按钮

当输入好服务器地址和端口号用,点击连接按钮,可以连接到服务器,槽函数实现见下:

void Widget::on_connectButton_clicked()
{
// 获取ip地址和端口号
QString IP = ui->ipLineEdit->text();
QString port = ui->portLineEdit->text();

if(IP.isEmpty() || port.isEmpty()) {
QMessageBox::warning(this, "警告", "请输入IP地址和端口号");
return;
}

qDebug() << "尝试连接服务器: " << IP << ":" << port;

// 连接服务器
socket->connectToHost(QHostAddress(IP),port.toShort());

// 禁用连接按钮,启用断开按钮
ui->connectButton->setEnabled(false);
ui->disconnectButton->setEnabled(true);
}

2.2.3 断开按钮

当客户端和服务器正常连接过程,想要断开连接,直接点击断开按钮,对应槽函数实现见下:

// 断开连接按钮
void Widget::on_disconnectButton_clicked()
{
if(socket->state() == QTcpSocket::ConnectedState) {
socket->disconnectFromHost();
if(socket->state() != QTcpSocket::UnconnectedState) {
socket->waitForDisconnected(1000); // 等待1秒断开
}
}

// 启用连接按钮,禁用断开按钮
ui->connectButton->setEnabled(true);
ui->disconnectButton->setEnabled(false);

ui->textBrowser->append("已断开与服务器的连接");
}

2.2.4 清除接收按钮

清除接收按钮主要是用于将接收到的内容全部删除,实现方法见下:

void Widget::on_clearButton_clicked()
{
ui->textBrowser->clear();
}

2.2.5 发送按钮

从服务器端发送数据到客户端,实现方法见下:

// 发送按钮
void Widget::on_sendButton_clicked()
{
if(socket->state() == QTcpSocket::ConnectedState) {
QString message = ui->sendTextEdit->toPlainText();
if(!message.isEmpty()) {
// 发送消息到服务器
socket->write(message.toUtf8());
socket->flush();

// 在客户端显示发送的消息
ui->textBrowser->append("客户端发送: " + message);
ui->sendTextEdit->clear();
}
} else {
QMessageBox::warning(this, "警告", "未连接到服务器");
}
}

2.3 客户端代码

2.3.1 头文件

客户端头文件代码见下:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpSocket>
#include <QHostAddress> // 主机地址
#include <QString>
#include <QMessageBox>
#include <QDebug>

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
Q_OBJECT

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

private slots:
void on_cancelButton_clicked(); // 关闭按钮槽函数
void on_connectButton_clicked(); // 连接按钮槽函数

void on_sendButton_clicked(); // 发送按钮槽函数
void on_disconnectButton_clicked(); // 断开连接按钮槽函数
void on_clearButton_clicked(); // 清除接收按钮槽函数

// 新增槽函数
void connected_success();
void disconnected_from_server();
void socket_ready_read();
//void socket_error(QAbstractSocket::SocketError error);

private:
Ui::Widget *ui;

QTcpSocket *socket; // 创建QTcoSocket对象
};
#endif // WIDGET_H

2.3.2 源文件

客户端源文件代码见下:

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

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

// // 初始化socket
socket = new QTcpSocket(this); // 创建socket对象

// 连接信号和槽
connect(socket, &QTcpSocket::connected, this, &Widget::connected_success);
connect(socket, &QTcpSocket::disconnected, this, &Widget::disconnected_from_server);
connect(socket, &QTcpSocket::readyRead, this, &Widget::socket_ready_read);

// 初始状态设置
ui->disconnectButton->setEnabled(false);
}

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

// 关闭按钮
void Widget::on_cancelButton_clicked()
{
this->close();
}

// 连接按钮
void Widget::on_connectButton_clicked()
{
// 获取ip地址和端口号
QString IP = ui->ipLineEdit->text();
QString port = ui->portLineEdit->text();

if(IP.isEmpty() || port.isEmpty()) {
QMessageBox::warning(this, "警告", "请输入IP地址和端口号");
return;
}

qDebug() << "尝试连接服务器: " << IP << ":" << port;

// 连接服务器
socket->connectToHost(QHostAddress(IP),port.toShort());

// 禁用连接按钮,启用断开按钮
ui->connectButton->setEnabled(false);
ui->disconnectButton->setEnabled(true);

}

// 断开连接按钮
void Widget::on_disconnectButton_clicked()
{
if(socket->state() == QTcpSocket::ConnectedState) {
socket->disconnectFromHost();
if(socket->state() != QTcpSocket::UnconnectedState) {
socket->waitForDisconnected(1000); // 等待1秒断开
}
}

// 启用连接按钮,禁用断开按钮
ui->connectButton->setEnabled(true);
ui->disconnectButton->setEnabled(false);

ui->textBrowser->append("已断开与服务器的连接");
}

// 清除接收按钮
void Widget::on_clearButton_clicked()
{
ui->textBrowser->clear();
}

// 发送按钮
void Widget::on_sendButton_clicked()
{
if(socket->state() == QTcpSocket::ConnectedState) {
QString message = ui->sendTextEdit->toPlainText();
if(!message.isEmpty()) {
// 发送消息到服务器
socket->write(message.toUtf8());
socket->flush();

// 在客户端显示发送的消息
ui->textBrowser->append("客户端发送: " + message);
ui->sendTextEdit->clear();
}
} else {
QMessageBox::warning(this, "警告", "未连接到服务器");
}
}

// 连接成功槽函数
void Widget::connected_success()
{
QMessageBox::information(this, "连接提示", "连接服务器成功");
ui->textBrowser->append("已连接到服务器");
}

// 断开连接槽函数
void Widget::disconnected_from_server()
{
QMessageBox::warning(this, "连接提示", "连接异常 服务器断开");
ui->textBrowser->append("与服务器断开连接");

// 启用连接按钮,禁用断开按钮
ui->connectButton->setEnabled(true);
ui->disconnectButton->setEnabled(false);
}

// 接收数据槽函数
void Widget::socket_ready_read()
{
QByteArray data = socket->readAll();
QString message = QString::fromUtf8(data);
ui->textBrowser->append("服务器回复: " + message);
}

2.3.3 可执行文件

运行上面工程代码,对应生成的可执行文件位于build\\Desktop_Qt_6_8_3_MinGW_64_bit-Debug\\debug路径下,双击可直接运行,见下:

在这里插入图片描述

三、服务器端

由于没有服务器,为了验证上面客户端界面是否能正常实现TCP通信功能,还需要开发一套对应的服务器端,用于和客户端连接后相互通信。

3.1 界面布局设计

界面布局主要用到控件有:Label、TextBrowser、PushButton、LineEdit和TextEdit,如下:

在这里插入图片描述

3.2 槽函数实现

3.2.1 获取客户端连接

获取客户端连接槽函数实现如下:

void Widget::newClientHandler()
{
// 建立TCP连接
QTcpSocket *socket = server->nextPendingConnection(); // 客户端的IP地址和端口号都保存在socket里面
// socket->peerAddress(); // 获取客户端地址
// socket->peerPort(); // 获取客户端端口号

if(!socket) return;

// 保存客户端socket
clientSockets.append(socket);

// 显示客户端信息
QString clientInfo = "客户端连接: " + socket->peerAddress().toString() + ":" +
QString::number(socket->peerPort());
ui->textBrowser->append(clientInfo);
ui->ipLineEdit->setText(socket->peerAddress().toString());
ui->portLineEdit->setText(QString::number(socket->peerPort()));

// 连接客户端断开信号
connect(socket, &QTcpSocket::disconnected, this, &Widget::clientDisconnected);

// 连接客户端数据可读信号
connect(socket, &QTcpSocket::readyRead, this, &Widget::clientReadyRead);
}

3.2.2 断开客户端连接

断开客户端连接即对应断开按钮,槽函数实现如下:

void Widget::on_disconnectButton_clicked()
{
if(clientSockets.isEmpty()) {
QMessageBox::information(this, "提示", "没有客户端连接");
return;
}

// 断开所有客户端连接
for(QTcpSocket *socket : clientSockets) {
if(socket && socket->state() == QAbstractSocket::ConnectedState) {
socket->disconnectFromHost();
if(socket->state() != QAbstractSocket::UnconnectedState) {
socket->waitForDisconnected(1000);
}
}
}

clientSockets.clear();
ui->textBrowser->append("已断开所有客户端连接");
}

3.2.3 清除接收内容按钮

清除接收内容按钮槽函数实现如下:

void Widget::on_clearButton_clicked()
{
ui->textBrowser->clear();
}

3.2.4 发送信息

发送信息即对应发送按钮,从服务器端向用户端发送信息,槽函数实现如下:

void Widget::on_sendButton_clicked()
{
if(clientSockets.isEmpty()) {
QMessageBox::warning(this, "警告", "没有客户端连接");
return;
}

QString message = ui->sendTextEdit->toPlainText();
if(message.isEmpty()) {
QMessageBox::warning(this, "警告", "消息不能为空");
return;
}

// 向所有客户端发送消息
for(QTcpSocket *socket : clientSockets) {
if(socket && socket->state() == QAbstractSocket::ConnectedState) {
socket->write(message.toUtf8());
socket->flush();
}
}

// 在服务器端显示发送的消息
ui->textBrowser->append("服务器发送: " + message);
ui->sendTextEdit->clear();
}

3.3 服务器端代码

3.3.1 头文件

服务器端头文件代码见下:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QMessageBox>

#define PORT 8000 // 定义端口号

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
Q_OBJECT

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

private slots:
void newClientHandler();
void clientDisconnected();
void clientReadyRead();
void on_sendButton_clicked(); // 发送按钮槽函数
void on_disconnectButton_clicked(); // 断开连接按钮槽函数
void on_clearButton_clicked(); // 清除接收按钮槽函数

private:
Ui::Widget *ui;

// 创建server对象指针
QTcpServer *server;
QList<QTcpSocket*> clientSockets; // 存储所有客户端连接
};
#endif // WIDGET_H

3.3.2 源文件

服务器端源文件代码见下:

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

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

// 创建一个服务器对象
server = new QTcpServer(this);

// 监听
//server->listen(QHostAddress::AnyIPv4,PORT); // 随便监听哪个地址都行
if(server->listen(QHostAddress::AnyIPv4, PORT)) {
ui->textBrowser->append("服务器已启动,正在监听端口 " + QString::number(PORT));
} else {
ui->textBrowser->append("服务器启动失败: " + server->errorString());
}

// 客户端发起连接,server发出信号
connect(server,&QTcpServer::newConnection,this,&Widget::newClientHandler);
}

Widget::~Widget()
{

// 关闭所有客户端连接
for(QTcpSocket *socket : clientSockets) {
if(socket && socket->state() == QAbstractSocket::ConnectedState) {
socket->disconnectFromHost();
if(socket->state() != QAbstractSocket::UnconnectedState) {
socket->waitForDisconnected(1000);
}
}
}

// 停止服务器
server->close();

delete ui;
}

void Widget::newClientHandler()
{
// 建立TCP连接
QTcpSocket *socket = server->nextPendingConnection(); // 客户端的IP地址和端口号都保存在socket里面
// socket->peerAddress(); // 获取客户端地址
// socket->peerPort(); // 获取客户端端口号

if(!socket) return;

// 保存客户端socket
clientSockets.append(socket);

// 显示客户端信息
QString clientInfo = "客户端连接: " + socket->peerAddress().toString() + ":" +
QString::number(socket->peerPort());
ui->textBrowser->append(clientInfo);
ui->ipLineEdit->setText(socket->peerAddress().toString());
ui->portLineEdit->setText(QString::number(socket->peerPort()));

// 连接客户端断开信号
connect(socket, &QTcpSocket::disconnected, this, &Widget::clientDisconnected);

// 连接客户端数据可读信号
connect(socket, &QTcpSocket::readyRead, this, &Widget::clientReadyRead);
}

// 连接客户端断开信号提示
void Widget::clientDisconnected()
{
QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
if(socket) {
ui->textBrowser->append("客户端断开: " + socket->peerAddress().toString() +
":" + QString::number(socket->peerPort()));
clientSockets.removeOne(socket);
socket->deleteLater();
}
}

// 连接客户端数据可读信号
void Widget::clientReadyRead()
{
QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
if(socket) {
QByteArray data = socket->readAll();
QString message = QString::fromUtf8(data);

// 显示接收到的消息
QString clientInfo = socket->peerAddress().toString() + ":" +
QString::number(socket->peerPort());
ui->textBrowser->append("收到来自 " + clientInfo + " 的消息: " + message);
}
}

// 断开所有连接按钮
void Widget::on_disconnectButton_clicked()
{
if(clientSockets.isEmpty()) {
QMessageBox::information(this, "提示", "没有客户端连接");
return;
}

// 断开所有客户端连接
for(QTcpSocket *socket : clientSockets) {
if(socket && socket->state() == QAbstractSocket::ConnectedState) {
socket->disconnectFromHost();
if(socket->state() != QAbstractSocket::UnconnectedState) {
socket->waitForDisconnected(1000);
}
}
}

clientSockets.clear();
ui->textBrowser->append("已断开所有客户端连接");
}

// 清除接收按钮
void Widget::on_clearButton_clicked()
{
ui->textBrowser->clear();
}

// 发送信息
void Widget::on_sendButton_clicked()
{
if(clientSockets.isEmpty()) {
QMessageBox::warning(this, "警告", "没有客户端连接");
return;
}

QString message = ui->sendTextEdit->toPlainText();
if(message.isEmpty()) {
QMessageBox::warning(this, "警告", "消息不能为空");
return;
}

// 向所有客户端发送消息
for(QTcpSocket *socket : clientSockets) {
if(socket && socket->state() == QAbstractSocket::ConnectedState) {
socket->write(message.toUtf8());
socket->flush();
}
}

// 在服务器端显示发送的消息
ui->textBrowser->append("服务器发送: " + message);
ui->sendTextEdit->clear();
}

3.3.3 可执行文件

运行上面服务器端工程代码,在build\\Desktop_Qt_6_8_3_MinGW_64_bit-Debug\\debug路径下生成可执行文件,双击即可运行,如下:

在这里插入图片描述

四、效果展示

最终的通信动态效果见文章开头:

在这里插入图片描述

五、总结

基于Qt的TCP通信实现总结 。本文介绍了使用Qt框架实现TCP客户端/服务器通信的完整过程。主要内容包括: 源码工程:提供完整的客户端和服务器端项目源码,包含头文件和源文件,可直接编译运行。 客户端实现: 界面设计:使用Label、TextBrowser、PushButton等控件 ; 功能实现:连接/断开服务器、发送/接收消息、清空消息等按钮功能; 核心类:QTcpSocket类处理网络通信 ; 关键功能: 连接服务器:通过IP和端口号建立连接; 消息收发:支持文本消息的发送和接收显示 ; 状态管理:处理连接和断开状态变化; 以上就是使用TCP实现在客户端和服务器之间的通信的详细过程,希望能帮你快速上手!

🌟 感谢一路看到这里!

若本文对您有所启发,请毫不吝啬地留下 👍 点赞、⭐ 收藏、✍️ 评论三连——你的每一次互动,都是我持续输出干货的最大动力!

更多精彩内容已就绪:扫码关注下面「视觉研坊」 📸,让实战教程、独家案例与前沿资讯第一时间与你相遇!

赞(0)
未经允许不得转载:171主机测评 » 【图文详解】TCP 通信实战:Qt Creator 下的客户端与服务器实现(附 C++ 完整代码)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址