【问题标题】:Writing to QTcpSocket does not always emit readyRead signal on opposite QTcpSocket写入 QTcpSocket 并不总是在相反的 QTcpSocket 上发出 readyRead 信号
【发布时间】:2017-06-23 19:18:41
【问题描述】:

过去 5 天我一直坚持这个问题,我不知道如何继续。

概述:

我有一个client UIdata handler 库交互,而data handler 库使用network manager 库,这就是我的问题所在。

更多信息

首先,QT 为QTcpServer (Fortune Server) 和QTcpSocket (Fortune Client) 之间的交互提供了一个基本示例。

因此,我将这段代码实现为我自己的一个非常基本的示例,它就像一个魅力,没有任何问题。

我自己改编的财富clientserver备案(基本)

快速解释:

服务器应用程序运行,点击start server,然后在客户端,在字段中输入文本并点击connect to server,然后显示文本,简单!

问题:

将上面的代码实现到我的network manager 库中,不会触发上面服务器应用程序中的QTcpSocket::readyRead()

它连接到server,正如预期的那样,QTcpServer::newConnection() 被触发,之后client 写入套接字但服务器套接字上的readyRead() 不会触发,但是在示例中确实如此。

注意: portip address 在这个 server-client 应用示例和我当前的应用中使用了相同的 portip address,并且服务器也在运行。

更多信息:

从上面的代码中,我从客户端直接复制过来。仅更改/修改了 2 件事:

  • 发送到服务器的字符串
  • 方法的返回类型

这被复制到我的network mannager ::write() 方法中。在运行我的应用程序时,QMainWindow 的实例通过data handler 类传递,并创建了我的network manager 类的实例,该实例继承了QObject 并实现了Q_OBJECT 宏。

代码示例:

//client_UI类(sn-p):

data_mananger *dman = new data_mananger(this);                //this -> QMainWindow
ReturnObject r = dman->NET_AuthenticateUser_GetToken(Query);

//data_manager库(sn-p)

data_mananger::data_mananger(QObject *_parent) :
    parent(_parent)
{}

ReturnObject data_mananger::NET_AuthenticateUser_GetToken(QString Query){
    //Query like "AUTH;U=xyz@a;P=1234"

    //convert query string to char
        QByteArray ba = Query.toLatin1();

    //send query and get QList return
        ReturnCode rCode = networkManager.write(ba);

    //...
}

//netman库(sn-p)

//.h

class NETMANSHARED_EXPORT netman : public QObject
{
    Q_OBJECT
public
    netman();
    netman(QObject *_parent);
    //...

private:
    QTcpSocket *tcp_con;
    //...
};

//cpp

netman::netman(QObject *_parent) :
    parent(_parent)
{
    tcp_con = new QTcpSocket(parent);
}

        return;
    }
    serverIP.setAddress(serverInfo.addresses().first().toIPv4Address());
}

ReturnCode netman::write(QByteArray message, int portNumber){

    tcp_con->connectToHost(QHostAddress("127.0.0.1"), 5000);

    if (!tcp_con->waitForConnected())
    {
        qDebug(log_lib_netman_err) << "Unable to connect to server";
        return ReturnCode::FailedConnecting;
    }

    if (!tcp_con->isValid()) {
        qDebug(log_lib_netman_err) << "tcp socket invalid";
        return ReturnCode::SocketError;
    }

    if (!tcp_con->isOpen()) {
        qDebug(log_lib_netman_err) << "tcp socket not open";
        return ReturnCode::SocketError;
    }

    //    QByteArray block(message);
    QByteArray block;
    QDataStream out(&block,QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_0);

    out << QString("Hello world");

    if (!tcp_con->write(block)){
        qDebug(log_lib_netman_err) << "Unable to send data to server";
        return ReturnCode::WriteFailed;
    }
    else{
        qDebug(log_lib_netman_info) << "Data block sent";
        return ReturnCode::SentSuccess;
    }
}

结论:

客户端的核心代码已经完全实现了,但是我不明白为什么会出现这个错误。

非常感谢您的帮助/建议!

【问题讨论】:

  • 一个观察:你绝对不想每次写作时都connectToHost。即使您已经连接或正在连接,该功能也可能需要很长时间才能返回。
  • 你试过冲洗插座吗?
  • @NicolasHolthaus 对此表示感谢,但是我还没有完成我的网络管理器库。但阻碍我的问题是上面提到的。完成我的图书馆后,我会记住您的建议
  • @NicolasHolthaus 如果我使用与服务器客户端演示中的“完全相同”的实现,为什么要刷新?
  • 如果它解决了当前的问题,我会做出一个很好的解释;)程序流/事件循环的问题有时会导致这样的副作用。也就是说,仅仅因为你没有得到readyRead 并不意味着消息没有被传输。我现在无法将您的代码与财富示例进行比较,因为我的代理不允许我访问 pastebin 上的代码。

标签: c++ qt sockets qtcpsocket qtcpserver


【解决方案1】:

tcp_con-&gt;flush() 语句添加到写入函数的末尾。

为什么/如何工作

您的接收器中没有收到readyRead 信号,因为写入的数据正在缓冲到套接字中,但实际上并未“通过线路”传输。 flush() 命令导致缓冲区被传输。来自the docs

这个函数尽可能从内部写入 缓冲到底层网络套接字,没有阻塞。如果有任何数据 写好了,这个函数返回true;否则返回 false。

你应该怎么知道

在我的情况下,串行端口和冲洗有很多经验/挫败感。相当于“你重启了吗?”在套接字调试工具箱中。

如果其他一切正常,您可能不必flush,但它是特定于应用程序的,取决于套接字的生命周期、TCP 窗口大小、套接字选项设置和各种其他因素。也就是说,我总是刷新,因为我喜欢完全控制我的套接字,并且我想确保在我想要的时候传输数据。我不认为这是一种黑客行为,但在某些情况下,它可能表明存在其他问题。同样,特定于应用程序。

为什么缓冲区不能自己刷新?

我很确定在财富服务器示例中不需要刷新,因为它们 disconnectFromHostsendFortune() 函数的末尾,并且来自 Qt documentation

尝试关闭套接字。如果有待处理的数据 写,QAbstractSocket 会进入 ClosingState 并等到所有 数据已写入。

如果套接字也被销毁,它也会断开连接,但是从我的代码中可以看出,你也没有这样做,而且缓冲区也没有满,所以实际上可能没有什么能刺激缓冲区刷新自己.

其他原因可能是:

  • 流控制不会返回到事件循环(阻塞调用等),因此永远不会执行缓冲区刷新。
  • 传输发生在循环内部,看起来它会退出(例如while(dataToTransmit)),但实际上条件永远不会变为假,这会导致事件循环被阻塞。
  • Nagles 算法:缓冲区可能正在等待更多数据,然后才刷新自身以保持高网络吞吐量。您可以通过设置 QAbstractSocket::LowDelayOption 来禁用此功能,但它可能会对您的吞吐量产生不利影响……它通常用于对延迟敏感的应用程序。

【讨论】: