欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 建筑 > Qt开发:QTcpSocket的详解

Qt开发:QTcpSocket的详解

2025/5/20 3:55:04 来源:https://blog.csdn.net/TechNomad/article/details/147933508  浏览:    关键词:Qt开发:QTcpSocket的详解

文章目录

    • 一、QTcpSocket 简介
    • 二、常用方法的介绍和使用
    • 三、常用的信号函数

一、QTcpSocket 简介

  QTcpSocket 是 Qt 网络模块中用于实现基于 TCP 协议的客户端通信的类。它提供了一个面向流的接口,允许程序通过套接字连接到远程主机,发送和接收数据。

  • 所属模块:QtNetwork
  • 用于建立 TCP 客户端连接,读取/写入数据。
  • 特点:异步非阻塞(配合信号槽机制)、支持事件驱动编程模型。

二、常用方法的介绍和使用

2.1 void QTcpSocket::connectToHost(hostname, port)
用于发起 TCP 连接的核心函数,常用于客户端向服务器发起连接请求。

参数说明:
在这里插入图片描述
使用示例:

QTcpSocket* socket = new QTcpSocket(this);// 连接信号槽
connect(socket, &QTcpSocket::connected, this, [](){qDebug() << "Connected to server.";
});
connect(socket, &QTcpSocket::errorOccurred, this, [](QAbstractSocket::SocketError err){qDebug() << "Connection error:" << err;
});// 发起连接
socket->connectToHost("127.0.0.1", 12345);

阻塞等待连接:
如果在非 GUI 程序中使用,或者想同步等待连接结果,可以使用:

socket->connectToHost("127.0.0.1", 12345);
if (socket->waitForConnected(3000)) {qDebug() << "Connected!";
} else {qDebug() << "Connection failed:" << socket->errorString();
}

常见错误排查:
在这里插入图片描述
2.2 void QAbstractSocket::disconnectFromHost()
用于主动断开 TCP 连接的函数。它的作用是通知对方:客户端希望关闭连接,然后进行连接释放。

使用说明:
当调用 disconnectFromHost() 后:

  1. 客户端发送 TCP 的 FIN 包;
  2. 如果连接关闭成功,触发 disconnected() 信号;
  3. 如果对方已经关闭连接,你仍然可以调用它释放资源;
  4. 若要强制关闭连接,可以使用 abort()。

基本示例:

QTcpSocket* socket = new QTcpSocket(this);// 建立连接
socket->connectToHost("127.0.0.1", 12345);
connect(socket, &QTcpSocket::connected, this, [socket]() {qDebug() << "Connected to server.";// 写入数据后断开socket->write("Bye Server!");socket->flush();  // 确保发送出去了socket->disconnectFromHost();
});connect(socket, &QTcpSocket::disconnected, this, []() {qDebug() << "Disconnected from server.";
});

同步等待断开:

socket->disconnectFromHost();
if (socket->state() != QAbstractSocket::UnconnectedState) {socket->waitForDisconnected(3000);  // 最多等 3 秒
}

与 abort() 的区别:
在这里插入图片描述
建议:正常退出时用 disconnectFromHost();发生错误/异常时使用 abort() 立即关闭连接。

*2.3 qint64 QIODevice::write(const char data, qint64 maxSize)
它在客户端和服务端之间发送字节流,是实现 socket 通信的核心方法之一。

参数说明:

  • data:指向要发送的原始数据。
  • maxSize:要发送的数据长度(字节数)。

返回值:返回实际写入的字节数,如果返回 -1,表示写入失败。

注意事项:

  • write() 只将数据写入到内部缓冲区,并不保证立刻发送。
  • 若想确保数据被真正写出,可使用 flush() 或等待 bytesWritten(qint64) 信号。
  • 如果 socket 未连接,调用 write() 会失败。

示例代码:
示例 1:发送一个 C 风格字符串

QTcpSocket *socket = new QTcpSocket(this);
socket->connectToHost("127.0.0.1", 8888);if (socket->waitForConnected(3000)) {const char *msg = "Hello from client!";qint64 bytesWritten = socket->write(msg, strlen(msg));qDebug() << "Bytes written:" << bytesWritten;
}

示例 2:发送结构体(二进制数据)

