如何使用Indy TCP Server / Client传输数据?

时间:2017-07-22 14:08:39

标签: delphi indy transfer

我想将数据从TIdTCPServer传输到TIdTCPClient。

在服务器端,我有:

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var x:Integer;
    Received:String;
    SendBuff:TBytes;
    hFile:THandle;
    fSize:Int64;
begin
 fSize:=0;
 if MOpenFileForRead(hFile,MGetExePath+'\test.jpg') then begin
  fSize:=MFileSize(hFile);
  SetLength(SendBuff,fSize);
  MReadFile(hFile,SendBuff[0],fSize);
  MCloseFile(hFile);
 end;

 // ... here the SendBuff contains valid data, I checked. 

 repeat
  Received:=AContext.Connection.Socket.ReadLn;
  if not AContext.Connection.Connected then Exit;
  if Received=CMD_TEST_FILE then begin
   AContext.Connection.Socket.Write(fSize);
   AContext.Connection.Socket.WriteBufferOpen;
   AContext.Connection.Socket.Write(SendBuff);
   AContext.Connection.Socket.WriteBufferClose;
  end;
 until False;
end;

客户方:

procedure TForm1.Button2Click(Sender: TObject);
var fSize:Int64;
    RecvBuff:TBytes;
    hFile:THandle;
begin
  IdTCPClient1.Socket.WriteLn(CMD_TEST_FILE);
  fSize:=IdTCPClient1.Socket.ReadInt64;
  SetLength(RecvBuff,fSize);
  IdTCPClient1.Socket.ReadBytes(RecvBuff,fSize);
  if MCreateFile(hFile, MGetExePath+'\new.jpg') then begin
   MWriteFile(hFile,RecvBuff[0],fSize);
   MCloseFile(hFile);
  end;
  Memo1.Lines.Add('ok');
end;

......但它不起作用。我检查了所使用的读写数据函数,它们没问题。在服务器上,缓冲区设置为ok,文件大小到达客户端ok,但客户端缓冲区的内容只是零。

P.S:我想以这种方式发送文件而不是流或其他任何东西。

1 个答案:

答案 0 :(得分:3)

如果你查看ReadBytes()的签名,它有一个可选的AAppend参数默认为True:

procedure ReadBytes(var VBuffer: TIdBytes; AByteCount: Integer; AAppend: Boolean = True); virtual;

如果为true,则从套接字读取字节并将它们附加到现有字节数组的末尾。由于您预先分配了数组,因此初始字节未定义,文件字节在未定义的字节之后。

要解决此问题,您需要:

  1. 停止预先分配字节数组,让ReadBytes()为您分配。

    procedure TForm1.Button2Click(Sender: TObject);
    var
      fSize: Int64;
      RecvBuff: TBytes;
      hFile: THandle;
    begin
      IdTCPClient1.Socket.WriteLn(CMD_TEST_FILE);
      fSize := IdTCPClient1.Socket.ReadInt64;
      // SetLength(RecvBuff,fSize); // <-- remove this line
      IdTCPClient1.Socket.ReadBytes(RecvBuffer, fSize);
      if MCreateFile(hFile, MGetExePath+'\new.jpg') then
      begin
        MWriteFile(haile, RecvBuff[0], fSize);
        MCloseFile(hFile);
      end;
      Memo1.Lines.Add('ok');
    end;
    
  2. 预先分配数组,但将AAppend设置为False,以便字节填充现有数组而不是附加到它。

    procedure TForm1.Button2Click(Sender: TObject);
    var
      fSize: Int64;
      RecvBuff: TBytes;
      hFile: THandle;
    begin
      IdTCPClient1.Socket.WriteLn(CMD_TEST_FILE);
      fSize := IdTCPClient1.Socket.ReadInt64;
      SetLength(RecvBuff, fSize);
      IdTCPClient1.Socket.ReadBytes(RecvBuff, fSize, False);
      if MCreateFile(hFile, MGetExePath+'\new.jpg') then
      begin
        MWriteFile(haile, RecvBuff[0], fSize);
        MCloseFile(hFile);
      end;
      Memo1.Lines.Add('ok');
    end;
    
  3. 更新:话虽如此,我强烈建议您改用TStream,尽管您说不想这样做。它将大大简化代码和内存管理,而不会破坏您选择使用的通信协议:

    procedure TForm1.IdTCPServer1Connect(AContext: TIdContext);
    begin
      AContext.Data := TFileStream.Create(MGetExePath+'\test.jpg', fmOpenRead or fmShareDenyWrite);
      AContext.Connection.IOHandler.LargeStream := True;
    end;
    
    TForm1.IdTCPServer1Execute(AContext: TIdContext);
    var
      Received: String;
    begin
      Received := AContext.Connection.IOHandler.ReadLn;
      if Received = CMD_TEST_FILE then
      begin
        AContext.Connection.IOHandler.Write(TStream(AContext.Data), 0, True);
      end;
    end;
    

    procedure TForm1.Button2Click(Sender: TObject);
    var
      FileName: string;
      Strm: TStream;
    begin
      FileName := MGetExePath+'\new.jpg';
      Strm := TFileStream.Create(FileName, fmCreate);
      try
        try
          IdTCPClient1.IOHandler.WriteLn
    (CMD_TEST_FILE);
          IdTCPClient1.IOHandler.ReadStream(Strm, -1, False);
        finally
          Strm.Free;
        end;
      except
        DeleteFile(FileName);
        raise;
      end;
      Memo1.Lines.Add('ok');
    end;