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

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: Работа с QTcpSocket  (Прочитано 23455 раз)
Ruzzz
Гость
« : Ноябрь 02, 2009, 19:12 »

Как начинающий Qt'шник решил разобраться с QTcpSocket и привести в порядок мысли. Первым делом изучил класс QTcpSocket в Help'е. Затем изучил примеры Fortune Server и Threaded Fortune Server. Ну и решил сделать некий график для себя. Делюсь - скачать файл для XMind или в виде картинки:


Немного комментариев и за одно вопросов.

Работая с QTcpServer у нас есть две возможности обрабатывать входящие соединения:

  • Переопределить incomingConnection
  • Или ловить сигнал newConnection

После выполнения listen, как я понимаю где-то в недрах QtNetwork выполняется QTcpServer::waitForNewConnection, из которой идет вызов QTcpServerPrivate::readNotification, в теле этой функции я и нашел такие вызовы:
Код:
QTcpServer::incomingConnection(int socketDescriptor);
emit newConnection();

То есть здесь и происходит вызов incomingConnection, а затем посылка сигнала newConnection. Что говорит нам, что эти способы не взаимоисключающие, и мы можем использовать их оба для своих нужд, мало ли.

Теперь хотел бы рассмотреть код QTcpServer::incomingConnection(int socketDescriptor):
Код:
QTcpSocket *socket = new QTcpSocket(this);
socket->setSocketDescriptor(socketDescriptor);
d_func()->pendingConnections.append(socket);

Динамически в куче создается новый экземпляр сокета, связывается с входящим подключением и сохраняется в списке. Важно что QTcpSocket создается в основном потоке! Поэтому если мы не переопределяем incomingConnection и хотим использовать сокет не в главном потоке, то не забываем делать: setParent(0) и потом QObject->moveToThread() и не забываем что теперь за удаление отвечаем мы. Интересно что добавляемый в список (в третьей строке) сокет, удаляется оттуда QTcpSocket *QTcpServer::nextPendingConnection().

Теперь рассмотрим способ с сигналом newConnection.

Создаем наследник QTcpServer, например myTcpServer. В его конструкторе связываем сигнал с нашим слотом строкой connect(myTcpServer, SIGNAL(newConnection()), myTcpServer, SLOT(connectionProcess()));. Далее в слоте имеет примерно такой код:
Код:
QTcpSocket *clientConnection = myTcpServer->nextPendingConnection();
connect(clientConnection, SIGNAL(disconnected()), clientConnection, SLOT(deleteLater()));
clientConnection->read(…);
clientConnection->write(…);
clientConnection->disconnectFromHost();

Первая строка извлекает раннее сохраненный в списке объект QTcpSocket. Важно, что он сохранялся в incomingConnection, поэтому если мы ее будем переопределять, то использовать nextPendingConnection нельзя. Далее вторая строка, говорит что при дисконнекте мы поставим объект сокета в очередь на удаление. Если мы создали сокет динамически где-то раньше, то после того как сокет соединился, это верный способ для его удаления. В конце мы даем команду оборвать соединение.

Далее способ с переопределением incomingConnection

В примере Threaded Fortune Server в теле функции создается новый поток, и уже в нем идет работа с сокетом, отвечающим за соединение. Но как я понимаю никто не мешает нам выполнять обработку прям здесь Улыбающийся. Но тут вопрос, будут ли проблемы с большим кол-вом соединений? Ведь мы не сохраняем их в список(очередь). Хотя по моему создание отдельных потоков, на одно процессорной машине не то что не увеличит скорость, но наоборот — уменьшить ее. Далее. Здесь используется setSocketDescriptor, который связывает объект сокета с дескриптором который получен ранее от системы. Ну а далее все как обычно. Еще стоит обратить внимание на waitForDisconnected, она заставляет нас не выходить быстро из функции, иначе будет удален tcpSocket, потому что мы объявили его не динамически. А если он будет удален, вполне возможно сокет не сможет корректно «отработать». Также если вы работаете с сокетом в отдельном потоке, waitForDisconnected заставляет не выходить из рабочей функции потока, и тем самым поток не «умирает», а он нужен, так как сокет использует его очередь сообщений, без которой он не сможет корректно разъединиться.

