C++ QTcpSocket 状态始终连接,甚至拔掉以太网线

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10445122/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 14:04:02  来源:igfitidea点击:

QTcpSocket state always connected, even unplugging ethernet wire

c++qtnetwork-programmingstateqtcpsocket

提问by Roman Rdgz

I have a QTcpSocket and I am reading into a loop. Each time a full packet has been read, or there has been an error, I manually check the status of the socket inside the loop, with:

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

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

When I execute de program, once connected and the loop starts, it always prints qDebug()<<"Socket status: connected. Looking for packets..."; and then stucks at waitForReadyReaduntil some data is ready to be read.

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

The problem is that disconnections are not detected. If I disconnect from network from the OS options, or even if I unplug the ethernet wire, it behaves the same: Socket state equals QAbstractSocket::ConnectedState, so it goes on, but without receiving anything of course.

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

I also tried to detect disconnections connecting disconnected()signal (after fist connection) to a reconnect function:

我还尝试检测连接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();
}

But signal is never emited, because this code is never executed. Which is logical, since it looks like socket state is always ConnectedState.

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

If I plug again, connection is restored and starts to receive data again, but I do want to detect disconnections to show "Disconnected" at the GUI.

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

Why is QTcpSocket behaving this way, and how can I solve this problem?

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

EDIT: I'm creating socket at the class constructor, and then initialising calling prepareSocket function:

编辑:我在类构造函数中创建套接字,然后初始化调用 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;
}

采纳答案by Roman Rdgz

Finally found the solution in this Qt forum:

终于在这个Qt 论坛中找到了解决方案:

If no data is exchanged for a certain while, TCP will start sending keep-alive segments (basically, ACK segments with the acknowledgement number set to the current sequence number less one). The other peer then replies with another acknowledgement. If this acknowledgment is not received within a certain number of probe segments, the connection is automatically dropped. The little problem is that the kernel starts sending keep-alive segments after 2 hours since when the connection becomes idle! Therefore, you need to change this value (if your OS allows that) or implement your own keep-alive mechanism in your protocol (like many protocols do, e.g. SSH). Linux allows you to change it using setsockopt:

如果在一段时间内没有数据交换,TCP 将开始发送保持活动的段(基本上,确认号设置为当前序列号减一的 ACK 段)。另一个对等点然后用另一个确认进行回复。如果在一定数量的探测段内未收到此确认,则连接将自动断开。小问题是内核在连接空闲后 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));

回答by Matt Brown

I've been facing similar problems with a QT client app. Basically I handle it with Timers, signals and slots. When the app starts up, it starts a 4 second checkConnectionTimer. Every 4 seconds the timer expires, if the client socket state != AbstractSocket::Connected or Connecting, it attempt to connect with clientSocket->connectToHost

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

When the socket signals "connected()", it starts a 5 second server heartbeat timer. The server should send a one byte heartbeat message to its clients every 4 seconds. When I get the heartbeat (or any type of message signaled by readyRead()), I restart the heartbeat timer. So if the heartbeat timer ever has a timeout, I assume the connection to be down and it calls clientSocket->disconnectFromHost ();

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

This is working very well for all different kinds of disconnects on the server, graceful or otherwise (yanking cable). Yes it requires custom heartbeat type of stuff, but at the end of the day it was the quickest and most portable solution.

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

I wasn't to keen on setting KEEPALIVE timeouts in the kernel. This way its more portable. In the constructor:

我并不热衷于在内核中设置 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();
}

回答by ecoretchi

try this signal slot connection:

试试这个信号槽连接:

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

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

at slot implementation:

在插槽实现:

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

I have the same problem, but instead your problem ( always connected ), i have delay 4-5 seconds to receive disconnect signals, after unplugget ethernet wire.

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

Still looking solution, post answer if find.

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

回答by andrea.marangoni

try my template of client in Qt:

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

on cpp:

在 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
}

i omitted some code as constructor and slots connections.. try with this and if it doesn t work maybe there is something wrong on server side..

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