Russian Qt Forum

Qt => Работа с сетью => Тема начата: meandnano от Май 01, 2010, 16:40



Название: TCP. отключение от сервера.
Отправлено: meandnano от Май 01, 2010, 16:40
здравствуйте. такой вопрос:
Есть клиент-сервеное приложение (TCP). При отключении клиента от сервера мне неоюходимо знать, какой именно клиент отключился. Если клиент уходит, посредством нажатия специально обученой кнопки, тогда никаких проблем - программа сначала отсылает серверу соответствующее сообщение. Но необходимо обработать уход клиента и при случайном разрыве.
Пытался соеденить disconnected() клиента cо слотом в котором делается QTcpSocket *X = (QTcpSocket*) sender(); - но так не прокатывает, при отключении сервер вылетает с сегфолтом.

Собственно нужно это за тем, чтобы удалить соответствующий элемент из контейнера с клиентами QVector<QTcpSocket*> и оповестить всех остальных клиентов.
Достаточно знать хотя бы socketDescriptor клиента.
Есть ли варианты?


Название: Re: TCP. отключение от сервера.
Отправлено: BRE от Май 01, 2010, 16:54
Есть ли варианты?
Покажи, что делается в слоте, который подключен к disconnected.
Думаю ты просто разрушаешь объект delete, если это так, то так делать нельзя. В общем покажи, что бы мы не гадали.  ;)


Название: Re: TCP. отключение от сервера.
Отправлено: garryHotDog от Май 01, 2010, 18:12
попробуй обрабатывать сигнал сокета
Цитировать
void QAbstractSocket::stateChanged ( QAbstractSocket::SocketState socketState )   [signal]
и при отключении сокета посылать новый сигнал!

например:
Код:

// регистрация типа
qRegisterMetaType<QAbstractSocket::SocketState>("QAbstractSocket::SocketState");
// изменение состояние сокета.
connect(&m_Socket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(slot_OnSocketChangeState(QAbstractSocket::SocketState)));
...
...
...
//------------------------------------------------------------
// изменение состояние сокета. [слот]
//------------------------------------------------------------
void CMyClass::slot_OnSocketChangeState(QAbstractSocket::SocketState state)
{
switch (state)
{
                case (QAbstractSocket::UnconnectedState):
case (QAbstractSocket::ClosingState):
{
                                // пошлем наш сигнал
                                emit Mysignal(socketID);
break;
}
        }
}



Название: Re: TCP. отключение от сервера.
Отправлено: meandnano от Май 01, 2010, 18:37
BRE

вот соединение
Код:
void cserver::newconn(){
    QTcpSocket *Client = cServ->nextPendingConnection();
    connect(Client,SIGNAL(readyRead()),SLOT(readMess()));
    connect(Client,SIGNAL(disconnected()),this,SLOT(disconn()));

еще была строка:
connect(Client,SIGNAL(disconnected()),Client,SLOT(deleteLater()));
но убрал по случаю соединения с disconn();

Код:
void cserver::disconn(){
    QTcpSocket *Client = (QTcpSocket*) sender();
    int i,j,SID=Client->socketDescriptor();

    /*.....*/

    int _sid = sockets.indexOf(Client);  //то самое удаление
    sockets.remove(_sid);                  //  из QVector

   /*...*/

    delete Client;
}
вот этот код не работает и еще к тому же надо удалить объект, пославший дисконнект. сработает ли
delete sender(); ?

garryHotDog
спасибо, сейчас буду пробовать


Название: Re: TCP. отключение от сервера.
Отправлено: BRE от Май 01, 2010, 18:51
Попробуй так:
Код
C++ (Qt)
void cserver::disconn(){
   QTcpSocket *Client = qobject_cast<QTcpSocket*>( sender() );
   if( !Client )
    return;
 
   int i,j,SID=Client->socketDescriptor();
 
   /*.....*/
 
   int _sid = sockets.indexOf(Client);  //то самое удаление
   sockets.remove(_sid);                  //  из QVector
 
  /*...*/
 
   Client->deleteLater();
}
 


Название: Re: TCP. отключение от сервера.
Отправлено: meandnano от Май 01, 2010, 19:02
Попробуй так:
Код
C++ (Qt)
void cserver::disconn(){
   QTcpSocket *Client = qobject_cast<QTcpSocket*>( sender() );
   if( !Client )
    return;
}
 


попробую

garryHotDog

компилятор ругается на регистрацию типа - заявляет
Цитировать
error: too few template-parameter-lists
хотя запись соответствует мануалу


Название: Re: TCP. отключение от сервера.
Отправлено: BRE от Май 01, 2010, 19:05
попробую

Главное все же это: :)
Код
C++ (Qt)
void cserver::disconn(){
...
   Client->deleteLater();
}
 


