user1803300
user1803300

Reputation:

Exchanging data over sockets

When i send to client a string (tkString), client tells me (inside WMClientRecv) access violation when trying to rebuild TValue, why ? -_-" with other typed data it works well...

for example, Integer (tkEnum)

here's server code (sends invoke data to client)

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
  // copy method name to record
  lstrcpy(InvokeRec.Method, PChar(InvokableMethod));

  // copy arg array to record
  InvokeRec.ArgRawSize := MethodArg.DataSize;

  if (MethodArg.DataSize <> 0) then
  begin
    MethodArg.ExtractRawData(PByte(@InvokeRec.ArgRawData[0]));

    InvokeRec.ArgTypeInf := MethodArg.TypeInfo^;
  end; 

  // send record
  ServerSocket.Socket.Connections[idxSocket].SendBuf(PByte(@InvokeRec)^, SizeOf(InvokeRec));
end;

Client reads it as:

procedure TClientClass.ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket);
var
  Buffer: PByte;
  BufLen: Integer;

  InvokeRec: TInvokeRec;
begin
  BufLen := Socket.ReceiveLength;

  Memo1.Lines.Append(Format('%d: Received %d bytes', [Memo1.Lines.Count, BufLen]));

  // dump keep-alive from recv buffer
  if (BufLen = 64) then
  begin
    GetMem(Buffer, BufLen);
    try
      Socket.ReceiveBuf(Buffer^, BufLen);
    finally
      FreeMem(Buffer, BufLen);
    end;
    Exit;
  end;

  Socket.ReceiveBuf(PByte(@InvokeRec)^, BufLen);
  SendMessage(Handle, WM_ClientRecv, 0, LPARAM(@InvokeRec));
end;

Routine of WMClientRecv:

procedure TClientClass.WMClientRecv(var Msg: TMessage);
var
  // we send to server
  ReadRec: TSharedRec;

  // we recv from server
  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;

  InvokableSubclass.ExecMethod(InvokeRecPtr.Method, Value);
end;

And ExceMethod:

procedure TInvokableSubclass.ExecMethod(const vmName: string; const Arg0: TValue);
var
  LContext: TRttiContext;
begin
  //
  FSendMode := TextMode;

  //
  if (Arg0.DataSize = 0) then
  begin
    LContext.GetType(TInvokableSubclass).GetMethod(vmName).Invoke(Self, []);
    Exit;
  end;

  LContext.GetType(TInvokableSubclass).GetMethod(vmName).Invoke(Self, Arg0);
end;

Upvotes: 2

Views: 1529

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 596176

As I just commented on your other question, TTypeInfo is variable length and contains pointers to other data, so you can't transmit it as-is and have it be meaningful on the receiving end. You will have to transmit the TValue.TypeInfo.Name string and then you can use TRttiContext.FindType(Name).Handle on the receiving end to get the appropriate TTypeInfo to pass to TValue.Make().

Upvotes: 0

David Heffernan
David Heffernan

Reputation: 612954

You are trying to serialize a variable of type TTypeInfo. Take a look at its declaration:

TTypeInfo = record
  Kind: TTypeKind;
  Name: ShortString;
 {TypeData: TTypeData}
end;

Your code will serialize the first two fields, but will fail to serialize the hidden field that is part of the implementation detail. This is a rather special type. You cannot simply assign one TTypeInfo variable to another. You are meant to pass around PTypeInfo variables.

In fact, consider the code that sends the type information:

InvokeRec.ArgTypeInf := MethodArg.TypeInfo^;

This code is already broken because it is assigning to a TTypeInfo, which as I stated above cannot be done with simple assignment.

Now, you obviously cannot trivially serialize a PTypeInfo because that only has meaning in the address space of the process that owns it. And you cannot readily call GetTypeData() and serialize the PTypeData that is returned. That's a complex structure which also contains hard to serialize pointers.

I suspect the simplest approach will be to roll your own type information serialization mechanism. Perhaps it's enough to send the name of the type and then get the receiver to look it up using that name.

Upvotes: 4

Related Questions