1. 程式人生 > >QT中ReadyRead()訊號只觸發一次問題解決

QT中ReadyRead()訊號只觸發一次問題解決

1、傳送端Write一次,那麼接收方就會有新資料到達,ReadyRead()訊號就會觸發一次,這種說法是錯誤的。

2、傳送方和接收方沒有一 一對應關係,傳送端Write()函式呼叫一次,假如這一次Write了較大資料(2M),那麼接收方ReadyRead()訊號往往會觸發兩次以上,反過來,如果傳送方Write()函式被呼叫了兩次或是以上,接收方的ReadyRead()訊號也可能只調用一次。經過程式證明這是正確的。

3、摘自網友:文件明明說有新的資料來,ReadyRead()訊號就會觸發一次,其實,這裡說新的資料來,不是說從傳送端有新的資料來到你的機子,而是資料從你的Tcp/ip協議棧到達你的Qt應用程式,也就是系統io緩衝區到達Qt應用程式,資料從系統到達你的Qt應用程式一次,readyread訊號就會觸發一次。還有一個非常要注意的詞就是“only once”,僅僅一次。什麼意思呢?其實是這樣的,第一次資料來的時候,觸發一次readyread訊號,但如果此時你的readyread槽函式還沒有及時執行,而新的資料又來了而且來了很多次(在QTcpSocket快取沒有滿的情況下,滿的情況下系統不會再發資料給應用),那麼,這些所有的都將會只再觸發一次readyread訊號。如果此時你的readyread槽函式執行了,那麼這時候來的新的資料就會觸發第三個readyread訊號。也就是說,還沒有響應的readyread訊號最多隻有兩個。想想也是啊,如果我傳送端一直髮送資料,你的系統就一直將資料傳送給你的應用,然後readyread訊號一直觸發,觸發到成千上萬個,那豈不是很傻的操作。

在寫程式的過程中,我遇到的問題是:傳送方傳送了傳送了兩次資料,但是接收方只觸發了一次ReadyRead()訊號,問題程式碼如下:

先說下程式碼採用的資料包模型:資料長度(qint64)+命令型別(qint64)+資料。

傳送程式碼:

void char_Server::sendResult(QTcpSocket* psock,QString strRet,int nRet)
{
	qint64 totalBytes = 0;

	QByteArray block;
	QDataStream output(&block,QIODevice::WriteOnly);
	output.setVersion(QDataStream::Qt_4_0);
	totalBytes = strRet.toUtf8().size();

	output << qint64(totalBytes) << qint64(nRet);
	output << strRet;
	psock->write(block);
}

sendResult()函式連續傳送兩次。

接收方程式碼:

/*
** 功能:接收伺服器傳送來的資料
** 資料型別:登入結果、註冊結果、聊天訊息...
*/
void SocketHander::readMessage()
{
	QDataStream in(m_tcpSocket);
	in.setVersion(QDataStream::Qt_4_0);

	if (blocksize == 0)
	{
		if (m_tcpSocket->bytesAvailable() < (int)sizeof(qint64))
			return;

		in >> blocksize;
	}

	if (cmd == 0)
	{
		if (m_tcpSocket->bytesAvailable() < (int)sizeof(qint64))
			return;

		in >> cmd;
	}

	if(m_tcpSocket->bytesAvailable() < blocksize)
		return;

	in >> strMsg;

	emit logsignal(cmd,strMsg);

	blocksize = 0;
	cmd = 0;
	strMsg.clear();
}

經過多次測試發現:接收方的ReadyRead()訊號只觸發了一次,第二次沒有觸發,資料也沒有收到。問題原因上面也說了,兩次傳送確實只觸發了一次訊號,對程式碼進行優化如下:

/*
** 功能:接收伺服器傳送來的資料
** 資料型別:登入結果、註冊結果、聊天訊息...
*/
void SocketHander::readMessage()
{
	QDataStream in(m_tcpSocket);
	in.setVersion(QDataStream::Qt_4_0);

	forever{

		if (blocksize == 0)
		{
			if (m_tcpSocket->bytesAvailable() < (int)sizeof(qint64))
				return;

			in >> blocksize;
		}

		if (cmd == 0)
		{
			if (m_tcpSocket->bytesAvailable() < (int)sizeof(qint64))
				return;

			in >> cmd;
		}

		if(m_tcpSocket->bytesAvailable() < blocksize)
			return;

		in >> strMsg;

		emit logsignal(cmd,strMsg);

		blocksize = 0;
		cmd = 0;
		strMsg.clear();
	}
}