【问题标题】:QTcpSocket state always connected, even unplugging ethernet wireQTcpSocket 状态始终连接,即使拔掉以太网线
【发布时间】:2012-05-13 18:42:10
【问题描述】:

我有一个 QTcpSocket,我正在读入一个循环。每次读取一个完整的数据包,或者出现错误时,我都会手动检查循环内套接字的状态,使用:

    while(true){
    if(socket->state()==QAbstractSocket::ConnectedState){
        qDebug()<<"Socket status: connected. Looking for packets...";
        if(socket->waitForReadyRead(2000)){
        //...
    }

当我执行de程序时,一旦连接并开始循环,它总是打印qDebug()&lt;&lt;"Socket status: connected. Looking for packets...";然后停留在waitForReadyRead,直到准备好读取一些数据。

问题是没有检测到断开连接。如果我从操作系统选项断开网络连接,或者即使我拔下以太网线,它的行为也是一样的:套接字状态等于QAbstractSocket::ConnectedState,所以它会继续,但当然没有收到任何东西。

我还尝试检测将disconnected() 信号(在第一次连接后)连接到重新连接函数的断开连接:

// Detect disconnection in order to reconnect
    connect(socket, SIGNAL(disconnected()), this, SLOT(reconnect()));

void MyClass::reconnect(){
    qDebug()<<"Signal DISCONNECTED emitted. Now trying to reconnect";
    panelGUI->mostrarValueOffline();
    socket->close();
    prepareSocket((Global::directionIPSerialServer).toLocal8Bit().data(), 8008, socket);
    qDebug()<<"Reconnected? Status: "<<socket->state();
}

但是信号永远不会发出,因为这段代码永远不会被执行。这是合乎逻辑的,因为它看起来像套接字状态总是ConnectedState

如果我再次插入,连接会恢复并再次开始接收数据,但我确实想检测断开连接以在 GUI 上显示“已断开连接”。

为什么 QTcpSocket 会这样,我该如何解决这个问题?

编辑:我在类构造函数中创建套接字,然后初始化调用 prepareSocket 函数:

socket = new QTcpSocket();
socket->moveToThread(this);

bool prepareSocket(QString address, int port, QTcpSocket *socket) {
    socket->connectToHost(address, port);
    if(!socket->waitForConnected(2000)){
        qDebug()<<"Error creating socket: "<<socket->errorString();
        sleep(1);
        return false;
    }
    return true;
}

【问题讨论】:

  • 拔掉电缆等了多长时间?
  • 不知道... 10 秒? POSIX 套接字确定它们已断开连接的必要性要少得多。我应该等多久?
  • TCP 超时远高于此。至少等待几分钟。
  • 在这个类似的问题stackoverflow.com/questions/10331016 中,我提到(在我回答后的cmets)TCP_KEEPALIVE 可能有助于在一段时间后检测网络断开连接。那有帮助吗?您在寻找更快的方法吗?
  • 但是对于 POSIX 套接字,它们需要大约一秒钟才能开始说“网络无法访问”。为什么 QTcpSockets 没有?在这 2 分钟之前没有任何方法可以检测到这一点吗? (不使用保活协议)

标签: c++ qt network-programming state qtcpsocket


【解决方案1】:

终于在这个Qt forum找到了解决办法:

如果一段时间内没有数据交换,TCP将开始发送 保持活动的段(基本上,带有确认的 ACK 段 number 设置为当前序列号减一)。另一个同行 然后回复另一个确认。如果这个确认是 在一定数量的探测段内未收到,连接 被自动丢弃。小问题是内核启动 自连接后 2 小时后发送保持活动段 变得空闲!因此,您需要更改此值(如果您的操作系统 允许)或在您的 协议(就像许多协议一样,例如 SSH)。 Linux 允许您 使用 setsockopt 更改它:

int enableKeepAlive = 1;
int fd = socket->socketDescriptor();
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &enableKeepAlive, sizeof(enableKeepAlive));

int maxIdle = 10; /* seconds */
setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &maxIdle, sizeof(maxIdle));

int count = 3;  // send up to 3 keepalive packets out, then disconnect if no response
setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &count, sizeof(count));

int interval = 2;   // send a keepalive packet out every 2 seconds (after the 5 second idle period)
setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &interval, sizeof(interval));

【讨论】:

  • 对于其他尝试这个的人:#include &lt;sys/socket.h&gt; #include &lt;netinet/in.h&gt; #include &lt;netinet/tcp.h&gt;
  • 在您创建 QTcpSocket 之后,此代码是否会立即运行?您是否必须调用另一个函数来在 Qt 的套接字上设置这些选项(或者它们是否立即在 Qt 套接字上生效)。
  • @TSG 我和你有同样的问题。
