Уведомления

Группа в Telegram: @pythonsu

#1 Дек. 14, 2011 20:43:47

Andrewshkovskii
От:
Зарегистрирован: 2011-09-30
Сообщения: 6
Репутация: +  0  -
Профиль   Отправить e-mail  

Проблема с приемом данных из QTcpSocket.

Друзья, в ниже изложенном алгоритме приема данных из сокета я наблюдаю проблему, которую не знаю как решить, может вы мне поможете?
Условие передачи данных по сокетам - %длина_передаваемых_данных%|%сериализированный_json% .
Слот чтения данных соединен с сигналом readyRead. Обменивается сервер и клиент т.н. “Командами”, сериализованный JSON с обязательным атрибутом ‘command’.
Приходит команда от сервероного сокета (на сервер сайде используются не qt-сокеты, а питоновские, из модуля socket).
Почему-то, при приеме сравнительно большем объеме данных (100к+ символов) возникает следующая проблема:

Но я не успеваю получить их полностью и в этот поток данных команды вливается, почему-то, ещё один поток данных, из следующей команды. Соответственно, при достижении нужного размера полученных данных я отправляю на обработку не валидный JSON. А происходит это так , допустим есть команда, содержащая 100к+ символов. ОТправляем сокету, и пока сокет её получает, отправляет ещё одну, и ещё одну. и всё это дело почему-то перемешивается. А недостающие куски потом прилетают.
Как это может быть?Разве слоты обрабатываются асинхронного ? (софтина не многопоточная). Да и потом, протокол обеспечивает “правильную” последовательность данных, а тут как-то наоборот. Может я не верно слот с сигналом соединил? Использую следующий код :

# -*- coding: utf-8 -*-
from PyQt4.QtCore import QVariant, pyqtSignal, Qt
from PyQt4.QtNetwork import QTcpSocket
from re import match

MAX_WAIT_LEN = 8

class UpQSocket(QTcpSocket):
data_ready = pyqtSignal(unicode)
def __init__(self):
QTcpSocket.__init__(self)
self.wait_len = ''
self.temp = ''
self.setSocketOption(QTcpSocket.KeepAliveOption, QVariant(1))
self.readyRead.connect(self.on_ready_read, Qt.QueuedConnection)

def connectToHost(self, host, port):
self.temp = ''
self.wait_len = ''
QTcpSocket.connectToHost(self, host, port)

def close(self):
QTcpSocket.close(self)

def send(self, data):
self.writeData('%s|%s' % (len(data), data))

def on_ready_read(self):
if self.bytesAvailable():
data = str(self.readAll())
while data:
if not self.wait_len and '|' in data:#new data and new message
self.wait_len , data = data.split('|',1)
if match('[0-9]+', self.wait_len) and (len(self.wait_len) <= MAX_WAIT_LEN) and data.startswith('{'):#okay, this is normal length
self.wait_len = int(self.wait_len)
self.temp = data[:self.wait_len]
data = data[self.wait_len:]
else:#oh, it was crap
self.wait_len , self.temp = '',''
return
elif self.wait_len:#okay, not new message, appending
tl= int(self.wait_len)-len(self.temp)
self.temp+=data[:tl]
data=data[tl:]
elif not self.wait_len and not '|' in data:#crap
return
if self.wait_len and self.wait_len == len(self.temp):#okay, full message
self.data_ready.emit(self.temp)
self.wait_len ,self.temp = '',''
if not data:
return



Офлайн

#2 Дек. 14, 2011 20:48:12

s0rg
От:
Зарегистрирован: 2011-06-05
Сообщения: 777
Репутация: +  25  -
Профиль   Отправить e-mail  

Проблема с приемом данных из QTcpSocket.

Andrewshkovskii
Как это может быть?
http://en.wikipedia.org/wiki/Nagle%27s_algorithm

Офлайн

#3 Дек. 14, 2011 21:03:20

