Ошибки доступа при работе со строками в Delphi: восстановление значений TValue через soket
При работе с сетевыми коммуникациями в Delphi, разработчики часто сталкиваются с ошибками доступа, особенно при передаче и восстановлении данных, таких как строки и пользовательские типы. В данной статье мы рассмотрим проблему, связанную с ошибками доступа при работе со строками в контексте передачи данных через soket, а именно при восстановлении значений TValue на стороне клиента.
Проблема
Разработчик столкнулся с ошибкой доступа при работе с строковыми данными (TkString), которые передаются на клиентскую сторону. При попытке восстановления TValue в обработчике WMClientRecv возникает ошибка доступа. В то же время, передача данных других типов, например, целых чисел (tkEnum), проходит успешно.
Пример кода сервера
Сервер использует следующий метод для отправки данных клиенту:
procedure TServerClass.InvokeMethod(const InvokableMethod: TInvokeableMethod; const MethodArg: TValue);
var
InvokeRec: packed record
Method: Array[0..19] of Char;
ArgRawSize: Integer;
ArgTypeInf: TTypeInfo;
ArgRawData: Array[0..255] of Byte;
end;
begin
// Копирование имени метода в запись
lstrcpy(InvokeRec.Method, PChar(InvokableMethod));
// Копирование аргумента в запись
InvokeRec.ArgRawSize := MethodArg.DataSize;
// и так далее...
// Отправка записи
ServerSocket.Socket.Connections[idxSocket].SendBuf(PByte(@InvokeRec)^, SizeOf(InvokeRec));
end;
Пример кода клиента
Клиент читает данные следующим образом:
procedure TClientClass.ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket);
var
Buffer: PByte;
BufLen: Integer;
InvokeRec: TInvokeRec;
begin
BufLen := Socket.ReceiveLength;
// и так далее...
Socket.ReceiveBuf(PByte(@InvokeRec)^, BufLen);
SendMessage(Handle, WM_ClientRecv, 0, LPARAM(@InvokeRec));
end;
Обработчик WMClientRecv восстанавливает значение TValue:
procedure TClientClass.WMClientRecv(var Msg: TMessage);
var
InvokeRecPtr: PInvokeRec;
Value: TValue;
begin
InvokeRecPtr := PInvokeRec(Msg.LParam);
case InvokeRecPtr.ArgRawSize of
0:
Value.Empty;
else
TValue.Make(PByte(@InvokeRecPtr.ArgRawData)^, PTypeInfo(@InvokeRecPtr.ArgTypeInf), Value);
end;
// и так далее...
end;
Анализ проблемы
При передаче строковых данных возникает проблема из-за особенностей структуры TTypeInfo, которая содержит скрытые поля и не может быть сериализована напрямую. Попытка сериализации TTypeInfo приводит к ошибке доступа, так как не все поля структуры учитываются в процессе копирования.
Подтвержденный ответ
Для решения проблемы необходимо изменить подход к сериализации данных. Вместо передачи TTypeInfo следует передавать только имя типа и использовать его для восстановления TTypeInfo на стороне клиента. Пример кода клиента с использованием этого подхода:
procedure TClientClass.WMClientRecv(var Msg: TMessage);
var
InvokeRecPtr: PInvokeRec;
Value: TValue;
LContext: TRttiContext;
begin
InvokeRecPtr := PInvokeRec(Msg.LParam);
case InvokeRecPtr.ArgRawSize of
0:
Value.Empty;
else
Value := TValue.Make(PByte(@InvokeRecPtr.ArgRawData)^, LContext.FindType(InvokeRecPtr.ArgTypeInf).Handle);
end;
// и так далее...
end;
Альтернативные решения
В качестве альтернативы можно рассмотреть использование протоколов, таких как COM или RPC, которые предоставляют готовые решения для передачи данных между процессами. Также возможно использование простых записей TLV (type, length, value) для упрощения процесса сериализации данных.
Заключение
При работе с сетевыми коммуникациями важно понимать особенности сериализации данных и корректно обрабатывать различные типы данных, включая строки. В данном случае, изменение подхода к сериализации TTypeInfo позволяет избежать ошибок доступа при работе со строками в Delphi.
Проблема связана с ошибками доступа при работе со строками в Delphi, вызванными некорректной сериализацией `TTypeInfo` при передаче данных через soket.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.