QtcpSocket. Проблема с асинхронностью чтения данных |
Здравствуйте, гость ( Вход | Регистрация )
QtcpSocket. Проблема с асинхронностью чтения данных |
Andrewshkovskii |
14.12.2011, 23:33
Сообщение
#1
|
Активный участник Группа: Участник Сообщений: 351 Регистрация: 27.12.2008 Пользователь №: 467 Спасибо сказали: 18 раз(а) Репутация: 1 |
Друзья, в ниже изложенном алгоритме приема данных из сокета я наблюдаю проблему, которую не знаю как решить, может вы мне поможете?
Условие передачи данных по сокетам - %длина_передаваемых_данных%|%сериализированный_json% . Слот чтения данных соединен с сигналом readyRead. Обменивается сервер и клиент т.н. "Командами", сериализованный JSON с обязательным атрибутом 'command'. Приходит команда от сервероного сокета (на сервер сайде используются не qt-сокеты, а питоновские, из модуля socket). Почему-то, при приеме сравнительно большем объеме данных (100к+ символов) возникает следующая проблема: Но я не успеваю получить их полностью и в этот поток данных команды вливается, почему-то, ещё один поток данных, из следующей команды. Соответственно, при достижении нужного размера полученных данных я отправляю на обработку не валидный JSON. А происходит это так , допустим есть команда, содержащая 100к+ символов. ОТправляем сокету, и пока сокет её получает, отправляет ещё одну, и ещё одну. и всё это дело почему-то перемешивается. А недостающие куски потом прилетают. Как это может быть?Разве слоты обрабатываются асинхронного ? (софтина не многопоточная). Да и потом, протокол обеспечивает "правильную" последовательность данных, а тут как-то наоборот. Может я не верно слот с сигналом соединил? Использую следующий код :
|
|
|
Влад |
15.12.2011, 13:27
Сообщение
#2
|
Участник Группа: Участник Сообщений: 146 Регистрация: 20.3.2009 Из: Санкт-Петербург Пользователь №: 627 Спасибо сказали: 46 раз(а) Репутация: 8 |
Гм. Я правильно понял: у тебя И на серверной стороне, И на клиентской - работают гарантированно однопоточные программы?
Я сталкивался с подобным явлением при передаче по TCP соединению больших блоков данных (большинство блоков >64K, хотя некоторые блоки имели размер всего 1-2K) - на серверной стороне работал многопоточный софт, и в этом софте была ошибка, заключавшаяся в том, что никакой синхронизации между потоками при записи данных в один и тот же сокет программистом не было предусмотрено. В результате, при достижении некоторого уровня загрузки приложения, на клиент начинали бессистемно (иногда) приходить "битые" блоки данных, что рушило работу системы. Именно по описанному тобою сценарию - "предыдущие" и "последующие" куски данных перемешивались... |
|
|
Andrewshkovskii |
15.12.2011, 19:08
Сообщение
#3
|
Активный участник Группа: Участник Сообщений: 351 Регистрация: 27.12.2008 Пользователь №: 467 Спасибо сказали: 18 раз(а) Репутация: 1 |
Гарантированно однопоточные. Никак не могу понять, почему слот выполняет асинхронно, а не последовательно.
Даже не так - данные не приходят полностью, порядок следования данных нарушается. Сообщение отредактировал Andrewshkovskii - 15.12.2011, 19:08 |
|
|
ssoft |
16.12.2011, 6:42
Сообщение
#4
|
Участник Группа: Участник Сообщений: 130 Регистрация: 17.2.2010 Из: Москва Пользователь №: 1470 Спасибо сказали: 30 раз(а) Репутация: 3 |
Могу только пофантазировать пока на тему использования Qt.QueuedConnection, который вызывает слот через очередь а не на прямую, попробуй Qt.DirectConnection. Хотя даже используя Qt.QueuedConnection вызов слота должен быть последовательным.
|
|
|
Влад |
16.12.2011, 13:01
Сообщение
#5
|
Участник Группа: Участник Сообщений: 146 Регистрация: 20.3.2009 Из: Санкт-Петербург Пользователь №: 627 Спасибо сказали: 46 раз(а) Репутация: 8 |
Я думаю, что дело тут не в сокетах и не в TCP, а в банальной ошибке в алгоритме обработки прочитанного из сокета. Честно говоря, я впервые сталкиваюсь с Питоном (да!), но вот это место я написал бы иначе, чем у тебя:
Сообщение отредактировал Влад - 16.12.2011, 13:01 |
|
|
Andrewshkovskii |
17.12.2011, 12:19
Сообщение
#6
|
Активный участник Группа: Участник Сообщений: 351 Регистрация: 27.12.2008 Пользователь №: 467 Спасибо сказали: 18 раз(а) Репутация: 1 |
Если бы была ошибка в алгоритме, то он бы не работал при обычных условиях.приведенная Вами строчка — преобразование bytearray в строку. а как бы сделали Вы?
|
|
|
Влад |
19.12.2011, 10:15
Сообщение
#7
|
Участник Группа: Участник Сообщений: 146 Регистрация: 20.3.2009 Из: Санкт-Петербург Пользователь №: 627 Спасибо сказали: 46 раз(а) Репутация: 8 |
Ну, вот при обычных условиях - он и не работает :-)) Или ты хочешь сказать, что у тебя условия "необычные"?
Разумеется, я способен разобраться в том, что делает преобразование bytearray в строку. Мое сомнение вызвало то, что, насколько я понимаю, в строке data = str(self.readAll()) затирается старое содержимое data (если там что-то оставалось к моменту чтения!) новыми данными, прочитанными только что из сокета. Рассмотри такой сценарий работы (use case): пусть передатчик шлет такие сообщения в соответствии с твоим протоколом (где цифры - валидная длина, а буквы - валидное тело сообщения): 12345|abcdefgh 67890|ijklmnop на приемной стороне из сокета вынимаются данные и дергается on_ready_read: 12345|ab cdef gh67 8 9 0|ijklmnop Что будет в data после каждого приема данных и будет ли правильно работать твоя функция? Сообщение отредактировал Влад - 19.12.2011, 12:02 |
|
|
PAFOS |
20.12.2011, 8:26
Сообщение
#8
|
Активный участник Группа: Участник Сообщений: 258 Регистрация: 27.12.2010 Из: Дмитров Пользователь №: 2309 Спасибо сказали: 29 раз(а) Репутация: 8 |
может быть у переменной есть ограничение на максимальное кол-во символов?
|
|
|
Andrewshkovskii |
26.12.2011, 13:53
Сообщение
#9
|
Активный участник Группа: Участник Сообщений: 351 Регистрация: 27.12.2008 Пользователь №: 467 Спасибо сказали: 18 раз(а) Репутация: 1 |
Извините друзья, я лежал в больнице, не мог ответить,только вот приехал.
Ну, вот при обычных условиях - он и не работает :-)) Или ты хочешь сказать, что у тебя условия "необычные"? Разумеется, я способен разобраться в том, что делает преобразование bytearray в строку. Мое сомнение вызвало то, что, насколько я понимаю, в строке data = str(self.readAll()) затирается старое содержимое data (если там что-то оставалось к моменту чтения!) новыми данными, прочитанными только что из сокета. Рассмотри такой сценарий работы (use case): пусть передатчик шлет такие сообщения в соответствии с твоим протоколом (где цифры - валидная длина, а буквы - валидное тело сообщения): 12345|abcdefgh 67890|ijklmnop на приемной стороне из сокета вынимаются данные и дергается on_ready_read: 12345|ab cdef gh67 8 9 0|ijklmnop Что будет в data после каждого приема данных и будет ли правильно работать твоя функция? Естественно data затирается. только это ЛОКАЛЬНАЯ переменная. и для каждого вызова функции она будет своя,т.е. твое предположение верно только для tmp и wait_len , может "затереться" из-за асинхронного вызова. Так вот, если есть асинхронность вызова функции, или не верный порядок вызова - то весь алгоритм терпит крах. при иных условиях, исключающих вышесказанные все работает прекрасно. может быть у переменной есть ограничение на максимальное кол-во символов? Интересная мысль, но нет, проблема не в ограничении. Сообщение отредактировал Andrewshkovskii - 26.12.2011, 13:56 |
|
|
Andrewshkovskii |
27.12.2011, 11:23
Сообщение
#10
|
Активный участник Группа: Участник Сообщений: 351 Регистрация: 27.12.2008 Пользователь №: 467 Спасибо сказали: 18 раз(а) Репутация: 1 |
В общем есть идея отказаться от сигнальной системы для сокетов и попробовать переписать под блокирующие вызовы (waitFor..), сокеты эти должны жить в отдельном треде, только для каждого сокета должен быть свой тред.
|
|
|
Текстовая версия | Сейчас: 27.4.2024, 5:58 |