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

Rom*_*dgz 25 c++ qt state network-programming qtcpsocket

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

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

当我执行de program时,一旦连接并且循环开始,它总是打印qDebug()<<"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();
}
Run Code Online (Sandbox Code Playgroud)

但是信号永远不会被发出,因为这段代码永远不会被执行.这是合乎逻辑的,因为看起来套接字状态总是如此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;
}
Run Code Online (Sandbox Code Playgroud)

Rom*_*dgz 20

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

如果在一定时间内没有交换数据,TCP将开始发送保持活动段(基本上,确认号设置为当前序列号减去1的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));
Run Code Online (Sandbox Code Playgroud)

  • 对于其他尝试此操作的人:`#include <sys/socket.h> #include <netinet/in.h> #include <netinet/tcp.h> (8认同)
  • 创建 QTcpSocket 之后,这段代码会立即执行吗?您是否必须调用另一个函数来在 Qt 套接字上设置这些选项(或者它们是否立即在 Qt 套接字上生效)。 (2认同)

Mat*_*own 11

我一直面临着QT客户端应用程序的类似问题.基本上我用定时器,信号和插槽处理它.当应用程序启动时,它会启动4秒checkConnectionTimer.定时器到期每4秒,如果客户端套接字状态!= AbstractSocket :: Connected或Connecting,它尝试连接clientSocket-> connectToHost

当套接字信号"connected()"时,它启动一个5秒的服务器心跳计时器.服务器应每4秒向其客户端发送一个单字节心跳消息.当我获得心跳(或由readyRead()发出的任何类型的消息)时,我重新启动心跳计时器.因此,如果心跳计时器有超时,我认为连接已关闭并且它会调用clientSocket->disconnectFromHost ();

这适用于服务器上所有不同类型的断开连接,优雅或其他(yanking电缆).是的,它需要自定义心跳类型的东西,但在一天结束时它是最快,最便携的解决方案.

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

  • 我尝试了页面上的所有答案。这是唯一对我有用的。 (3认同)