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

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: Клиент не успевает обрабатывать данные (слабый канал или железо)  (Прочитано 3341 раз)
Ground
Гость
« : Июль 31, 2011, 15:24 »

Добрый день!
Сегодня тестировал клиент-сервер: с ноутбуком (клиент) отошел достаточно далеко от роутера - клиент повис, загрузив при этом весь процессор. В ходе непродолжительного дебага выяснилось, что клиент не успевает обработать все данные, первая партия приходит на обработку - 100 байт, за это время прилетает 200 байт, потом 400 и.т.д. Аналогичная ситуация на слабом железе, но тут все понятнее.
Вопрос по первому явлению, почему так? Из-за слабого канала скачет скорость передачи, за какой-то промежуток времени прилетает сразу много байт - и клиент виснет, правильно я понимаю?
И второй вопрос - как такое явление можно подавить или хотя бы минимизировать его влияние, ведь должны же быть какие-то стандартные приемы?
Немного про сервер: с сервера клиентам отсылается стабильным потоком 2КБ/с. Причем, если накапливаются 2 партии по 2КБ, то первая партия уже не нужна.
Код отправки:
Код
C++ (Qt)
qint8 Server::slotSendData()
{
// Данные для отправки в сыром виде
QByteArray clientData = clientFiles[i].read();
// Вычисление размера отправляемых данных
qint16 sizeOfData = 2 * sizeof(qint16) + clientData.size();
// Формирование строки данных для отправки клиенту
data.clear();
// Байты 0 - 1: количество передаваемых байт
data.append(reinterpret_cast<char*>(&sizeOfData), sizeof(qint16));
// Байты 2 - 3: идентификатор элемента
data.append(reinterpret_cast<char*>(&i), sizeof(qint16));
// Данные из MMF
data.append(clientData);
 
tcpClients[i]->write(data);
 
return 0;
}

Код получения:
Код
C++ (Qt)
qint8 Client::slotReadyRead()
{
QByteArray data;
QByteArray clientData;
qint16 sizeOfData = 0;
qint16 id = 0;
 
data.clear();
// Считывание пришедших данных
while (clientSocket->bytesAvailable())
{
data.append(clientSocket->readAll());
}
 
while (true)
{
if (data.size() < (qint16)sizeof(qint16))
break;
 
// Получение количества отправленных байт (0 - 1 байты)
memcpy(&sizeOfData, data.data(), sizeof(qint16));
 
if (data.size() < sizeOfData)
break;
 
// Получение id элемента (2 - 3 байты)
memcpy(&id, data.data() + sizeof(qint16), sizeof(qint16));
// Данные
clientData.clear();
clientData = data.mid(2 * sizeof(qint16), sizeOfData - 2 * sizeof(qint16));
 
// Передача данных элементу
emit signalSendDataToElement(id, clientData);
 
data = data.mid(sizeOfData);
}
 
return 0;
}

Вот на втором цикле при приемке данных и зависает программа.

Чуть не забыл! Отдельно хотелось бы спросить про первый цикл при приеме данных (увидел данную идею у М. Шлее), насколько он корректен и нужен ли вообще? Сам ставлю его под сомнение.
« Последнее редактирование: Июль 31, 2011, 15:26 от Ground » Записан
LisandreL
Птица говорун
*****
Offline Offline

Сообщений: 984


Надо улыбаться


Просмотр профиля
« Ответ #1 : Июль 31, 2011, 16:06 »

В читателе:

1)
Код
C++ (Qt)
while (clientSocket->bytesAvailable())
{
data.append(clientSocket->readAll());
}
Заменить на просто:
Код:
data.append(clientSocket->readAll());
О следующих порциях данных всё равно readReady() придёт.

2) Первый if можно в while внести инвертировав условие ( просто для красоты ).

3) Где оно у вас на втором цикле виснет? В любом случае data должен рано или поздно кончиться.
Записан
Ground
Гость
« Ответ #2 : Август 01, 2011, 09:34 »

Код
C++ (Qt)
while (clientSocket->bytesAvailable())
{
data.append(clientSocket->readAll());
}
Заменить на просто:
Код:
data.append(clientSocket->readAll());
О следующих порциях данных всё равно readReady() придёт.

Спасибо за наводку. Такую элементарную вещь выпустил из головы. Проблема была в том, что при каждом вызове Client::slotReadyRead() переменная data, хранящая принятые данные, создавалась заново. В результате, пока соединение работает стабильно - от сервера приходит по одной целой группе данных за раз. Как только соединение становится хуже - количество принятых за раз байт прыгает, в результате байты парсятся с ошибками и вызывают segmentation fault. Решил проблему, сделав data статической.

Но тогда встает ребром другая проблема. Наблюдал следующую ситуацию: сервер отправляет данные клиенту, клиент кое-как их принимает, но в один прекрасный момент соединение на секунду прерывается. После этого обрыва сервер все так же отправляет данные клиенту (без write error), но клиент ничего не принимает и думает, что сервер просто ничего не отсылает. Через несколько минут на стороне сервера появляется ошибка write error, соединение разрывается. Клиент теряет связь с сервером примерно где-то в этом же промежутке времени. Какими методами можно справиться с такой проблемой? Т.е. либо переподключать клиента (но не через 2 минуты, а побыстрее), либо как-то возобновлять текущее соединение? Пока что в мыслях два варианта - отправка подтверждения доставки данных, и второе - широковещательный запрос или что-то в этом духе. Может быть есть какие-нибудь другие способы, а если нет - какой из этих лучше?
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #3 : Август 01, 2011, 11:33 »

ТС, почитай ка уже теорию: про модель ISO/OSI + возьми и посмотри для примера спецификацию на какой-нить протокол передачи данных (например X-Modem - как один из самых простых), а потом уже приступай к реализации своего велосипеда.

PS: Замучали уже с этими вопросами. Понаделают хрени, а потом спрашивают почему не работает...
Записан

ArchLinux x86_64 / Win10 64 bit
Ground
Гость
« Ответ #4 : Август 01, 2011, 12:08 »

ТС, почитай ка уже теорию: про модель ISO/OSI + возьми и посмотри для примера спецификацию на какой-нить протокол передачи данных (например X-Modem - как один из самых простых), а потом уже приступай к реализации своего велосипеда.

PS: Замучали уже с этими вопросами. Понаделают хрени, а потом спрашивают почему не работает...

Так ведь в том-то и проблема - откуда мы (велосипедостроители) знаем что читать? В обычных книжках такого не пишут, в примерах Qt - тоже такого не найдешь. Только в узкоспециализированной литературе, но опять же вопрос - какой? Ей богу, никто ж не просит отвечать на вопрос, но можно хотя бы написать - rtfm x-modem и iso/osi, даже за это огромное спасибо. И замечу, все с чего-то начинали
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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