Andrewshkovskii
От:
Зарегистрирован: 2011-09-30
Сообщения: 6
Репутация: +  0  -
Профиль   Отправить e-mail  

Проблема с приемом данных из QTcpSocket.

Извините, я не совсем понял. У меня нет возможности поменять конвенцию передаваемых данных, т.е поменять код сервера возможности нет.



Офлайн

#4 Дек. 14, 2011 21:08:56

s0rg
От:
Зарегистрирован: 2011-06-05
Сообщения: 777
Репутация: +  25  -
Профиль   Отправить e-mail  

Проблема с приемом данных из QTcpSocket.

Статья по ссылке объясняет как такое может быть.
Как чинить - Гуглите про отключение этого алгоритма для QTcpSocket.

Офлайн

#5 Дек. 14, 2011 21:10:09

Andrewshkovskii
От:
Зарегистрирован: 2011-09-30
Сообщения: 6
Репутация: +  0  -
Профиль   Отправить e-mail  

Проблема с приемом данных из QTcpSocket.

Т.е. отключив оптимизацию сокета на стороне клиента(QAbstractSocket::LowDelayOption) - такого не должно быть?



Отредактировано (Дек. 14, 2011 21:10:42)

Офлайн

#6 Дек. 14, 2011 21:15:16

s0rg
От:
Зарегистрирован: 2011-06-05
Сообщения: 777
Репутация: +  25  -
Профиль   Отправить e-mail  

Проблема с приемом данных из QTcpSocket.

С QTcpSocket не работал, для обычных сокетов (через setsockopt) это помогало.

Офлайн

#7 Дек. 14, 2011 21:22:56

Andrewshkovskii
От:
Зарегистрирован: 2011-09-30
Сообщения: 6
Репутация: +  0  -
Профиль   Отправить e-mail  

Проблема с приемом данных из QTcpSocket.

Мне кажется тут проблема в асинхронном выполнение слота для сигнала readyRead.
Никак не могу понять - причем тут оптимизация передаваемых пакетов, отключив её на стороне клиента - я все равно буду принимать данные так, разве нет?
Пожалуйста, развейте мои заблуждения.
Сейчас нет возможности проверить - сервер не под нагрузкой, и воспроизвести проблему я не могу.



Офлайн

#8 Дек. 14, 2011 21:40:58

s0rg
От:
Зарегистрирован: 2011-06-05
Сообщения: 777
Репутация: +  25  -
Профиль   Отправить e-mail  

Проблема с приемом данных из QTcpSocket.

Асинхронность тут не при чем, по своему опыту могу сказать, что она, сама по себе, не может являтся причиной проблем.
У вас склеиваются данные - типичный пример работы алгорима Нагла.
Я вижу два выхода:
1) Отключить этот алгоритм для сокета.
2) Раз уж данные идут как ‘%длина_передаваемых_данных%|%сериализированный_json%’, то прежде чем скармливать полученный json дальше нужно пробежатся по нему и проверит нет ли ситуации где json закончился (символ - ‘}’) но после него есть еще данные. В таком случае ‘откусывать’ этот хвост во внутренний буффер и при поступлении следующей порции данных - склеивать обратно (то_что_откусили + новые_данные)

Отредактировано (Дек. 14, 2011 21:41:56)

Офлайн

#9 Дек. 14, 2011 21:47:54

Andrewshkovskii
От:
Зарегистрирован: 2011-09-30
Сообщения: 6
Репутация: +  0  -
Профиль   Отправить e-mail  

Проблема с приемом данных из QTcpSocket.

Да в том то и проблема, что формируемый json в этом случае до конца не формируется, а обрывается, и к формируемым данным приклеивается другая команда. получается что-то типа
%num%|{…%num%|{…%num%|{…} .
Вот как-то так http://pastebin.com/j8GrrdjB в пятой строке(по нумерации Pastebin) начало следующей команды, но она склеивается с предыдущей, т.к. преидущая не заканчивалась.



Отредактировано (Дек. 14, 2011 21:51:21)

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version