微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

怪异的QTcpSocket行为

如何解决怪异的QTcpSocket行为

调试下几行代码时,我一直做噩梦,除了我所看到的之外,肯定还有一些隐藏的东西。

这是客户端和服务器之间的连接

QByteArray Client::request(QByteArray cmd)
{
    //DEBUG << "Command: " << cmd.toStdString().c_str();
    QTcpsocket *socket = new QTcpsocket(this);
    socket->connectToHost(hostAddress,port,qiodevice::ReadWrite);
    if (socket->waitForConnected(TIMEOUT))
    {
        socket->write(cmd);
        DEBUG << "Size of bytes written :" << sizeof (cmd);
    }
    else DEBUG << "Couldn't connect to socket";
    if (socket->waitForBytesWritten(TIMEOUT)) DEBUG << "Command sent";
    else DEBUG << "Couldn't write to socket";
    if (socket->waitForReadyRead(TIMEOUT))
    {
        QByteArray data = socket->readAll();
        socket->close();
        return data;
    }
    else DEBUG << "No reply from Server";
    return QByteArray();
}

对于客户端,对于服务器而言,基本上就是这些。

class ConnectionHandler : public QTcpserver
{
    Q_OBJECT


public:
    explicit ConnectionHandler(QObject *parent = 0);
    void write(QByteArray);

protected:
    void ConnectionHandler::incomingConnection(qintptr descriptor)
{
    DEBUG << "ConnectionHandler:" << "Incoming Connection :" << descriptor;
    ConnectionThread *thread = new ConnectionThread(this,descriptor);
    connect(thread,&QThread::finished,thread,&QThread::deleteLater);
    connect(thread,&ConnectionThread::signalIncomingMessage,this,&ConnectionHandler::slotIncomingMessage);
    connect(this,&ConnectionHandler::signalOutgoingMessage,&ConnectionThread::slotOutgoingMessage);
    thread->start();
}

public slots:
    void slotIncomingMessage(QByteArray);
    void slotListen(bool checked){
    if (checked)
    {
        if (!this->listen(QHostAddress::LocalHost,PORT_NUMBER)) {
            DEBUG << "ConnectionHandler:" << "Could not start the server!";
        } else {
            DEBUG << "ConnectionHandler:" << "Listening...";
        }
    } else {
        this->close();
        DEBUG << "ConnectionHandler:" << "Connection Closed!";
    }
}

signals:
    void signalOutgoingMessage(QByteArray);
};
class ConnectionThread : public QThread
{
    Q_OBJECT
public:
    ConnectionThread(QObject* parent = 0,qintptr descriptor = -1){
    if (descriptor != -1)
    {
        socket = new QTcpsocket();

        DEBUG << "ConnectionThread: Connecting to socket number" << descriptor;
        if (!socket->setSocketDescriptor(descriptor))
        {
            DEBUG << "ConnectionThread: Connection Failed.";
            DEBUG << socket->errorString();
        }
        else
        {
            DEBUG << "ConnectionThread: Connected Successfully.";
            connect(socket,&QAbstractSocket::disconnected,&ConnectionThread::slotSocketdisconnected);
        }
    }
    else {
        DEBUG << "ConnectionThread: Please provide a descriptor for the connection thread";
    }
}
    void run() override{
    if (socket->state() != QAbstractSocket::ConnectedState)
    {
        DEBUG << "ConnectionThread: Socket is not connected!";
        DEBUG << "ConnectionThread: Closing Thread!";
        emit signalThreadError(socket->errorString());
    }
    else
    {
        DEBUG << "ConnectionThread: Socket is Connected.";
        connect(socket,&qiodevice::readyRead,&ConnectionThread::slotthreadReadyRead,Qt::DirectConnection);
        exec();
    }
}

signals:
    void signalThreadError(QString);
    void signalIncomingMessage(QByteArray);

public slots:
    void slotthreadReadyRead(){
    QByteArray msg = socket->readAll();

    if (!msg.isEmpty()) {
        emit signalIncomingMessage(msg);
    }

    DEBUG << "ConnectionThread: Data in:" << msg;
}
    void slotSocketdisconnected();
    void slotOutgoingMessage(QByteArray msg)
{
    if (socket != nullptr) {
        socket->write(msg);
        if (socket->waitForBytesWritten(TIMEOUT)) {
            DEBUG << "ConnectionThread: Outgoing Message: " << msg;
        } else {
            DEBUG << "ConnectionThread: Outgoing Message Timeout";
        }
    }
}

private:
    QTcpsocket *socket = nullptr;
};

基本上发生的事情是,有时它会接收来自客户端的传入命令,而在大多数情况下却没有,实际上,它实际上接收了一些命令而忽略了一些不同的命令。

这是一个比较诡异的行为,请注意//DEBUG << "Command: " << cmd.toStdString().c_str();中的行Client::request(),当该行被注释掉时,服务器实际上会收到一些命令,否则,服务器会聋。

这是注释掉该行的输出

Size of bytes written : 8
Command sent
target num = 4  (a reply was received)
Size of bytes written : 8
Command sent
No reply from Server

当提到的行没有被注释掉时,这是另一个输出

Command:  NUMB

Size of bytes written : 8
Command sent
No reply from Server
target num = 0
Command:  TRAN

Size of bytes written : 8
Command sent
No reply from Server

更新 解决了在客户端进行套接字编写与打开线程之间发生的竞争,这对我来说是成功的,在这里我不需要使用线程,因此我只删除了它。由@Botje回答

解决方法

该问题是由种族引起的:

  1. (主线程)ConnectionHandler :: incomingConnection创建一个新的ConnectionThread
  2. (主线程)ConnectionThread构造函数创建一个套接字,该套接字也由主线程拥有
  3. (主线程)返回事件循环,轮询套接字上的传入数据
  4. (ConnectionThread)run方法将插槽连接到readyRead信号

如果数据到达2到4之间的窗口,则readyRead信号将不会接收它。 有几种可能的解决方案,但是最简单的方法可能是在构造后将套接字移到ConnectionThread,或者将其构造推迟到ConnectionThread :: run方法。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。