使用 Indy TCPClient/TCPServer 从手机 XE8 发送图片
Using Indy TCPClient/TCPServer to send picture from mobile XE8
我有一个用 Delphi XE8 编写的简单移动应用程序,允许用户拍照,然后使用 Indy TCPClient/TCP Server 将照片发送到服务器。
我搜索了论坛并找到了很多以各种方式发送数据的示例。我尝试的每一种方法都会在服务器端导致访问冲突或数据损坏。
我的最终目标是将包含唯一标识符、描述和图片(位图)的记录从客户端发送到服务器。
但我开始尝试简单地从 windows 客户端向服务器发送带有一些文本的记录。然后我会尝试将解决方案实施到我的移动应用程序中。
type
TSendRec = record
// SONo: string;
Text: string;
// Bitmap: TBitMap;
end
我已经按照下面的代码尝试了以下3种方法:
使用流发送
使用 RawToBytes 和 TIDBytes
发送。
使用Writeln
和Readln
发送一行文本
当我尝试使用流发送时,我遇到了以下访问冲突:
Project memorystream_server.exe raised the exception class $C0000005 with message 'access violation at 0x00409e46: write of address 0x0065d1bc
当我尝试在服务器端访问 MiRec.Text
的值时发生错误。
Memo1.Lines.Add(MiRec.Text);
所以我假设 MIRec
的读取由于某种原因失败了:
当我使用 RawToBytes
发送时,没有出现错误消息,但 MIRec.Text
的值是垃圾。
当我使用 WriteLn
发送一行文本时,服务器正确接收并显示数据,没有发生访问冲突。
我尝试遵循从其他帖子中找到的有关此问题的示例。如果能深入了解我做错了什么,我将不胜感激。
以下是我的客户端和服务器端代码片段:
客户端
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;
服务器
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;
TIdTCPServer
是一个多线程组件。它的 OnConnect
、OnDisconnect
和 OnExecute
事件在工作线程的上下文中触发。因此,在访问 UI 控件(如备忘录)时,您必须与主 UI 线程同步。
此外,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;
我有一个用 Delphi XE8 编写的简单移动应用程序,允许用户拍照,然后使用 Indy TCPClient/TCP Server 将照片发送到服务器。
我搜索了论坛并找到了很多以各种方式发送数据的示例。我尝试的每一种方法都会在服务器端导致访问冲突或数据损坏。
我的最终目标是将包含唯一标识符、描述和图片(位图)的记录从客户端发送到服务器。 但我开始尝试简单地从 windows 客户端向服务器发送带有一些文本的记录。然后我会尝试将解决方案实施到我的移动应用程序中。
type
TSendRec = record
// SONo: string;
Text: string;
// Bitmap: TBitMap;
end
我已经按照下面的代码尝试了以下3种方法:
使用流发送
使用 RawToBytes 和
TIDBytes
发送。使用
Writeln
和Readln
发送一行文本
当我尝试使用流发送时,我遇到了以下访问冲突:
Project memorystream_server.exe raised the exception class $C0000005 with message 'access violation at 0x00409e46: write of address 0x0065d1bc
当我尝试在服务器端访问 MiRec.Text
的值时发生错误。
Memo1.Lines.Add(MiRec.Text);
所以我假设 MIRec
的读取由于某种原因失败了:
当我使用 RawToBytes
发送时,没有出现错误消息,但 MIRec.Text
的值是垃圾。
当我使用 WriteLn
发送一行文本时,服务器正确接收并显示数据,没有发生访问冲突。
我尝试遵循从其他帖子中找到的有关此问题的示例。如果能深入了解我做错了什么,我将不胜感激。
以下是我的客户端和服务器端代码片段:
客户端
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;
服务器
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;
TIdTCPServer
是一个多线程组件。它的 OnConnect
、OnDisconnect
和 OnExecute
事件在工作线程的上下文中触发。因此,在访问 UI 控件(如备忘录)时,您必须与主 UI 线程同步。
此外,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;