Qt: Обнаружение отключения QTcpSocket в консольном приложении, когда пользователь закрывает его

Моего заголовка вопроса должно быть достаточно. Я уже пробовал (безуспешно):

  1. Использование деструктора в стиле C в функции: __attribute__((destructor)):
    void sendToServerAtExit() __attribute__((destructor)) { mySocket->write("$%BYE_CODE%$"); }

Вызывается деструктор приложения, но сокет уже отключен и я не могу писать на сервер.

  1. Использую стандартную функцию C atexit(), но TCP-соединение уже потеряно, поэтому я не могу ничего отправить на сервер.
    atexit(sendToServerAtExit); // is the same function of point 1

Решение, которое я нашел, - проверять каждую секунду, все ли подключенные сокеты все еще подключены, но я не хочу делать это неэффективно. Это только временное решение. Кроме того, я хочу, чтобы другие приложения (даже веб-приложения) могли присоединиться к чату моего консольного приложения, и я не хочу запрашивать данные каждую секунду.

Что я должен делать?


person Community    schedule 08.11.2017    source источник
comment
покажи свой код.   -  person eyllanesc    schedule 08.11.2017


Ответы (3)


Если я правильно понимаю ваш вопрос, вы хотите отправить данные по TCP, чтобы уведомить удаленный компьютер о том, что вы закрываете сокет.

Технически это можно сделать в Qt, прослушивая сигналы QIODevice::aboutToClose() или QAbstractSocket::stateChanged().

Однако, если вы любезно выйдете из своей программы и закроете QTcpSocket, отправив пакет FIN на удаленный компьютер. Это означает, что на удаленном компьютере запущенная программа будет уведомлена о завершении TCP-соединения. Например, если удаленная программа также использует QTcpSocket, будет выдан сигнал QAbstractSocket::disconnected().

Настоящие проблемы возникают, когда одна из программ не завершается корректно (сбой, проблема с оборудованием, отсоединение кабеля и т. д.). В этом случае пакет TCP FIN не будет отправлен, и удаленный компьютер никогда не получит уведомление о том, что другая сторона TCP-соединения отключена. TCP-соединение прервется через несколько минут. Однако в этом случае вы также не можете отправить свой окончательный фрагмент данных на сервер.

В конце концов, единственное решение — время от времени посылать пакет «Я здесь». Несмотря на то, что вы утверждаете, что это неэффективно, это широко используемый метод, а также его преимущество в том, что он работает.

person Benjamin T    schedule 08.11.2017

Обработайте приведенный ниже сигнал (QTcpSocket унаследован от QAbstractSocket)

void QAbstractSocket::stateChanged(QAbstractSocket::SocketState socketState)

Внутри вызываемого слота проверьте, является ли socketState QAbstractSocket::ClosingState.

QAbstractSocket::ClosingState указывает на то, что сокет вот-вот закроется.

http://doc.qt.io/qt-5/qabstractsocket.html#SocketState-enum

person Pavan Chandaka    schedule 08.11.2017
comment
Или есть сигнал aboutToClose(), который в этом случае более прямолинеен. - person Benjamin T; 08.11.2017

Вы можете подключить слот к сигналу отключения.

connect(m_socket, &QTcpSocket::disconnected, this, &Class::clientDisconnected);

Ознакомьтесь с документацией.

Вы также можете узнать, какой пользователь был отключен, используя такой слот:

void Class::clientDisconnected
{
    QTcpSocket* client = qobject_cast<QTcpSocket*>(sender());
    if(client)
    {
        // Do something
        client->deleteLater();
    }
    else
    {
        // Handle error
    }
}

Этот метод полезен, если у вас есть пул соединений. Вы также можете использовать его, если у вас одно соединение, но не забывайте nullptr после client->deleteLater().

person albertTaberner    schedule 08.11.2017
comment
Вопрос в отправке данных до закрытия сокета. Сигнал disconnected() выдается слишком поздно. - person Benjamin T; 08.11.2017
comment
Затем вы должны реализовать механизм предварительного отключения со стороны вашего клиента. Даже состояние qtcpsocket о разъединении недействительно, так как клиент не будет прослушивать новые входящие байты. - person albertTaberner; 08.11.2017