Состояние QTcpSocket всегда подключено, даже отсоединение ethernet-провода

у меня есть QTcpSocket, и я читаю в цикл. Каждый раз, когда полный пакет был прочитан или произошла ошибка, Я вручную проверяю состояние сокета внутри цикла с помощью:

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

когда я выполняю программу de, после подключения и запуска цикла она всегда печатает qDebug()<<"Socket status: connected. Looking for packets..."; а затем stucks на waitForReadyRead пока некоторые данные готовы для чтения.

проблема в том, что разъединения не обнаружены. Если я отключусь от сети от параметров ОС, или даже если я отключу провод ethernet, он ведет себя так же: состояние сокета равно 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 ведет себя таким образом, и как я могу решить эту проблему?

EDIT: я создаю сокет в конструкторе класса, а затем инициализирую вызов функции 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 ответов


наконец-то нашел решение в этом Qt на форуме:

если данные не обмениваются в течение определенного времени, TCP начнет отправку сегменты keep-alive (в основном, сегменты ACK с подтверждением номер, установленный на текущий порядковый номер меньше единицы). Другие пиры затем отвечает другим подтверждением. Если это подтверждение не полученный внутри некоторое количество этапов зонда, соединение автоматически удаляется. Небольшое количество проблема в том, что ядро запускается отправка сегментов keep-alive через 2 часа с момента подключения становится праздным! Поэтому вам нужно изменить это значение (если ваша ОС позволяет это) или реализовать свой собственный механизм keep-alive в вашем протокол (как и многие протоколы, например 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));

Я сталкивался с аналогичными проблемами с клиентским приложением QT. В основном я справляюсь с таймерами, сигналами и слотами. Когда приложение запускается, оно запускает 4-секундный checkConnectionTimer. Каждые 4 секунды таймер истекает, если состояние сокета клиента != AbstractSocket:: Connected или подключение, он пытается подключиться к clientSocket - >connectToHost

когда сокет сигнализирует " connected ()", он запускает 5-секундный таймер сердцебиения сервера. Сервер должен отправить один байт heartbeat сообщение своим клиентам каждые 4 секунды. Когда я получаю сердцебиение (или любое сообщение, сигнализированное readyRead ()), я перезапускаю таймер сердцебиения. Поэтому, если таймер сердцебиения когда-либо имеет тайм-аут, я предполагаю, что соединение не работает, и оно вызывает clientSocket->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();
}

попробуйте это соединение слота сигнала:

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

при осуществлении шлица:

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

у меня такая же проблема, но вместо вашей проблемы ( всегда подключен ), у меня есть задержка 4-5 секунд для получения сигналов отключения, после отключения ethernet-провода.

все еще ищет решение, ответ на сообщение, если найти.


попробуйте мой шаблон клиента в 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
}

Я опустил некоторый код в качестве конструктора и соединений слотов.. попробуйте с этим, и если он не работает, возможно, что-то не так на стороне сервера..