Qt的TCP通讯

本文介绍了使用Qt进行TCP通信的基本步骤,包括服务端和客户端的创建及信号处理。服务端通过QTcpServer监听连接,利用newConnection信号获取QTcpSocket对象进行数据交互。客户端通过QTcpSocket连接服务端,处理readyRead和disconnected信号进行数据读写。文章还讨论了拆包封包问题,强调了接收端需要处理可能的分块数据,以正确重组消息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基本使用

使用Qt的进行TCP通讯,主要用到两个类,QTcpServer和QTcpSocket。前者主要用于服务端的监听,相当于原始socket中由socket函数创建的监听socket;后者主要用于读写数据,相当于原始socket中由accept函数返回的连接socket。

服务端的大致使用流程如下:

  1. 创建QTcpServer对象,调用listen函数进行IP和端口号的监听,执行过该函数后,客户端就可以进行连接操作了,创建的服务器直接就是多连接多消息服务器,不需要我们去处理并发连接的问题。
  2. 监听QTcpServer对象的newConnection信号,每当有客户端连接后就会发射该信号
  3. 在newConnection的槽函数中使用nextPendingConnection函数获取连接的客户端,返回值是一个QTcpSocket对象,我们就用该对象和客户端进行数据读写
  4. 得到QTcpSocket对象后绑定该对象的readyRead和disconnected信号,当客户端有数据发送过来时会发射readyRead信号,当客户端断开连接时会发射disconnected信号。

需要注意的是,一般需要在disconnected的槽函数中通过deleteLater函数删除该连接的QTcpSocket对象,如果不进行手动删除,Qt会在QTcpServer对象销毁时自动消除所有连接的QTcpSocket对象,在此之前,那些断开的QTcpSocket对象会一直占用内存,造成内存没必要的浪费。

客户端的大致使用流程如下:

  1. 创建QTcpSocket对象,绑定该对象的readyRead和disconnected信号,这点同服务端一样
  2. 调用connectToHost函数连接服务端
  3. 调用waitForConnected或其他函数判断是否连接成功
  4. 在readyRead的槽函数中收发数据
  5. 调用close函数断开连接,销毁该QTcpSocket对象

相对于服务端,客户端的disconnected倒是没那么重要,是否需要处理看情况而定。

拆包封包问题

首先需要了解的是readyRead信号的行为,该信号只有在有新数据到来时发射一次,与缓冲区中是否还有未读数据无关。比如发送端一次发来了十个字节的数据,但接收端一次只能读取五个字节,需要在一个信号响应过程中读两次,因为该信号只会发射一次。通常情况下要在槽函数中写一个循环来读取数据,如果缓冲区中的所有数据都读完了,再次调用read函数会返回0字节。
发送端处理起来比较简单,write函数会将指定的数据无脑写入缓冲区,由Qt自行处理封包拆包问题,比较麻烦的是接收端。
我们每次发送的数据长度是不确定的,而Qt又不管我们调用过几次write,也不管每次发送多少字节,都是无脑写入缓冲区,这样就可能回造成一个问题,如果每次发送的数据很小,接收端会一次接收到多次write的数据,如果发送的数据很大,接收端一次接收的数据又很可能不完整,需要接收几次才行,甚至还很可能在接收端的一次接收时会接收到两次不完整的数据,第一次的后半段和第二次的前半段。因此在接收端需要进行拆包封包的操作,自行拼凑我们想要的数据。

具体例子

我们首先定义一个接收端和发送端共用的数据协议

enum ProtocolType
{
   
	TEXT = 0,
	PICTURE,
	END,
	MAX
};

struct MYBSProtocol
{
   
	int length;
	ProtocolType type;
	char data[0];
};

然后是服务端

class Server : public QObject
{
   
	Q_OBJECT

public:
	explicit Server(QObject *parent = nullptr);
	~Server();

	void startListen(int nPort);
private:
	QTcpServer *m_server;

public slots :
	void onNewConnection();
	void onReadyRead();
	void onDisconnected();
};

Server::Server(QObject *parent)
	: QObject(parent)
{
   
	m_server = new QTcpServer;
}

Server::~Server()
{
   
	delete m_server;
}

