Sending and receiving a TMemoryStream using IdTCPClient and IdTCPServer

爱⌒轻易说出口 提交于 2019-12-10 16:34:23

问题


I found Remy Lebeau's chat demo of IdTCP components in XE2 and I wanted to play with it a little bit. (It can be found here) I would like to send a picture using these components and the best approach seems to be using TMemoryStream. If I send strings, the connection works fine, the strings are transmitted successfully, however when I change it to Stream instead, it doesn't work. Here is the code:

Server

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;

Client

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;

When I try to send the stream from the client, nothing observable happens (breakpoint in the OnExecute doesn't fire). However, when closing the programs(after sending the MemoryStream), two things happen:

  • If the Client is closed first, only then does the except part get processed (the log displays the 'Failed to receive' error. However, even if I place a breakpoint on the first line of the try-except block, it somehow gets skipped and only the error is displayed).
  • If the Server is closed first, the IDE doesn't change back from debug, Client doesn't change its state to disconnected (as it normally does when server disconnects) and after the Client is closed as well, an Access Violation error from the Server app appears. I guess this means that there is a thread of the Server still running and maintaining the connection. But no matter how much time i give it, it never completes the task of receiving the MemoryStream.

Note: The server uses IdSchedulerOfThreadDefault and IdAntiFreeze, if that matters.

As I can't find any reliable source of help for the revamped Indy 10 (it all appears to apply for the older Indy 10, or even Indy 9), I hope you can tell me what is wrong. Thanks


- ANSWER -

SERVER

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;

CLIENT

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;

回答1:


Just use the proper parameters:

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

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



回答2:


Send the size of the stream upfront so that the server knows how many bytes it must read.

Server:

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;

Client (Fixed resource handling)

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;


来源:https://stackoverflow.com/questions/12159868/sending-and-receiving-a-tmemorystream-using-idtcpclient-and-idtcpserver

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!