Название: Re: TCP. отключение от сервера.
Отправлено: meandnano от Май 01, 2010, 19:17
BRE
вести с фонтов:
в disconn() обоими способами:
1) QTcpSocket *Client = (QTcpSocket*) sender();
2) QTcpSocket *Client = qobject_cast<QTcpSocket*>( sender() );
и Client->deleteLater() на конце

все проходит без ошибок, НО не правильно - отладка показывает, что Client->socketDescriptor()=-1


Название: Re: TCP. отключение от сервера.
Отправлено: garryHotDog от Май 01, 2010, 19:38
у меня регистрация вынесена в конструктор класса!


Название: Re: TCP. отключение от сервера.
Отправлено: BRE от Май 01, 2010, 19:46
все проходит без ошибок, НО не правильно - отладка показывает, что Client->socketDescriptor()=-1
А что ты ожидаешь там увидеть, при приеме сигнала disconnected. Сокет уже закрыт.


Название: Re: TCP. отключение от сервера.
Отправлено: meandnano от Май 01, 2010, 20:13
А что ты ожидаешь там увидеть, при приеме сигнала disconnected. Сокет уже закрыт.

ну я предполагал, что так и будет, но вся соль то как раз в том, что мне надо socketID получить от уходящего клиента.

garryHotDog
сделал так:
Код
C++ (Qt)
void cserver::exiting( QAbstractSocket::SocketState state ){
   switch( state ){
   case ( QAbstractSocket::ClosingState ):{
        QTcpSocket *Client = ( QTcpSocket* ) sender();
        emit hasGone( Client->socketDescroptor() );
        break;
       }
   }
}
 

правильно ли? Client->socketDescroptor() возвращает -1


Название: Re: TCP. отключение от сервера.
Отправлено: garryHotDog от Май 01, 2010, 22:41
Код
C++ (Qt)
QTcpSocket *Client = ( QTcpSocket* ) sender();
 
ты здесь корректно получаешь указатель на Client ? попробуем добавить
Код
C++ (Qt)
if( Client )
{
  int id=Client->socketDescroptor();
}
else{}
 

а получить другие параметры от Client можно? например адрес или порт??


цитатка из ассиста
Цитировать
The socket descriptor is not available when QAbstractSocket is in UnconnectedState.


Название: Re: TCP. отключение от сервера.
Отправлено: meandnano от Май 01, 2010, 22:49
В if( Client ) заходит, но id = -1


Название: Re: TCP. отключение от сервера.
Отправлено: garryHotDog от Май 01, 2010, 22:50
Цитировать
а получить другие параметры от Client можно? например адрес или порт??


Название: Re: TCP. отключение от сервера.
Отправлено: meandnano от Май 01, 2010, 23:03
у меня все на socketID завязано.
загвоздка в отм, что у меня есть QVector<QTcpSocket*> - где все они лежат. сделано это было с целью отправлять сообщения всем или только некоторым, путем перебора элементов в векторе.


Название: Re: TCP. отключение от сервера.
Отправлено: garryHotDog от Май 01, 2010, 23:11
что то я не понял до конца!?

как я понимаю есть 2 варианта обработки подключения к серверу
   1. обрабатывать сигнал сервера newConnection() и получать указатель на сокет с помощью nextPendingConnection ()
   2. переписать виртуальную функцию сервера incomingConnection(int), тем самым будем иметь дескриптор сокета.

Лично я использую 2, так как вывожу обработки клиента в поток.....и хранить список подключенных можно в след виде:
Код
C++ (Qt)
QList<int>
, где int - дескриптор сокета.....это я к тому что ты можешь добавить слот к клиенту, который по отключению будет слать свой дескриптор серверу, а тот в свою очередь удалять его из списка (именно списка!!! - потому что добавление и удаление в нем быстрее чем в векторе)...вот как то так


Название: Re: TCP. отключение от сервера.
Отправлено: garryHotDog от Май 01, 2010, 23:37
только что накидал небольшую прогу - пробовал получить socketDesck после отключения сокета - выдавал -1....единственное что могу посоветовать хранить не дескриптор сокета ,а например ip+port:
Код
C++ (Qt)
struct SClientID
{
quint32 ip;
quint16 port
}
QList<SClientID> m_clientsList;
 

