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();
}
}