struct MyData {int id;float value;char name[16];
};MyData data = {42, 3.14f, "example"};
qint64 size = sizeof(data);QTcpSocket *socket = new QTcpSocket(this);
socket->connectToHost("127.0.0.1", 8888);if (socket->waitForConnected(3000)) {qint64 bytesWritten = socket->write(reinterpret_cast<const char*>(&data), size);qDebug() << "Sent struct of size:" << bytesWritten;
}

示例3:写入大量数据
如果是大量数据,可能无法一次性全部写入完。这时候返回值可能小于你提供的 maxSize,即只写入了一部分数据。这是因为:

  • QTcpSocket 的底层发送缓冲区是有限的;
  • 写入时如果缓冲区满了,write() 只写部分内容或返回 -1;
  • 它是非阻塞的,不会等待所有数据写入成功才返回。

正确处理方式:分段循环写入 + 监听 bytesWritten()

class MySender : public QObject {Q_OBJECTpublic:MySender(QTcpSocket *sock, QObject *parent = nullptr): QObject(parent), socket(sock), totalSize(0), bytesSent(0), buffer(nullptr) {connect(socket, &QTcpSocket::bytesWritten, this, &MySender::onBytesWritten);
}void sendLargeData(const char *data, qint64 size) {buffer = data;totalSize = size;bytesSent = 0;writeMore();}private slots:void onBytesWritten(qint64 bytes) {Q_UNUSED(bytes);}private:void writeMore() {const int CHUNK_SIZE = 64 * 1024; // 每次最多写 64KBwhile (bytesSent < totalSize) {qint64 remaining = totalSize - bytesSent;qint64 toWrite = qMin(remaining, static_cast<qint64>(CHUNK_SIZE));qint64 written = socket->write(buffer + bytesSent, toWrite);if (written == -1) {qDebug() << "Write failed!";return;}bytesSent += written;if (written < toWrite) {// 写缓冲区满了,等下次 bytesWritten 再继续break;}}if (bytesSent == totalSize) {qDebug() << "All data sent.";disconnect(socket, &QTcpSocket::bytesWritten, this, &MySender::onBytesWritten);}}QTcpSocket *socket;const char *buffer;qint64 totalSize;qint64 bytesSent;
};

使用方式:

MySender *sender = new MySender(socket, this);
sender->sendLargeData(bigBuffer, bigSize);

2.4 qint64 QIODevice::write(const QByteArray &byteArray)
参数说明:byteArray是要写入的数据,封装在一个 QByteArray 对象中。

返回值:

  • 返回实际写入的字节数。
  • 返回 -1 表示写入失败(如设备未打开或不可写)。

使用示例:
示例 1:使用 QByteArray 向服务器发送文本数据

QTcpSocket *socket = new QTcpSocket(this);
socket->connectToHost("127.0.0.1", 12345);if (socket->waitForConnected(3000)) {QByteArray data = "Hello, server!";qint64 bytesWritten = socket->write(data);socket->flush(); // 建议 flush 确保尽快发送qDebug() << "Bytes written:" << bytesWritten;
}

示例 2:构造二进制数据并发送

QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);// 写入数据(可用来发送结构化内容)
int id = 42;
float value = 3.14f;
stream << id << value;socket->write(data);

示例 3:文件读取并通过 socket 发送(推荐使用 QByteArray)

QFile file("image.jpg");
if (file.open(QIODevice::ReadOnly)) {QByteArray fileData = file.readAll(); // 读入整个文件内容socket->write(fileData);
}

使用 QByteArray + 分段写入大数据的完整示例:
LargeDataSender.h

#ifndef LARGEDATASENDER_H
#define LARGEDATASENDER_H#include <QObject>
#include <QTcpSocket>class LargeDataSender : public QObject
{Q_OBJECTpublic:explicit LargeDataSender(QTcpSocket *socket, QObject *parent = nullptr);void send(const QByteArray &data);  // 启动发送过程private slots:void onBytesWritten(qint64 bytes);private:void writeNextChunk();QTcpSocket *m_socket;QByteArray m_data;qint64 m_totalSize;qint64 m_bytesSent;const int CHUNK_SIZE = 64 * 1024; // 每次写 64KB
};#endif // LARGEDATASENDER_H

LargeDataSender.cpp

