Форум: "Прочее";
Текущий архив: 2007.11.18;
Скачать: [xml.tar.bz2];
ВнизТестирование программного обеспечения Найти похожие ветки
← →
31512 (2007-10-15 11:25) [0]Здравствуйте уважаемые. Обращаюсь за вашим опытом в тестировании и отладке ПО. Итак имеется некоторая программа. Многопоточная, использующая сокеты для обмена данными по локальной сети.
Требование к работе программы простое: стабильная работа всегда. Тесты показывают, что программа может проработать 10 - 20 часов без единого сбоя. Потом может свалиться с Access violation. А может не свалиться. А может свалиться через 2 часа. Или через час. Короче - ошибка эта нестабильна. В процессе отладок и тестирования было выявлено и устранено много сторонних дефектов, приведщих к ускорению работы, более оптимальному расходу памяти и другие. Но устранить нестабильный Access violation пока не удалось. Программа написана на Delphi 7 (upd 1), работает под WinXP SP1 со всеми обновлениями. Поиски в интернете не увенчались успехом. Интерисует: любой опыт, даже случайный, при работе с системами такого рода. Подробности при обсуждении. Спасибо.
← →
Anatoly Podgoretsky © (2007-10-15 11:49) [1]> 31512 (15.10.2007 11:25:00) [0]
В интернете по этой проблемы гигатонны информации, все они сводятся к одному - неверное значение указателя.
При выдаче данного сообщения выдается и сопутствующая информация.
← →
31512 (2007-10-15 11:51) [2]
> Anatoly Podgoretsky © (15.10.07 11:49) [1]
Нет работы с указателями.
← →
KSergey © (2007-10-15 11:53) [3]> 31512 (15.10.07 11:25)
> Подробности при обсуждении.
Это о чем??
А вообще - по описанию ошибки очевидно, что есть кривость дизайна. И где-то что-то когда-то в этих потоках не срастается со ссылками на созданные/уничтоженные объекты.
Изучайте архитектуру своего приложения и обмозговывайте различные ситуации.
Не забывайте учитывать при этом, что переключение между потоками выполняется в совершенно произвольный момент, а вовсе не тогда, когда вы об этом думаете.
← →
KSergey © (2007-10-15 11:54) [4]> 31512 (15.10.07 11:51) [2]
> Нет работы с указателями.
Указатель здесь - ссылка на объект.
Я не поверю, что вы в своей программе не создаете ни одного наследника TObject.
← →
31512 (2007-10-15 11:56) [5]
> KSergey © (15.10.07 11:53) [3]
Спасибо за совет. Про подробности я имел ввиду, что отвечу на вопросы, на которые смогу ответить, касательно устройства программы.
← →
31512 (2007-10-15 11:59) [6]
> KSergey © (15.10.07 11:54) [4]
Философия меня интерисует. Я имел ввиду явное использование pointer.
Что такое указатель я знаю. Спасибо.
← →
Сергей М. © (2007-10-15 12:23) [7]
> устранить нестабильный Access violation пока не удалось
Интересны подробности неудачных попыток - что, где, при каких условиях осуществлялось при этих попытках ..
← →
Anatoly Podgoretsky © (2007-10-15 12:26) [8]> 31512 (15.10.2007 11:51:02) [2]
Исключено, большинство АПИ функций работает с указателями.
Наверняка есть объекты, строки, динамические массивы, буферы.
← →
clickmaker © (2007-10-15 12:29) [9]
> 31512 (15.10.07 11:25)
логи ведутся?
← →
31512 (2007-10-15 13:09) [10]
> Сергей М. © (15.10.07 12:23) [7]
> Интересны подробности неудачных попыток - что, где, при
> каких условиях осуществлялось при этих попытках ..
Ок. Из последнего. Суть такая: программа общается через сокеты с серверами, установленными на других машинах. Она посылает серверам комманды, а обратно приходят ответы. Команда имеет формат: 1 байт код команды, остальное данные. Некоторые комманды могут не иметь данных, такие комманды пусть будут названы однобайтовыми. От сервера приходит ответ: 1 байт код ошибки (0 - всё хорошо, не 0 какая-то ошибка) - остальное данные. Для передачи комманды формируется TByteArray, куда и запихивается всё простым копированием.
Примеры:
Комманда для установки управляющего кода формируется функцией
function CreateCode(const Code : word) : TCommand;
begin
Result := CreateCommand($28, SizeOf(Code), "Установка кода");
Move(Code, Result.Buffer[0], Result.BufferLength);
end;
где
TCommand = packed record
Code : byte; //код комманды
Name : ShortString; //наименование комманды
Buffer : TByteArray;//данные
BufferLength : Word;//длина данных в байтах
end;
и
function CreateCommand(const ACode : Byte; const ABufferLength : Word; const CommandName : string = "") : TCommand;
begin
Result.Code := ACode;
Result.Name := CommandName;
Result.BufferLength := ABufferLength;
end;
при отсылке формируется пакетик:
function TConnection.SendCommand(Command: TCommand; Delay : integer = 0) : TAnswer;
var
Buffer : TByteArray;
i, Len : integer;
Value : byte;
begin
...
Len := Command.BufferLength + 1;
Buffer[0] := Command.Code;
if Command.BufferLength > 0 then
for i:=1 to Command.BufferLength do
begin
Value := Command.Buffer[i - 1];
Buffer[i] := Value;
end;
ClientSocket.SendBuf(Buffer, Len);
Result := GetAnswer(Command);
...
end;
где,
TAnswer = packed record
ErrorCode : byte; //код ошибки
Data : TByteArray;//данные
DataLength : SmallInt;//длина данных
end;
и
function TConnection.GetAnswer(const Command : TCommand): TAnswer;
var
Buffer : TByteArray;
BytesReceived : integer;
begin
FillChar(Result.Data, SizeOf(Result.Data), 0);
if FClientSocket.WaitForData(5000) then
begin
BytesReceived := FClientSocket.ReceiveBuf(Buffer, SizeOf(TByteArray));
if BytesReceived > 0 then
begin
Result.DataLength := BytesReceived - 1;
Result.ErrorCode := Buffer[0];
RaiseException(Result.ErrorCode, Command);//генерация исключения в зависимости от кода ошибки
try
CheckData(Command, Buffer, BytesReceived);//проверка на длину данных, внутри при необходимости генерация исключения
if Result.DataLength > 0 then Move(Buffer[1], Result.Data, Result.DataLength);
except on E : Exception do
begin
//запись в лог
end;
end
end else begin
//потеря связи. запись в лог
end;
end;
Выснилось, что иногда с серверов приходят неполные данные. Срабатывает CheckData. Обработали и эту ситуацию. Иногда падает на однобайтовых коммандах.
> clickmaker © (15.10.07 12:29) [9]
Ведутся
> Anatoly Podgoretsky © (15.10.07 12:26) [8]
В таком случае, извиняюсь, я неправильно понял и указатели используются.
← →
Сергей М. © (2007-10-15 13:19) [11]
> иногда с серверов приходят неполные данные
Абсолютно нормальная ситуация.
> Обработали и эту ситуацию
Показывай как ...
И всвязи с эти контрвоапрос - а начерта гонять по сети 32кб бестолковых данных (см. тип TByteArray), если из этих 32кб информацию несет, например, всего один байт (см., к примеру, BufferLength = 1 или DataLength = 1) ?
> SendBuf(Buffer, Len);
SendBuf - это функциональный метод.
В условиях неблокирующего режима и с учетом тех самых 32кб это крайне важно.
← →
31512 (2007-10-15 13:23) [12]Повторюсь ещё раз. Ошибка нестабильна. Нельзя предсказать в какой момент времени она произойдёт. Один и тот же участок (я имею ввиду именно пробленый, который я пока не выявил) кода может отработать 1000 раз без проблем, но n-ый раз свалиться. Могут ли быть подвохи со стороны операционной системы?
← →
31512 (2007-10-15 13:29) [13]
> Сергей М. © (15.10.07 13:19) [11]
Ясно. Обработка имеет такой вид:
procedure CheckData(const Command : TCommand; Buffer : TByteArray; BytesReceived : integer);
var
Size : integer;
begin
case Command.Code of
$21 : begin //состояние сепарации
Size := SizeOf(TSeparateState) + 1;
if BytesReceived <> Size then raise Exception.Create(Format("Несовпадение данных о сепарации! Ожидалось %d получено %d", [Size, BytesReceived]));
end;
$24 : begin //Контрольный спектр
Size := SizeOf(TControlSpectrumData) + 1;
if BytesReceived <> Size then raise Exception.Create(Format("Несовпадение данных о контрольном спектре! Ожидалось %d получено %d", [Size, BytesReceived]));
end;
$25: begin //Гистограмма
Size := SizeOf(TGistogramm) + 1;
if BytesReceived <> Size then raise Exception.Create(Format("Несовпадение данных о гистограмме! Ожидалось %d получено %d", [Size, BytesReceived]));
end;
$28 : begin//установка кода питателя
Size := 1;
if BytesReceived <> Size then raise Exception.Create(Format("Несовпадение данных при изменеии кода питателя! Ожидалось %d получено %d", [Size, BytesReceived]));
end;
end;
end;
Вот кстати это и помогло выявить, что вместо 801 байт по гистограмме пришёл всего 1 байт с кодом ошибки 0.
У меня встречный вопрос, если не затруднит
1. почему абсолютно нормальная ситуация.
2. Разве передаётся не Len байт при SendBuf(Buffer, Len); Сервер отрабатывает коррекно.
3.
> SendBuf - это функциональный метод.
> В условиях неблокирующего режима и с учетом тех самых 32кб
> это крайне важно.
Подробности не помешают. Особенно почему ВАЖНО!
← →
Сергей М. © (2007-10-15 13:30) [14]
> Могут ли быть подвохи со стороны операционной системы?
>
Не могут.
Проблема - в непонимании тобой работы транспорта, реализованного в классе TCustomWinSocket.
← →
31512 (2007-10-15 13:33) [15]
> Сергей М. © (15.10.07 13:19) [11]
И ещё. Я сейчас перейду к указателям и опубликую код. Может лучше будет?
← →
Сергей М. © (2007-10-15 13:35) [16]
> 1. почему абсолютно нормальная ситуация
Потому что используется поточный транспортный протокол.
> 2. Разве передаётся не Len байт при SendBuf(Buffer, Len);
Передается ровно столько, сколько вернул результат вызова этого функционального метода.
> Подробности не помешают
Справку уже проштудировал ?
← →
31512 (2007-10-15 13:36) [17]
> Сергей М. © (15.10.07 13:30) [14]
> Проблема - в непонимании тобой работы транспорта, реализованного
> в классе TCustomWinSocket
Скорее всего ты прав. Я сейчас перепишу немного сообветсвующие участки кода.
← →
Сергей М. © (2007-10-15 13:36) [18]
> перейду к указателям и опубликую код. Может лучше будет?
Во всяком случае не хуже)
← →
31512 (2007-10-15 13:37) [19]
> Сергей М. © (15.10.07 13:35) [16]
> Справку уже проштудировал ?
Да. И сделал выводы.
← →
Сергей М. © (2007-10-15 13:40) [20]
> 31512 (15.10.07 13:37) [19]
Тоже самое касается и ReceiveBuf.
Тот факт, что он вернул результат > 0, еще не говорит о том, что принято не иначе как SizeOf(TByteArray) ожидаемых тобой байт данных.
← →
31512 (2007-10-15 13:48) [21]
> Сергей М. © (15.10.07 13:40) [20]
Это я давно понял, поэтому у меня и написано
BytesReceived := FClientSocket.ReceiveBuf(Buffer, SizeOf(TByteArray));
SizeOf(TByteArray) стоит чтобы было.
Вот, кстати, нельзя-ли от него вообще как-нибудь избавиться?
← →
Сергей М. © (2007-10-15 13:54) [22]
> 31512 (15.10.07 13:48) [21]
> поэтому у меня и написано
Ты ожидаешь от сервера 32кб данных.
В рез-те вызова ReceiveBuf ты получил всего 8кб (> 0) данных.
Твои действия ?
← →
Сергей М. © (2007-10-15 13:55) [23]
> нельзя-ли от него вообще как-нибудь избавиться?
А зачем он вообще фигурирует в структуре ? Чем это оправдано ?
← →
Сергей М. © (2007-10-15 13:59) [24]
> Многопоточная, использующая сокеты для обмена данными по
> локальной сети.
В приведенном коде нет ни намека на его использование в контексте более чем одного потока.
← →
31512 (2007-10-15 13:59) [25]
> Сергей М. © (15.10.07 13:54) [22]
Генерирую исключение. То, что использовать SizeOf(TByteArray) некорректно я понял. В этом конкретном случае нужно
BytesReceived := FClientSocket.ReceiveBuf(Buffer, 801);
это стало ясно. Или я не прав?
← →
Сергей М. © (2007-10-15 14:04) [26]
> 31512 (15.10.07 13:59) [25]
>
>
> Генерирую исключение.
С какого перепугу ?
← →
31512 (2007-10-15 14:09) [27]
> Сергей М. © (15.10.07 14:04) [26]
Объясню. Итак, я жду от сервера 801 байт. Но получаю 1 байт. Первый байт в ответе всегда результат выполнения комманды. Если это не 0, то дальше и смотреть нечего - ошибка на сервере. Если оставшееся меньшее 800 байт - то исключение, поскольку должно быть 800 и никак не меньше. Или я тебя не так понял?
← →
31512 (2007-10-15 14:20) [28]
> Сергей М. ©
Примерный вид такое пересылки будет более корректен?
var
Tmp : TByteArray
Buf : Pointer;
begin
Tmp[0] := Command.Code;
Move(Command.Buffer[0], Tmp[1], Command.BufferLength);
Buf = AllocMem(Command.BufferLength + 1);
Move(Buf, Tmp, Command.BufferLength + 1);
ClientSocket.SendBuf(Buf, Command.BufferLength + 1);
end;
← →
Сергей М. © (2007-10-15 14:20) [29]
> Если оставшееся меньшее 800 байт - то исключение, поскольку
> должно быть 800 и никак не меньше
Если это предусматривает прикладной протокол, то да.
А вот транспортный протокол волен передавать эти самые 800 байт так как ему вздумается: хоть одним целум "куском", хоть произвольным количеством "кусков" произвольного размера.
Обращаясь к ReceiveBuf-методу ты работаешь на уровне именно на транспортном уровне, поэтому не следует полагаться, что за один вызов этого метода ты получишь сразу все ошидаемые тобой 800 байт.
← →
Сергей М. © (2007-10-15 14:23) [30]
> Примерный вид такое пересылки будет более корректен?
А это вообще полное безобразие)
До тех пор пока ты не определишься, зачем у тебя везде фигурирует тип TByteArray, ничего путного не получится.
← →
Ping (2007-10-15 15:28) [31]Команда имеет формат: 1 байт код команды, остальное данные.
Ты уверен, что 1 байта достаточно? Не жалей байтиков, сделай cardinal, на будущее.
И меняй протокол:Command - 4 байта - команда
Size - 4 байта - размер пакета
Data - Size байт - пакет данных
(как-то так, как минимум...)
И принимай Size байтов в пакете данных. Не больше, и не меньше.
← →
31512 (2007-10-16 07:09) [32]
> Ping (15.10.07 15:28) [31]
Самое смешное, что когда-то я предлагал сделать именно так. Предлагал указывать размеры пакета или покрайней мере размеры его блоков. Но программист, который писал сервер наотрез отказался так делать, мотивируя, что у него уже всё написано. Теперь вот есть необльшая морока.
← →
31512 (2007-10-16 07:14) [33]
> Сергей М. © (15.10.07 14:23) [30]
Согласен, безобразие... Теперь о том, зачем нужен TByteArray... Мне необходимо сначала сформировать комманду, т.е. склеить код и данные. Передавать я могу только всё сразу. С приёмом та же ситуация. На ранних этапах комманды вообще склеивались в кучу. Это я заборол настройками сокета. Соображаю дальше...
← →
Сергей М. © (2007-10-16 08:14) [34]
> Мне необходимо сначала сформировать комманду, т.е. склеить
> код и данные
И ради этого нужно использовать именно TByteArray ?
Мда ..
Ты осознаешь, что из передаваемых тобой при этом 32кб данных лишь 1кб несет полезную информационную нагрузку, а остальные 31кб бестолково "гуляют" по сети ?
> На ранних этапах комманды вообще склеивались в кучу. Это
> я заборол настройками сокета
Это какими же ?
Уж не TCP_NODELAY и SO_RCVBUF/SO_SNDBUF ли ?
← →
31512 (2007-10-16 09:08) [35]
> Сергей М. © (16.10.07 08:14) [34]
Вот значит... Отправку данных поправил. Отсылается ровно столько, сколько нужно. Ни байтом больше. Осталось разобраться с приёмом ответов от сервера. Быстрого решения пока не вижу.
← →
Сергей М. © (2007-10-16 09:28) [36]
> Отправку данных поправил. Отсылается ровно столько, сколько
> нужно. Ни байтом больше
Показывай код ..
← →
31512 (2007-10-16 09:40) [37]Ок. Только во загвоздка. Поскольку ничего переписывать нельзя (нет времени) там мягко говоря через одно место сделано, но за-то быстро. И тесты пока успешные.
function TConnection.SendCommand(Command: TCommand; Delay : integer = 0) : TAnswer;
var
PBuffer : Pointer; <--- вот
Buffer : TByteArray;
i, Len : integer;
F : TextFile;
Value : byte;
begin
if not Connected then
if not TryToConnect then raise ESeparatorConnectException.CreateFmt("При попытке отсылки комманды "%s""#13"cоединение с %s:%d установить не удалось", [Command.Name, IPAddress, Port]);
if Connected then
begin
try
Len := Command.BufferLength + 1;
PBuffer := AllocMem(Len);
Buffer[0] := Command.Code;
if Command.BufferLength > 0 then
for i:=1 to Command.BufferLength do
begin
Value := Command.Buffer^[i - 1];
Buffer[i] := Value;
end;
Move(Buffer, PBuffer^, Len);<--- вот
//FClientSocket.SendBuf(Buffer, Len);
FClientSocket.SendBuf(PBuffer^, Len);<--- вот
PBuffer := nil;
if Delay>0 then Sleep(Delay);
except on E : Exception do
begin
try
AssignFile(F, "errors\core_log.txt");
if FileExists("errors\core_log.txt") then Reset(F) else Rewrite(F);
Append(F);
WriteLn(F, Format("[%s] TConnection.SendCommand"#13" Комманда:"#13" Код: %d"#13" Наименование: %s"#13" Длина поля данных %d байт"#13"Ошибка: %s", [DateTimeToStr(Now), Command.Code, Command.Name, Command.BufferLength, E.Message]));
Flush(F);
finally
CloseFile(F);
end;
end;
end;
Result := GetAnswer(Command);
end;
end;
Я прекрасно отдаю себе отчёт в том что это неправильно, но по-моему, то что хотелось. Отсылается ровно столько сколько нужно. Оптимизация будет чуть позже.
← →
31512 (2007-10-16 09:42) [38]Теперь нужно правильно организовать приём данных. Нужен совет.
← →
Сергей М. © (2007-10-16 09:48) [39]
> Buffer : TByteArray;
И опять TByteArray !
Зачем ?????
> FClientSocket.SendBuf(PBuffer^, Len);<--- вот
Ты вообще [11] читал ?)
← →
31512 (2007-10-16 09:59) [40]
> Сергей М. © (16.10.07 09:48) [39]
Погоди. Во-первых Buffer - промежуточное явление.
Из него я копирую только то, что мне нужно (не весь массив) в PBuffer.
Len всегда нужная, полная длина пакета комманды. Под PBuffer всегда выделяется столько, сколько нужно. Обращаю внимание: по-хорошему нужно переписать эту часть заново. Но на это нет времени.
Скорее всего я просто не понимаю, что ты хочешь мне сказать. Разве внесённые изменения не обеспечивают передачу только того, что нужно?
Страницы: 1 2 вся ветка
Форум: "Прочее";
Текущий архив: 2007.11.18;
Скачать: [xml.tar.bz2];
Память: 0.58 MB
Время: 0.043 c