void Server::startListen(int nPort)
{
   
	if (m_server->listen(QHostAddress::Any, nPort))
		qDebug() << "listen port "<< nPort << " ok";
	else
		qDebug() << "listen err";
	connect(m_server, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
}

void Server::onNewConnection()
{
   
	QTcpSocket *socket = m_server->nextPendingConnection();
	QString ip = socket->peerAddress().toString();
	quint16 port = socket->peerPort();
	qDebug() << "new client connect:" << ip << ":" << port;
	connect(socket, SIGNAL(readyRead()<
QT 是一个很强大的跨平台应用程序开发框架,它提供了丰富的网络编程类库,可以很方便地实现 TCP 通讯。下面我们就来看一下 QT 中如何使用 TCP。 首先需要在代码中包含 `QTcpSocket` 和 `QTcpServer` 头文件: ```cpp #include <QTcpSocket> #include <QTcpServer> ``` ## TCP 客户端 TCP 客户端通常需要以下步骤: 1. 创建一个 `QTcpSocket` 对象; 2. 连接到服务器; 3. 发送数据; 4. 接收服务器返回的数据; 5. 断开连接。 下面是一个简单的 TCP 客户端的代码示例: ```cpp #include <QTcpSocket> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 创建 TCP socket 对象 QTcpSocket socket; // 连接到服务器 socket.connectToHost("127.0.0.1", 1234); if (!socket.waitForConnected()) { qDebug() << "Failed to connect to server!"; return -1; } // 发送数据 QByteArray data = "Hello, server!"; socket.write(data); socket.waitForBytesWritten(); // 接收数据 socket.waitForReadyRead(); QByteArray response = socket.readAll(); qDebug() << "Server response:" << response; // 断开连接 socket.close(); return a.exec(); } ``` 上面的代码中,我们首先创建了一个 `QTcpSocket` 对象,然后使用 `connectToHost` 函数连接到服务器。如果连接失败,会输出一条错误信息并退出程序。 接着,我们发送一条消息给服务器,使用 `write` 函数将数据写入 socket 缓冲区,并调用 `waitForBytesWritten` 等待数据发送完成。 然后,我们等待服务器返回数据,使用 `waitForReadyRead` 等待数据可读,并使用 `readAll` 函数将所有可读数据读取出来。 最后,我们关闭 socket 连接。 ## TCP 服务器 TCP 服务器通常需要以下步骤: 1. 创建一个 `QTcpServer` 对象; 2. 绑定服务器地址和端口; 3. 监听客户端连接; 4. 接收客户端数据; 5. 回复客户端数据; 6. 断开连接。 下面是一个简单的 TCP 服务器的代码示例: ```cpp #include <QTcpServer> #include <QTcpSocket> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 创建 TCP server 对象 QTcpServer server; // 绑定服务器地址和端口 if (!server.listen(QHostAddress::Any, 1234)) { qDebug() << "Failed to start server!"; return -1; } qDebug() << "Server started!"; while (server.isListening()) { // 监听客户端连接 QTcpSocket *socket = server.nextPendingConnection(); qDebug() << "New client connected:" << socket->peerAddress().toString(); // 接收客户端数据 socket->waitForReadyRead(); QByteArray data = socket->readAll(); qDebug() << "Received data from client:" << data; // 回复客户端数据 QByteArray response = "Hello, client!"; socket->write(response); socket->waitForBytesWritten(); // 断开连接 socket->close(); } return a.exec(); } ``` 上面的代码中,我们首先创建了一个 `QTcpServer` 对象,并使用 `listen` 函数绑定服务器地址和端口。如果绑定失败,会输出一条错误信息并退出程序。 然后,我们进入一个无限循环,使用 `nextPendingConnection` 函数监听客户端连接。当有新的客户端连接时,会输出一条提示信息,并返回一个 `QTcpSocket` 对象,用于与客户端通讯。 接着,我们使用 `waitForReadyRead` 等待客户端发送数据,并使用 `readAll` 函数读取所有可读数据。 然后,我们回复客户端数据,使用 `write` 函数将数据写入 socket 缓冲区,并调用 `waitForBytesWritten` 等待数据发送完成。 最后,我们关闭 socket 连接。 这就是 QT使用 TCP 的基本步骤,希望对你有所帮助!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

木千

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

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

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

打赏作者

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

抵扣说明:

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

余额充值