#include "LargeDataSender.h"
#include <QDebug>LargeDataSender::LargeDataSender(QTcpSocket *socket, QObject *parent): QObject(parent), m_socket(socket), m_totalSize(0), m_bytesSent(0)
{connect(m_socket, &QTcpSocket::bytesWritten, this, &LargeDataSender::onBytesWritten);
}void LargeDataSender::send(const QByteArray &data)
{m_data = data;m_totalSize = data.size();m_bytesSent = 0;writeNextChunk(); // 立即尝试写第一段
}void LargeDataSender::writeNextChunk()
{while (m_bytesSent < m_totalSize) {qint64 remaining = m_totalSize - m_bytesSent;qint64 toWrite = qMin(remaining, static_cast<qint64>(CHUNK_SIZE));qint64 written = m_socket->write(m_data.constData() + m_bytesSent, toWrite);if (written == -1) {qWarning() << "Socket write failed!";return;}m_bytesSent += written;if (written < toWrite) {// 写入未完全,可能缓冲区满了,等 bytesWritten 再继续break;}}if (m_bytesSent == m_totalSize) {qDebug() << "All data sent (" << m_totalSize << " bytes)";disconnect(m_socket, &QTcpSocket::bytesWritten, this, &LargeDataSender::onBytesWritten);}
}void LargeDataSender::onBytesWritten(qint64 /*bytes*/)
{writeNextChunk(); // 写入下一段数据
}

使用方法:

QTcpSocket *socket = new QTcpSocket(this);
socket->connectToHost("127.0.0.1", 12345);if (socket->waitForConnected(3000)) {QFile file("largefile.bin");if (file.open(QIODevice::ReadOnly)) {QByteArray fileData = file.readAll();LargeDataSender *sender = new LargeDataSender(socket, this);sender->send(fileData); // 启动分段发送}
}

2.5 QByteArray QIODevice::read(qint64 maxSize)

参数说明:maxSize是要读取的最大字节数。实际读取的可能小于 maxSize,具体取决于设备中当前可用的数据量。

返回值

  • 返回一个 QByteArray,包含读取到的数据。
  • 如果没有数据可读,则返回空的 QByteArray(不是 nullptr)。
  • 如果读取失败(如设备未打开),返回空。

使用示例(基于 QTcpSocket)
示例 1:简单读取 socket 中的数据

connect(socket, &QTcpSocket::readyRead, this, [=]() {QByteArray data = socket->read(1024); // 最多读取 1024 字节qDebug() << "Received:" << data;
});

示例 2:持续读取直到没有更多数据(适合大数据)

connect(socket, &QTcpSocket::readyRead, this, [=]() {while (socket->bytesAvailable() > 0) {QByteArray chunk = socket->read(4096); // 每次读 4KBprocessData(chunk);}
});

注意事项
在这里插入图片描述
示例 3:读取带长度前缀的结构化数据
这种方式适合从 TCP 中接收完整消息(防止粘包问题)。

void MyReceiver::onReadyRead()
{QDataStream in(socket);in.setByteOrder(QDataStream::BigEndian);static quint32 expectedSize = 0;while (true) {if (expectedSize == 0) {if (socket->bytesAvailable() < sizeof(quint32))return;in >> expectedSize; // 先读长度}if (socket->bytesAvailable() < expectedSize)return;QByteArray payload = socket->read(expectedSize);processPayload(payload);expectedSize = 0; // 重置准备读下一包}
}

2.6 QByteArray QIODevice::readAll() const
功能说明:

  • 读取并返回 socket 缓冲区中当前已接收到的全部数据。
  • 不会阻塞,只能读取当前可用的部分数据(TCP 流可能分段到达)。
  • 回一个 QByteArray,包含已读数据;若没有数据,返回空。

使用示例:
示例 1:读取客户端发来的完整数据(非结构化协议)

connect(socket, &QTcpSocket::readyRead, this, [=]() {QByteArray data = socket->readAll();qDebug() << "Received:" << data;
});

2.7 bool QIODevice::isOpen() const

作用:判断 socket(或其他 IO 设备)是否被成功打开。对 QTcpSocket 来说,打开意味着 connectToHost() 已成功调用,并进入连接状态(ConnectedState)。

使用示例

QTcpSocket *socket = new QTcpSocket(this);
socket->connectToHost("127.0.0.1", 12345);if (socket->waitForConnected(3000)) {if (socket->isOpen()) {qDebug() << "Socket is open.";}
}

2.8 bool QAbstractSocket::isValid() const

作用:判断 socket 的底层文件描述符是否有效。也就是它是否关联了一个系统资源(如 socket fd)。
即使 isOpen() 返回 true,isValid() 也可能是 false,如果底层 socket 描述符丢失或已关闭。

使用示例

if (socket->isValid()) {qDebug() << "Socket is valid (underlying descriptor exists).";
}

区别总结
在这里插入图片描述
建议使用方式

if (socket->isOpen() && socket->isValid()) {socket->write("ping");
}

检测当前 socket 是否仍处于有效连接状态

bool isSocketConnected(QTcpSocket *socket)
{if (!socket)return false;// 判断 socket 是否处于 ConnectedStateif (socket->state() != QAbstractSocket::ConnectedState)return false;// 是否逻辑打开if (!socket->isOpen())return false;// 底层 socket 是否还有效if (!socket->isValid())return false;// 可选:检测是否最近有错误if (socket->error() != QAbstractSocket::UnknownSocketError)return false;return true;
}

使用方法:

if (isSocketConnected(socket)) {socket->write("Hello\n");
} else {qDebug() << "Socket disconnected or invalid!";
}

2.9 bool QAbstractSocket::waitForConnected(int msecs = 30000)

参数说明:msecs是最长等待时间,单位是毫秒(ms)默认是 30000ms(30秒)

返回值:true表示成功连接到服务器,false表示超时、错误,未连接成功

使用示例(客户端连接服务器)

QTcpSocket *socket = new QTcpSocket(this);
socket->connectToHost("127.0.0.1", 12345); // 发起连接if (socket->waitForConnected(3000)) { // 最多等待 3 秒qDebug() << "Connected to server!";socket->write("hello");
} else {qWarning() << "Connection failed:" << socket->errorString();
}

2.10 bool QIODevice::waitForReadyRead(int msecs)
是 Qt 中 QIODevice 提供的一个阻塞函数,常用于 QTcpSocket 等设备,等待有数据可读。当你想同步读取数据时,这个函数非常实用。

参数说明:最多等待时间(单位:毫秒)默认由我们传入,例如 3000 表示最多等 3 秒

返回值:true表示已有数据可读,readyRead() 被触发,false表示超时或设备出错,无数据可读

使用示例(读取数据)

QTcpSocket socket;
socket.connectToHost("127.0.0.1", 12345);if (socket.waitForConnected(3000)) {socket.write("hello");if (socket.waitForReadyRead(3000)) {QByteArray data = socket.readAll();qDebug() << "Received:" << data;} else {qWarning() << "No response from server, timeout!";}
} else {qWarning() << "Failed to connect to server!";
}

三、常用的信号函数

在这里插入图片描述
基本使用示例:

#include <QTcpSocket>
#include <QDebug>class MyTcpClient : public QObject {Q_OBJECT
public:MyTcpClient(QObject* parent = nullptr) : QObject(parent) {socket = new QTcpSocket(this);connect(socket, &QTcpSocket::connected, this, &MyTcpClient::onConnected);connect(socket, &QTcpSocket::readyRead, this, &MyTcpClient::onReadyRead);connect(socket, &QTcpSocket::disconnected, this, &MyTcpClient::onDisconnected);connect(socket, &QTcpSocket::errorOccurred, this, &MyTcpClient::onError);socket->connectToHost("127.0.0.1", 12345);  // 连接服务器}private slots:void onConnected() {qDebug() << "Connected to server.";socket->write("Hello, Server!");  // 向服务器发送数据}void onReadyRead() {QByteArray data = socket->readAll();qDebug() << "Received from server:" << data;}void onDisconnected() {qDebug() << "Disconnected from server.";}void onError(QAbstractSocket::SocketError socketError) {qDebug() << "Socket error:" << socketError << socket->errorString();}private:QTcpSocket* socket;
};

注意事项:

  • QTcpSocket 一般在主线程使用,不阻塞 UI,数据通过信号处理;
  • 使用 write() 后可能不会立刻发送,建议配合 flush() 或等待 bytesWritten;
  • 注意处理粘包和拆包问题,TCP 是面向流的协议;
  • 可与 QDataStream 配合使用实现结构化数据传输。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词