Qt的TCP通讯

基本使用

使用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()<
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

木千

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

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

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

打赏作者

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

抵扣说明:

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

余额充值