I have a simple mobile application written in Delphi XE8 that allows the user to take a picture and then send the picture to a server using Indy TCPClient/TCP Server.
I have scoured the forums and found numerous examples to send the data in a variety of ways. Every method I try results in an access violation or corrupt data on the server side.
My ultimate goal is to send a record containing a unique identifier, description and a picture(bitmap) from the client to the server. But I'm starting out be trying to simply send a record with some text from a windows client to the server. I will then try to implement the solution into my mobile app.
type
TSendRec = record
// SONo: string;
Text: string;
// Bitmap: TBitMap;
end
I have tried the following 3 methods as per the code below:
Send Using a Stream
Send using RawToBytes and TIDBytes
.
Send a line of text using Writeln
and Readln
When I try to send using a stream I get the following access violation:
Project memorystream_server.exe raised the exception class $C0000005 with message 'access violation at 0x00409e46: write of address 0x0065d1bc
The error occurs when I try to access the value of MiRec.Text
on the server side.
Memo1.Lines.Add(MiRec.Text);
So I assume the read of the MIRec
is failing for some reason:
When I send using RawToBytes
, no error message occurs but the value of MIRec.Text
is garbage.
When I just send a line of text using WriteLn
, the server receives and displays the data correctly and no access violation occurs.
I tried to follow examples that I have found from other posts on this issue. I would greatly appreciate any insight into what I am doing wrong.
Following are my client and server side code snippets:
Client
procedure TfrmMemoryStreamClient.btnSendClick2(Sender: TObject);
var
Buffer: TIdBytes;
MIRec: TSendRec;
msRecInfo: TMemoryStream;
msRecInfo2: TIdMemoryBufferStream;
begin
IdTCPClient1.Connect;
MIRec.Text := 'Hello World';
if rbSendStream.Checked then
begin
msRecInfo := TMemoryStream.Create;
try
msRecInfo.Write(MIRec, SizeOf(MIRec));
IdTCPClient1.IOHandler.Write(msRecInfo, 0, False);
finally
msRecInfo.Free;
end;
{
msRecInfo2 := TIdMemoryBufferStream.Create(@MIRec, SizeOf(TSendRec));
try
IdTCPClient1.IOHandler.Write(msRecInfo2);
finally
msRecInfo.Free;
end;
}
end
else
if rbSendBytes.Checked then
begin
Buffer := RawToBytes(MIRec, SizeOf(MIRec));
IdTCPClient1.IOHandler.Write(Buffer);
end
else
if rbWriteLn.Checked then
begin
IdTCPClient1.Socket.WriteLn(Edit1.Text);
end;
IdTCPClient1.DisConnect;
end;
Server
procedure TStreamServerForm.IdTCPServer1Execute(AContext: TIdContext);
var sName: String;
MIRec: TSendRec;
Buffer: TIdBytes;
msRecInfo: TMemoryStream;
begin
if not chkReceiveText.Checked then
begin
try
if chkReadBytes.Checked then
begin
AContext.Connection.IOHandler.ReadBytes(Buffer, SizeOf(MIRec));
BytesToRaw(Buffer, MIRec, SizeOf(MIRec));
Memo1.Lines.Add(MiRec.Text);
end
else
begin
msRecInfo := TMemoryStream.Create;
try
// does not read the stream size, just the stream data
AContext.Connection.IOHandler.ReadStream(msRecInfo, SizeOf(MIRec), False);
msRecInfo.Position := 0;
msRecInfo.Read(MIRec, SizeOf(MIRec));
Memo1.Lines.Add(MiRec.Text);
finally
msRecInfo.Free;
end;
{
AContext.Connection.IOHandler.ReadStream(msRecInfo, -1, False);
msRecInfo.Position := 0;
msRecInfo.Read(MIRec, SizeOf(MIRec));
Memo1.Lines.Add(MiRec.Text);
}
end;
Memo1.Lines.Add('read File');
except
Memo1.Lines.Add('error in read File');
end;
end
else
begin
sName := AContext.Connection.Socket.ReadLn;
Memo1.Lines.Add(sName);
end;
AContext.Connection.Disconnect;
end;
答案 0 :(得分:0)
TIdTCPServer
是一个多线程组件。其OnConnect
,OnDisconnect
和OnExecute
事件在工作线程的上下文中触发。因此,在访问UI控件时,您必须与主UI线程同步,例如Memo。
此外,String
是编译器管理的数据类型,TBitmap
是对象。两者都将数据存储在内存中的其他位置,因此您无法编写包含此类字段的记录。您只会写入其数据指针的值,而不是写入指向的实际数据。您需要在发送端将记录序列化为可传输格式,然后在接收端对其进行反序列化。这意味着单独处理记录字段。
尝试更像这样的事情:
type
TSendRec = record
SONo: string;
Text: string;
Bitmap: TBitMap;
end;
客户端
procedure TfrmMemoryStreamClient.btnSendClick2(Sender: TObject);
var
MIRec: TSendRec;
ms: TMemoryStream;
begin
MIRec.SONo := ...;
MIRec.Text := 'Hello World';
MIRec.Bitmap := TBitmap.Create;
...
try
IdTCPClient1.Connect;
try
IdTCPClient1.IOHandler.WriteLn(MIRec.SONo);
IdTCPClient1.IOHandler.WriteLn(MIRec.Text);
ms := TMemoryStream.Create;
try
MIRec.Bitmap.SaveToStream(ms);
IdTCPClient1.IOHandler.LargeStream := True;
IdTCPClient1.IOHandler.Write(ms, 0, True);
finally
ms.Free;
end;
finally
IdTCPClient1.Disconnect;
end;
finally
MIRec.Bitmap.Free;
end;
end;
服务器
procedure TStreamServerForm.IdTCPServer1Execute(AContext: TIdContext);
var
MIRec: TSendRec;
ms: TMemoryStream;
begin
MIRec.SONo := AContext.Connection.IOHandler.ReadLn;
MIRec.Text := AContext.Connection.IOHandler.ReadLn;
MIRec.Bitmap := TBitmap.Create;
try
ms := TMemoryStream.Create;
try
AContext.Connection.IOHandler.LargeStream := True;
AContext.Connection.IOHandler.ReadStream(ms, -1, False);
ms.Position := 0;
MIRec.Bitmap.LoadFromStream(ms);
finally
ms.Free;
end;
TThread.Synchronize(nil,
procedure
begin
Memo1.Lines.Add(MIRec.SONo);
Memo1.Lines.Add(MIRec.Text);
// display MIRec.Bitmap as needed...
end;
end;
finally
MIRec.Bitmap.Free;
end;
end;
可替换地:
客户端
procedure TfrmMemoryStreamClient.btnSendClick2(Sender: TObject);
var
MIRec: TSendRec;
ms: TMemoryStream;
procedure SendString(const S: String);
var
Buf: TIdBytes;
begin
Buf := IndyTextEncoding_UTF8.GetBytes(S);
IdTCPClient1.IOHandler.Write(Int32(Length(Buf)));
IdTCPClient1.IOHandler.Write(Buf);
end;
begin
MIRec.SONo := ...;
MIRec.Text := 'Hello World';
MIRec.Bitmap := TBitmap.Create;
...
try
IdTCPClient1.Connect;
try
SendString(MIRec.SONo);
SendString(MIRec.Text);
ms := TMemoryStream.Create;
try
MIRec.Bitmap.SaveToStream(ms);
IdTCPClient1.IOHandler.LargeStream := True;
IdTCPClient1.IOHandler.Write(ms, 0, True);
finally
ms.Free;
end;
finally
IdTCPClient1.Disconnect;
end;
finally
MIRec.Bitmap.Free;
end;
end;
服务器
procedure TStreamServerForm.IdTCPServer1Execute(AContext: TIdContext);
var
MIRec: TSendRec;
ms: TMemoryStream;
function RecvString: String;
begin
Result := AContext.Connection.IOHandler.ReadString(
AContext.Connection.IOHandler.ReadInt32,
IndyTextEncoding_UTF8);
end;
begin
MIRec.SONo := RecvString;
MIRec.Text := RecvString;
MIRec.Bitmap := TBitmap.Create;
try
ms := TMemoryStream.Create;
try
AContext.Connection.IOHandler.ReadStream(ms, -1, False);
ms.Position := 0;
MIRec.Bitmap.LoadFromStream(ms);
finally
ms.Free;
end;
TThread.Synchronize(nil,
procedure
begin
Memo1.Lines.Add(MIRec.SONo);
Memo1.Lines.Add(MIRec.Text);
// display MIRec.Bitmap as needed...
end;
end;
finally
MIRec.Bitmap.Free;
end;
end;