2017-06-13 475 views
1

我是Qt的新手,我有點掙扎。我想從客戶端發送一個字符串到使用QTcpSocket的服務器。QTcpSocket :: readAll()是空的

客戶端:

QByteArray block; 
QDataStream out(&block, QIODevice::WriteOnly); 
out.setVersion(QDataStream::Qt_4_0); 
out << name; 

tSock->connectToHost(ipAddress, portNumb.toInt()); 
tSock->waitForConnected(); 
tSock->write(block); // block is not empty 
tSock->flush(); 

服務器端:

void Server::readyRead() 
{ 
    QByteArray block; 
    QDataStream out(&block, QIODevice::ReadOnly); 
    out << tcpSocket->readAll(); 
    QString name(block); // block is empty 
    players.insert(name, tcpSocket); 
    std::cout << "name: " << name.toStdString(); // TODO remove 
} 

在服務器端程序進入readyRead()當接收數據時,但塊是空的,儘管在客戶端塊不是空的當tSock->write(block)被稱爲...哪裏似乎是問題?我將不勝感激任何幫助。謝謝!

回答

2

編輯:你的錯誤是,你打開只讀模式out數據流,但要儘量接收的字節數組分配給它:

void Server::readyRead() 
{ 
    QByteArray block; 
    QDataStream out(&block, QIODevice::ReadOnly); // !mistake, WriteOnly mode is needed 
    out << tcpSocket->readAll(); // this is write operation 
    //... 
} 

附加條件:請注意,有這種情況下有用的Serialization mechanism of Qt Data Types

tSock->write(block); // this is write just a raw data of the block, not the "QByteArray" 

可以使用流操作編寫必要的Qt數據類型直接在插座,沒有皈依到QByteArray

// Connect firstly 
tSock->connectToHost(ipAddress, portNumb.toInt()); 
tSock->waitForConnected(); 

// Then open a data stream for the socket and write to it: 
QDataStream out(tSock, QIODevice::WriteOnly); 
out.setVersion(QDataStream::Qt_4_0); 
out << name; // write string directly without a convertion to QByteArray 

// Then you may 
tSock->flush(); 

在客戶端,然後使用服務器端的類似的流操作:

void Server::readyRead() 
{ 
    QString name; 
    QDataStream in(tcpSocket, QIODevice::ReadOnly /*or QIODevice::ReadWrite if necessary */); 
    in.setVersion(QDataStream::Qt_4_0);  
    in >> name; // read the string 
    //... 
} 

也有可能讀/寫對象的另一種Qt的I/O設備:QFileQSerialPortQProcessQBuffer等。

編輯2:不保證在readyRead信號你會收到完整的包發送。因此請參閱下面的示例。


請注意,在現實情況下(當你在客戶端 - 服務器通信的幾個不同的數據包,並且不知道什麼樣的,你已經收到了幾個可能的數據包的),通常有使用更復雜的算法,這是因爲下面的情況下,可能對在通信的readyRead事件發生:

  1. 全部分組接收到
  2. 僅接收到包的一部分
  3. 個收到幾個包一起

的算法的變體(Qt 4 Fortune Client Example):

void Client::readFortune() // on readyRead 
{ 
    QDataStream in(tcpSocket); 
    in.setVersion(QDataStream::Qt_4_0); 

    if (blockSize == 0) { 
     if (tcpSocket->bytesAvailable() < (int)sizeof(quint16)) 
      return; 

     in >> blockSize; 
    } 

    if (tcpSocket->bytesAvailable() < blockSize) 
     return; 

    QString nextFortune; 
    in >> nextFortune;  

    //... 
} 

的Qt 4。0是Qt的舊版本,因此請參閱Qt 5.9 Fortune Client Example

+0

一般來說,一個插座上重新創建一個'QDataStream'或者是錯誤的,因爲你會失去流的狀態,並且可能會丟失數據以及。 –

+0

@KubaOber,但這是Qt 4文檔中的一個例子,我確信上述'readFortune()'函數的代碼是正確的 –

+0

它是正確的,但它會很快變得不正確修改它。 –

0

看起來您的服務器似乎沒有足夠的時間從客戶端寫入數據。 請試試這個..

tcpsocket->waitForBytesWritten(1000); 
0

readyRead代碼錯誤地填寫了block。你還假設你會讀取足夠的數據來傳達整個字符串。這從來沒有保證。所有你知道什麼時候readyRead激發它至少有一個字節被讀取。交易系統可以幫助解決這個問題。

這將是一個正確的實現:

class Server : public QObject { 
    Q_OBJECT 
    QPointer<QTcpSocket> m_socket; 
    QDataStream m_in{m_socket.data(), &QIODevice::ReadOnly}; 
    void onReadyRead(); 
public: 
    Server(QTcpSocket * socket, QObject * parent = {}) : 
    QObject(parent), 
    m_socket(socket) 
    { 
    connect(socket, &QIODevice::readyRead, this, &Server::onReadyRead); 
    } 
} 

void onReadyRead() { 
    while (true) { // must keep reading as long as names are available 
    QString name; 
    in >> name; 
    if (in.commitTransaction()) { 
     qDebug << name; 
     players.insert(name, &m_socket.data()); 
    } else 
     break; 
    } 
};