使用IdTCPClient和IdTCPServer发送和接收TMemoryStream

时间:2012-08-28 13:03:04

标签: delphi tcp indy

我在XE2中找到了Remy Lebeau的IdTCP组件聊天演示,我想稍微玩一下。 (可以找到here)我想使用这些组件发送图片,最好的方法似乎是使用TMemoryStream。如果我发送字符串,连接工作正常,字符串传输成功,但是当我将其更改为流时,它不起作用。这是代码:

服务器

procedure TMainForm.IdTCPServerExecute(AContext: TIdContext);
var rcvdMsg: string;
  ms:TMemoryStream;
begin
// This commented code is working, it receives and sends strings.
//  rcvdMsg:=AContext.Connection.IOHandler.ReadLn;
//  LogMessage('<ServerExec> '+rcvdMsg);
//
//  TResponseSync.SendResponse(AContext, rcvdMsg);
  try
    ms:=TMemoryStream.Create;
    AContext.Connection.IOHandler.ReadStream(ms);

    ms.SaveToFile('c:\networked.bmp');
  except
    LogMessage('Failed to receive',clred);
  end;
end;

客户端

procedure TfrmMain.Button1Click(Sender: TObject);
var ms: TMemoryStream;
    bmp: TBitmap;
    pic: TPicture;
    s: string;
begin
  // Again, this code is working for sending strings.
  // s:=edMsg.Text;
  // Client.IOHandler.WriteLn(s);
  ms:=TMemoryStream.Create;

  pic:=TPicture.Create;
  pic.LoadFromFile('c:\Back.png');

  bmp:=TBitmap.Create;
  bmp.Width:=pic.Width;
  bmp.Height:=pic.Height;
  bmp.Canvas.Draw(0,0,pic.Graphic);

  bmp.SaveToStream(ms);
  ms.Position:=0;
  Client.IOHandler.Write(ms);
  ms.Free;
end;

当我尝试从客户端发送流时,没有任何可观察的事件发生(OnExecute中的断点不会触发)。但是,在关闭程序时(发送MemoryStream之后),会发生两件事:

  • 如果首先关闭客户端,那么except部分才会被处理(日志显示'接收失败'错误。但是,即使我在第一行上放置一个断点 - 除了块之外,它会以某种方式被跳过并且只显示错误。)
  • 如果首先关闭服务器,则IDE不会从 debug 更改回来,客户端不会将其状态更改为 disconnected (正如服务器通常所做的那样)断开连接)并在客户端关闭后,将显示Server应用程序中的Access Violation错误。我想这意味着服务器的一个线程仍在运行并维护连接。但无论我给它多少时间,它都永远不会完成接收MemoryStream的任务。

注意:如果重要,服务器会使用IdSchedulerOfThreadDefaultIdAntiFreeze

由于我找不到任何可靠的改造Indy 10的帮助来源(它似乎都适用于较旧的Indy 10,甚至Indy 9),我希望你能告诉我出了什么问题。感谢


- 答案 -

服务器

procedure TMainForm.IdTCPServerExecute(AContext: TIdContext);
var size: integer;
    ms:TMemoryStream;
begin
  try
    ms:=TMemoryStream.Create;
    size:=AContext.Connection.IOHandler.ReadLongInt;
    AContext.Connection.IOHandler.ReadStream(ms, size);

    ms.SaveToFile('c:\networked.bmp');
  except
    LogMessage('Failed to receive',clred);
  end;
end;

客户端

procedure TfrmMain.Button1Click(Sender: TObject);
var ms: TMemoryStream;
    bmp: TBitmap;
    pic: TPicture;
begin
  ms:=TMemoryStream.Create;

  pic:=TPicture.Create;
  pic.LoadFromFile('c:\Back.png');

  bmp:=TBitmap.Create;
  bmp.Width:=pic.Width;
  bmp.Height:=pic.Height;
  bmp.Canvas.Draw(0,0,pic.Graphic);

  bmp.SaveToStream(ms);
  ms.Position:=0;
  Client.IOHandler.Write(ms, 0, True);
  ms.Free;
end;

2 个答案:

答案 0 :(得分:6)

只需使用正确的参数:

.IOHandler.Write(ms, 0, true); //true ... indicates WriteByteCount

.IOHandler.ReadStream(ms, -1); //-1 ... use ByteCount

答案 1 :(得分:3)

预先发送流的大小,以便服务器知道它必须读取多少字节。

服务器:

procedure TMainForm.IdTCPServerExecute(AContext: TIdContext);
var rcvdMsg: string;
  ms:TMemoryStream;
  size : integer;
begin
// This commented code is working, it receives and sends strings.
//  rcvdMsg:=AContext.Connection.IOHandler.ReadLn;
//  LogMessage('<ServerExec> '+rcvdMsg);
//
//  TResponseSync.SendResponse(AContext, rcvdMsg);
  try
    ms:=TMemoryStream.Create;
    try 
     size := AContext.Connection.IOHandler.ReadLongint; 
     AContext.Connection.IOHandler.ReadStream(ms, size, false);
     ms.SaveToFile('c:\networked.bmp');
    finally
     ms.Free;
    end;        
  except
    LogMessage('Failed to receive',clred);
  end;
end;

客户端(固定资源处理)

procedure TfrmMain.Button1Click(Sender: TObject);
var ms: TMemoryStream;
    bmp: TBitmap;
    pic: TPicture;
    s: string;
begin
  // Again, this code is working for sending strings.
  // s:=edMsg.Text;
  // Client.IOHandler.WriteLn(s);
  ms:=TMemoryStream.Create;
  pic:=TPicture.Create;
  bmp:=TBitmap.Create;
  try
   pic.LoadFromFile('c:\Back.png');   
   bmp.Width:=pic.Width;
   bmp.Height:=pic.Height;
   bmp.Canvas.Draw(0,0,pic.Graphic);
   bmp.SaveToStream(ms);
   ms.Position:=0;
   Client.IOHandler.Write(ms.Size);
   Client.IOHandler.Write(ms);
  finally
   ms.Free;
   bmp.Free;
   pic.Free;
  end;
end;