Russian Qt Forum
Ноябрь 06, 2024, 02:09 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: KeepAlive у TCP  (Прочитано 12740 раз)
Orfus
Гость
« : Декабрь 04, 2010, 15:44 »

Доброго времени суток читающим сие.
Вступление: Я реализую сервер и клиент общающиеся посредством протокола TCP.
На данный момент, клиент подключается к серверу, сервер его видит. Клиент разъединяется (происходит выключение клиентской программы) сервер это тоже видит.

Проблема же возникла в некорректном отключении, т.е. если кто-то выдернул сетевой кабель. Сигналов об этом "событии" не приходит. Возник вопрос, как же всё таки об этом событии узнать. Есть классическое решение посылать пакеты "я живой" и если за определённый интервал времени не пришло ни одного пакета ответа ( или пакета "я живой") считать противоположную сторону отключённой.

Однако, решение вроде бы есть в самом qt http://doc.qt.nokia.com/4.7/qabstractsocket.html#setSocketOption
Цитировать
void QAbstractSocket::setSocketOption ( QAbstractSocket::SocketOption option, const QVariant & value )
Устанавливает заданную опцию option равным значению, описываемым значением value.
Эта функция была введена в Qt 4.6.

---
enum QAbstractSocket::SocketOption

Данное перечисление представляет опции, которые могут быть установлены у сокета. Если требуется, они могут установить после получения сигнала connected() от сокета или после получения нового сокета от QTcpServer.
Константа   Значение   Описание
QAbstractSocket::LowDelayOption   0   Пытается оптимизировать сокет для уменьшения времени ожидания. Для TcpSocket будет установлена опция TCP_NODELAY и отключён алгоритм Nagle. Установите равной 1 для включения.
QAbstractSocket::KeepAliveOption   1   Установите равным 1 для включения опции сокета SO_KEEPALIVE

This enum was introduced or modified in Qt 4.6.

Но что-то либо я делаю не так, либо необходимо поднастроить.
Собственно кусок кода:
Код:
//Конструктор QTcpServer 
Net_TcpServer::Net_TcpServer()
{
    if(!this->listen(QHostAddress::Any,TCPServPort))
     {
        // обработка ошибок
     }
    connect(this,SIGNAL(newConnection()),this,SLOT(slotNewConnection()));
    qWarning("###################\nTCP SERVER STARTED!\n###################");
}
//////////////
//блок ответственный за новоподключившегося
void Net_TcpServer::slotNewConnection()
{
    QTcpSocket* pClientSocket = this->nextPendingConnection();
    pClientSocket->setSocketOption(QAbstractSocket::KeepAliveOption,1);//я включаю механизм Keep alive
    connect(pClientSocket,SIGNAL(disconnected()),this,SLOT(dis()));//сообщение в qwarning("") об отключившимся
    connect(pClientSocket,SIGNAL(disconnected()),pClientSocket,SLOT(deleteLater()));//прибить сокет
    connect(pClientSocket,SIGNAL(readyRead()),this,SLOT(slotReadClient()));//чтение сообщения

    qWarning("Client "+pClientSocket->peerAddress().toString().toAscii()+" connected.");
    qWarning("Client connected. socket opt ->>"+pClientSocket->socketOption(QAbstractSocket::KeepAliveOption).toString().toLocal8Bit());//тут я проверяю включен ли keep alive
}
Клиент подключается и я даже вижу "1" в socket opt, но при выдёргивании кабеля ничего не происходит. Грустный

У клиента всё ещё веселее:
Код:
//инициализация нити ответственной за сеть
void Net::run()
{
...
   TCPSocket = new QTcpSocket();
   connect(TCPSocket, SIGNAL(disconnected()),timerBr,SLOT(start()));
   connect(TCPSocket,SIGNAL(readyRead()),this,SLOT(TcpReadyRead()));
   connect(TCPSocket,SIGNAL(connected()),this,SLOT(TcpConnected()));
   connect(TCPSocket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(TcpError(QAbstractSocket::SocketError)));
...
   exec();//поддерживать нить "живой"
...
}

//по достижению некоторого события (приходит udp от учителя с ip), просходит следующее
void Net::TcpConToServ()
{
    TCPSocket->connectToHost(ServIP,TCPServPort);
}
//по сигналу подключен
void Net::TcpConnected()
{   
    TCPSocket->setSocketOption(QAbstractSocket::KeepAliveOption,1);
    qWarning("socket opt ->>"+TCPSocket->socketOption(QAbstractSocket::KeepAliveOption).toString().toLocal8Bit());//и это выводит мне ни "1", и не "0", а  "-255"
}

Собственно вопросы в том как донастроить у сервера, почему клиент выдаёт -255 и не проще ли самому руками слать пакеты "я живой" ?

Qt 4.7.0 пишу под WinXP. Тестировал на Win7 (на нём и было -255), на Win Vista, WinXP. Приложение разрабатывается только под локальную сеть.
 