【解决方案2】:

我在使用 QT 客户端应用时也遇到过类似的问题。基本上我用定时器、信号和插槽来处理它。当应用程序启动时,它会启动一个 4 秒的 checkConnectionTimer。每 4 秒计时器到期,如果客户端套接字状态 != AbstractSocket::Connected 或 Connecting,它会尝试与 clientSocket->connectToHost

连接

当套接字发出“connected()”信号时,它会启动一个 5 秒的服务器心跳计时器。服务器应每 4 秒向其客户端发送一个 1 字节的心跳消息。当我收到心跳(或由 readyRead() 发出的任何类型的消息)时,我重新启动心跳计时器。因此,如果心跳计时器超时,我假设连接已关闭并调用clientSocket-&gt;disconnectFromHost ();

这对于服务器上所有不同类型的断开都非常有效,无论是优雅的还是其他的(拉扯电缆)。是的,它需要自定义心跳类型的东西,但归根结底,它是最快、最便携的解决方案。

我并不热衷于在内核中设置 KEEPALIVE 超时。这种方式更便携。 在构造函数中:

connect(clientSocket, SIGNAL(readyRead()), this, SLOT(readMessage()));
connect(clientSocket, SIGNAL(connected()), this, SLOT(socketConnected()));
connect(clientSocket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
connect(heartbeatTimer, SIGNAL(timeout()), this, SLOT(serverTimeout()));
...
// Other Methods

void NetworkClient::checkConnection(){
    if (clientSocket->state() != QAbstractSocket::ConnectedState &&
            clientSocket->state() != QAbstractSocket::ConnectingState){
        connectSocketToHost(clientSocket, hostAddress, port);
    } 
}

void NetworkClient::readMessage()
{
    // Restart the timer by calling start.
    heartbeatTimer->start(5000);
    //Read the data from the socket
    ...
}

void NetworkClient::socketConnected (){
    heartbeatTimer->start(5000);
}

void NetworkClient::socketDisconnected (){
    prioResponseTimer->stop();
}

void NetworkClient::serverTimeout () {
    clientSocket->disconnectFromHost();
}

【讨论】:

  • 我尝试了页面上的所有答案。这是唯一对我有用的。
  • 如果网络过载并且(心跳)数据包延迟了 2 或 3 秒,然后到达怎么办?
  • 你的prioResponseTimer来自哪里?
【解决方案3】:

试试这个信号槽连接:

connect(this, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onStateChanged(QAbstractSocket::SocketState)));

在插槽实现时:

void TCPWorker::onStateChanged(QAbstractSocket::SocketState socketState ){
qDebug()<< "|GSTCPWorkerThread::onStateChanged|"<<socketState;
...}

我有同样的问题,但是你的问题(始终连接),我有延迟 4-5 秒来接收断开信号,拔掉以太网线后。

仍在寻找解决方案,如果找到,请发布答案。

【讨论】:

  • 对于其他人阅读,这是行不通的。拔掉电线时不会调用 onStateChanged()。
【解决方案4】:

在 Qt 中尝试我的客户端模板:

class Client: public QTcpSocket {
   Q_OBJECT
public:
    Client(const QHostAddress&, int port, QObject* parent= 0);
    ~Client();
    void Client::sendMessage(const QString& );
private slots:
    void readyRead();
    void connected();
public slots:
    void doConnect();
};

在 cpp 上:

void Client::readyRead() {

    // if you need to read the answer of server..
    while (this->canReadLine()) {
    }
}

void Client::doConnect() {
    this->connectToHost(ip_, port_);
    qDebug() << " INFO : " << QDateTime::currentDateTime()
            << " : CONNESSIONE...";
}

void Client::connected() {
    qDebug() << " INFO : " << QDateTime::currentDateTime() << " : CONNESSO a "
            << ip_ << " e PORTA " << port_;
    //do stuff if you need
}


void Client::sendMessage(const QString& message) {
    this->write(message.toUtf8());
    this->write("\n"); //every message ends with a new line
}

我省略了一些代码作为构造函数和插槽连接.. 试试这个,如果它不起作用,可能是服务器端有问题..

【讨论】:

  • 我确信这行得通,它或多或少是我所拥有的。但是连接已经有效,我想要的是检测断开连接,而您的模板也没有发生这种情况。此外,服务器端还可以,因为它可以使用 POSIX 套接字
  • 你在某处使用void QAbstractSocket::disconnectFromHost ()吗?
猜你喜欢
  • 1970-01-01
  • 2023-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-15
  • 1970-01-01
  • 2013-02-09
相关资源
最近更新 更多