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

时间:2012-05-04 08:14:15

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

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

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

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

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

我还尝试检测连接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会以这种方式运行,我该如何解决这个问题?

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

4 个答案:

答案 0 :(得分:20)

最后在Qt forum中找到了解决方案:

  

如果在一段时间内没有交换数据,TCP将开始发送   保持活动段(基本上是带有确认的ACK段)   数字设置为当前序列号减去1)。另一个同行   然后回复另一个确认。如果这个确认是   未在一定数量的探测段内接收到连接   自动删除。小问题是内核启动了   连接后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));

答案 1 :(得分:10)

我一直面临着QT客户端应用程序的类似问题。基本上我用定时器,信号和插槽处理它。当应用程序启动时,它会启动4秒checkConnectionTimer。定时器到期每4秒,如果客户端套接字状态!= AbstractSocket ::已连接或正在连接,则尝试连接clientSocket-&gt; 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();
}

答案 2 :(得分:1)

尝试此信号插槽连接:

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

在插槽实施时:

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

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

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

答案 3 :(得分:0)

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

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
}

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