когда клиент отключается он посылает сигнал - disconnected(), лови этот сигнал у удаляй его данные из списка.


Название: Re: TCP. отключение от сервера.
Отправлено: meandnano от Май 02, 2010, 00:08
я использую вариант 1 (дескриптов сокеты и так получишь). вопрос, как реализовать рассылку сообщений все клиентам, имея только их ip+port.
я вот думаю, может как-то организовать таймер на клиенте, который будет каждые n секунд посылать сообщения серверу - но пока хз как это устроить.


Название: Re: TCP. отключение от сервера.
Отправлено: garryHotDog от Май 02, 2010, 09:41
если клиент будет слать сообщения серваку - это будет лишней нагрузкой на сеть!! Если тебя это устроит?? а как тебе вариант сделать на серваке таймер, который через n секунд будет проверять список(вектор) с клиентами и смотрел кто отключился? этот вариант менее ресурсоемкий....можешь сделать данную проверку когда клиент шлет сигнал disconnected()!

p.s. А чем тебя не устроил вариант ?? у меня он работает!
Цитировать
когда клиент отключается он посылает сигнал - disconnected(), лови этот сигнал у удаляй его данные из списка.


Название: Re: TCP. отключение от сервера.
Отправлено: meandnano от Май 02, 2010, 17:21
а как тебе вариант сделать на серваке таймер, который через n секунд будет проверять список(вектор) с клиентами и смотрел кто отключился? этот вариант менее ресурсоемкий....можешь сделать данную проверку когда клиент шлет сигнал disconnected()!

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


Название: Re: TCP. отключение от сервера.
Отправлено: garryHotDog от Май 02, 2010, 18:47
Цитировать
как проверить подключен ли клиент из моего списка (вектора) к моему серверу?
ты при подключении будешь добавлять в список данные о клиенте, а при его дисконнекте будешь удалять!!!


Название: Re: TCP. отключение от сервера.
Отправлено: meandnano от Май 02, 2010, 23:40
остановился на работе с айпишником вместо socketID. все работает. всем спасибо


Название: Re: TCP. отключение от сервера.
Отправлено: garryHotDog от Май 03, 2010, 00:00
для более быстрого поиска по списку клиентов - можешь использовать Qlist<quint64>, где в quint64(8 байт) можно запихнуть ip клиента(4 байта) и порт (2байта), оставшиеся 2 байта занулить, например

quint64: |109|064|056|045|064|035|000|000|
                0     1     2    3     4    5     6    7
            |<---  ip адреса  --->|<--  порт -->|
сформировать такое число не сложно, а поиск будет быстрее....ну это уже на любителя ;D


Название: Re: TCP. отключение от сервера.
Отправлено: meandnano от Май 03, 2010, 00:11
не пойму - у меня же сервер слушает один порт, через него все и подключаются. зачем хранить его для каждого клиента отдельно?


Название: Re: TCP. отключение от сервера.
Отправлено: garryHotDog от Май 03, 2010, 00:18
а ну да...что то я загнался совсем...это немного из других дебрей, тогда тебе хватит просто quint32


Название: Re: TCP. отключение от сервера.
Отправлено: SABROG от Май 03, 2010, 23:45
ну я предполагал, что так и будет, но вся соль то как раз в том, что мне надо socketID получить от уходящего клиента.

Его конечно можно задублировать в отдельное поле, но зачем это надо? У тебя должен быть один идентификатор - указатель на класс. Мертвые дескрипторы разве что для отладочных целей, в логи выводить, хотя какую дополнительную информацию может нести номер дескриптора для человека открывшего логи? Можно конечно использовать, чтобы сопоставлять с другими записями, типа вот в 10:00 он подключился, а в 10:02 он отключился, но тут любой порядковый номер сойдет (1, 2, 3, 4). Даже будет информативнее, так как сможешь узнать общее количество активных соединений к тому времени. Ну или просто выводить hex представление адреса в указателе на QTcpSocket.


Название: Re: TCP. отключение от сервера.
Отправлено: ритт от Май 11, 2010, 14:52
не понимаю зачем вообще нужен SID=Client->socketDescriptor()
у тебя есть адрес сокета - за время жизни сокета его адрес не изменится - вот и используй его в качестве ключа...