Везде в QTcpSocket используется асинхронный режим (без потока), кроме waitForNewConnection.

Мастера, прошу указать на ошибки!

UPD Спасибо BRE!

UPD http://www.xmind.net/share/ruzzzua/work-with-qtcpsocket/ — может кому пригодится
« Последнее редактирование: Март 04, 2010, 13:45 от Ruzzz » Записан
BRE
Гость
« Ответ #1 : Ноябрь 02, 2009, 19:41 »

Поэтому способ не переопределяющий incomingConnection не подходит для многопоточных приложений.
Мы также не можем использовать QObject->moveToThread(), потому что этот сокет имеет родителя (передается this).
Если сокету сделать setParent( 0 ), то его можно перенести в "клиентский" поток.
Главное, не забывать, что теперь мы за него отвечаем и при завершение потока его убить.
Записан
BRE
Гость
« Ответ #2 : Ноябрь 02, 2009, 19:46 »

Где-то на форуме читал что deleteLater не совсем удаляет, так ли это? Почему же в официальном примере не боятся этого? Улыбающийся
Совсем удаляет, только чуть позже, точнее при очередном возвращении в eventloop.

Как я понимаю для Windows используются асинхронные функции для работы с сокетами? Или используется какой-то дополнительный поток?
Везде используется асинхронный режим (без потока).
Записан
Ruzzz
Гость
« Ответ #3 : Ноябрь 02, 2009, 20:27 »

BRE, правильно ли я понимаю, что в слоте, принимающем сигнал newConnection, строка  connect(clientConnection, SIGNAL(disconnected()), clientConnection, SLOT(deleteLater())); не обязательна, потому что в QTcpServer::incomingConnection(int socketDescriptor) в строке QTcpSocket *socket = new QTcpSocket(this); конструктору QTcpSocket передается this? Я думаю что да, но лучше спросить Улыбающийся
Записан
BRE
Гость
« Ответ #4 : Ноябрь 02, 2009, 20:31 »

BRE, правильно ли я понимаю, что в слоте, принимающем сигнал newConnection, строка  connect(clientConnection, SIGNAL(disconnected()), clientConnection, SLOT(deleteLater())); не обязательна, потому что в QTcpServer::incomingConnection(int socketDescriptor) в строке QTcpSocket *socket = new QTcpSocket(this); конструктору QTcpSocket передается this? Я думаю что да, но лучше спросить Улыбающийся
Это строка не обязательна.
Недавно обсуждали подобную тему: http://www.prog.org.ru/topic_11091_0.html
Записан
brucemax
Гость
« Ответ #5 : Декабрь 14, 2012, 17:18 »

А если сокет методом moveToThread кинуть в клиентский поток, то его ранее подключенные слоты будут выполняться в новом потоке?
Записан
fantom
Гость
« Ответ #6 : Декабрь 18, 2012, 16:05 »

Можно я вклинюсь немного, а то название темы подходящее))
Есть свой клиент — сервер.  Проблема состоит в следующем. Есть класс  tNet ( потомок от Qthread ) в котором написана реализация работы с сетью.  Если запустить его в главном классе MainWindow, то все работает.  Но по логике постоения программы, введен дополнительный класс — потомок от Qobject, в котором запускается tNet. Этот класс запускается в MainWindow. При таком построении программы, соединения с сервером не происходит, выдавая ошибку -
Connect Error: The host was not found.
Собственно почему так происходит, непонятно )
Записан
mutineer
Гость
« Ответ #7 : Декабрь 18, 2012, 16:07 »

Создавай тему, показывай код
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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