Заранее благодарен за ответы)
Записан
BRE
Гость
« Ответ #1 : Декабрь 04, 2010, 16:04 »

Информация по SO_KEEPALIVE из книги Стивенса
Цитировать
Параметр сокета SO_KEEPALIVE
Когда параметр SO_KEEPALIVE установлен для сокета TCP и в течение 2 часов не происходит обмена данными по сокету в любом направлении, TCP автоматически посылает собеседнику проверочное сообщение (keepalive probe). Это сообщение - сегмент TCP, на который собеседник должен ответить. Далее события могут развиваться по одному из трех сценариев
1) Собеседник отвечает, присылая ожидаемый сегмент ACK. Приложение не получает уведомления(поскольку всё в порядке). TCP снова отправляет проверочное сообщение ещё через 2 часа отсутствия активности в этом соединении
2) Собеседник отвечает, присылая сегмент RST, который сообщает локальному TCP, что узел собседника вышел из строя и перегрузился. Ошибка сокета, требующая обработки, устанавливается равной ECONNRESET, и сокет закрывается
3) На проверочное сообщение не приходит ответ от собеседника.  Код TCP, происходящий от Беркли, отправляет 8 дополнительных проверочных сообщений с интервалом в 75 секунд, пытаясь выявить ошибку. TCP прекратит попытки, если ответа не последует в течение 11 минут и 15 секунд после отправки первого сообщения. Если на все проверочные TCP не приходит ответа, то ошибка сокета, требующая обработки, устанавливается в  ETIMEDOUT, и сокет закрывается. Если же сокет получает ошибку ICMP (Internet Control Message Protocol - протокол управляющих сообщений Интернета) в ответ на одно из проверочных сообщений, то возвращается одна из соответствующих ошибок (табл A.3 и A.4), но сокет также закрывается. Типичная ошибка ICMP в этом сценарии - Host unreachable (Узел недоступен) - указывает на то, что узел собеседника не вышел из строя, а только является недоступным. При этом ошибка, ожидающая обработки, устанавливается в EHOSTUNREACH
Двухчасовой интервал это значение по умолчанию, его можно изменить.
Наверное для своего протокола проще сделать свой ping/pong.
Записан
Orfus
Гость
« Ответ #2 : Декабрь 04, 2010, 18:54 »

А не подскажите ли как сменить интервал?
Записан
BRE
Гость
« Ответ #3 : Декабрь 04, 2010, 18:58 »

А не подскажите ли как сменить интервал?
Почитай про:
int setsockopt(int socket, int level, int option_name, const void *option_value, socklen_t option_len);
Записан
Orfus
Гость
« Ответ #4 : Декабрь 04, 2010, 19:17 »

Премного благодарен)
Записан
Orfus
Гость
« Ответ #5 : Декабрь 09, 2010, 00:16 »

Решение пришлось выкапывать из бОльших глубин Смеющийся
Код:
....
#include <winsock2.h> // необходимые функции под виндой
....
//[1] данный кусок must be. инклюда с ним попросту не нашел 8(
#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4)

struct tcp_keepalive {
u_long onoff;
u_long keepalivetime;
u_long keepaliveinterval;
};
//{1}
....
...
//[2] механизм обеспечения жизни TCP соединения.
// этот кусок уже внутри функции connected. Выполняется только при уже установленном подключении.
    DWORD dwError = 0L,dwBytes ;
    tcp_keepalive pClSock_tcpKeepalive={0}, sReturned = {0};
    pClSock_tcpKeepalive.onoff=1;//включить keepalive
    pClSock_tcpKeepalive.keepalivetime=1000;// каждую "1.000" секунду отсылать пакет
    pClSock_tcpKeepalive.keepaliveinterval=150;// Если не пришел ответ выслать через 1.5с повторно
    if (WSAIoctl(pClientSocket->socketDescriptor(), SIO_KEEPALIVE_VALS, &pClSock_tcpKeepalive,
    sizeof(pClSock_tcpKeepalive), &sReturned, sizeof(sReturned), &dwBytes,
    NULL, NULL) != 0)
    {
    dwError = WSAGetLastError() ;
    qWarning((char*)dwError);
    }
    //{2} механизм обеспечения жизни TCP соединения.
...

Дословно я часть не понял (возможно косяк с dwBytes), но эта штука работает. А моё первое правило если работает, не трожь  Смеющийся
кстати говоря, после [2] {2} я на всякий случай для себя решил вызвать:
Код:
...
    qWarning("Client connected. socket opt ->>"+ pClientSocket->socketOption(QAbstractSocket::KeepAliveOption).toString().toLocal8Bit());
...
К моему удивлению эта штука выдаёт 0. Назначение QAbstractSocket::KeepAliveOption мной непонято вообще. Зачем она нужна я не представляю.
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.099 секунд. Запросов: 20.