web-dev-qa-db-fra.com

Etat QTcpSocket toujours connecté, même en débranchant le câble Ethernet

J'ai un QTcpSocket et je lis dans une boucle. Chaque fois qu'un paquet complet a été lu ou qu'il y a une erreur, je vérifie manuellement le statut de la socket dans la boucle, avec:

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

Lorsque j'exécute le programme, une fois connecté et que la boucle commence, elle affiche toujours qDebug()<<"Socket status: connected. Looking for packets..."; et stucks at waitForReadyRead jusqu'à ce que certaines données soient prêtes à être lues.

Le problème est que les déconnexions ne sont pas détectées. Si je me déconnecte du réseau depuis les options du système d'exploitation, ou même si je débranche le fil Ethernet, il se comporte de la même manière: l'état de la prise est égal à QAbstractSocket::ConnectedState, donc cela continue, mais sans rien recevoir bien sûr.

J'ai également essayé de détecter les déconnexions reliant le signal disconnected() (après la première connexion) à une fonction de reconnexion:

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

Mais le signal n'est jamais émis car ce code n'est jamais exécuté. Ce qui est logique, car il semble que l'état du socket est toujours ConnectedState.

Si je rebranche, la connexion est restaurée et commence à recevoir des données à nouveau, mais je souhaite détecter les déconnexions pour afficher "Déconnecté" sur l'interface graphique.

Pourquoi QTcpSocket se comporte-t-il de cette façon et comment puis-je résoudre ce problème?

EDIT: Je crée un socket au niveau du constructeur de la classe, puis j'initialise la fonction appelant 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;
}
24
Roman Rdgz

Enfin trouvé la solution dans ce forum Qt :

Si aucune donnée n'est échangée pendant un certain temps, TCP commencera à envoyer des segments Persistants (en gros, des segments ACK avec le numéro d'accusé de réception Défini sur le numéro de séquence actuel moins un). L'autre pair Répond ensuite par un autre accusé de réception. Si cet accusé de réception est Non reçu dans un certain nombre de segments de sonde, la connexion Est automatiquement interrompue. Le petit problème est que le noyau commence À envoyer des segments persistants au bout de 2 heures après le moment où la connexion Est devenue inactive! Par conséquent, vous devez modifier cette valeur (si votre système d'exploitation Le permet) ou implémenter votre propre mécanisme persistant dans votre protocole (comme le font de nombreux protocoles, par exemple SSH). Linux vous permet de Le changer en utilisant 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));
20
Roman Rdgz

J'ai rencontré des problèmes similaires avec une application client QT. En gros, je l’utilise avec des timers, des signaux et des slots. Lorsque l'application démarre, elle lance un checkConnectionTimer de 4 secondes. Toutes les 4 secondes, le minuteur expire. Si l'état du socket client! = AbstractSocket :: Connected ou Connecting, il tente de se connecter à clientSocket-> connectToHost.

Lorsque le socket signale "connected ()", il démarre un compteur de pulsations serveur de 5 secondes. Le serveur doit envoyer un message de pulsation d'un octet à ses clients toutes les 4 secondes. Lorsque je reçois la pulsation (ou tout type de message signalé par readyRead ()), je redémarre le minuteur de pulsation. Donc, si le compteur de pulsations a un délai d’attente, je suppose que la connexion est interrompue et qu’il appelle clientSocket->disconnectFromHost ();

Cela fonctionne très bien pour tous les différents types de déconnexions sur le serveur, gracieuses ou non (câble yanking). Oui, cela nécessite des types de pulsations personnalisés, mais au bout du compte, c’était la solution la plus rapide et la plus portable. 

Je n'avais pas envie de définir des délais d'expiration KEEPALIVE dans le noyau. De cette façon, il est plus portable. Dans le constructeur:

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();
}
10
Matt Brown

essayez cette connexion de slot de signal:

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

à la mise en place de la fente:

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

J'ai le même problème, mais à la place de votre problème (toujours connecté), j'ai un délai de 4 à 5 secondes pour recevoir les signaux de déconnexion, après avoir débranché le câble Ethernet.

Toujours à la recherche d'une solution, postez la réponse si vous trouvez.

1
ecoretchi

essayez mon template de client dans 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();
};

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

j’ai omis du code en tant que constructeur et connexions de slots.

0