Подтвердить что ты не робот

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

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

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

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

Проблема заключается в том, что разъединения не обнаружены. Если я отключусь от сети из опций ОС, или даже если я отключу провод Ethernet, он будет вести себя одинаково: состояние Socket равно 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.

Если я снова подключаюсь, соединение восстанавливается и снова начинает получать данные, но я хочу обнаружить отключения, чтобы показать "Disconnected" в графическом интерфейсе.

Почему 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;
}
4b9b3361

Ответ 1

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

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

Ответ 2

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

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

Ответ 3

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

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

в реализации слота:

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

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

Все еще выглядящее решение, ответ на сообщение, если найдете.

Ответ 4

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

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