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

2021-12-09 00:00:00 qt state network-programming c++ qtcpsocket

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

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:

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

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

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 waitForReadyRead until some data is ready to be read.

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

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.

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

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

// 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.

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

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

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.

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

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

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

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

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

推荐答案

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

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

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:

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));

相关文章