Search code examples
socketsdelphitextbufferdelphi-7

Send/Receive text with buffer using sockets in Delphi


So I have searched for a way to make this work but found no working solution. I am trying to send text that has been inserted in to a memo from the Client to the Server using sockets. Unfortunately I cannot get the coding working. It might be something stupid I'm doing wrong; but I don't know where the mistake is. I created a stable connection between the server and client, and I am able to send text using Server.Socket.Connections[Form1.ListView1.Selected.Index].SendText('Texthere'); but I just cant get it to send and receive text or anything using buffers.

Code on Client:

procedure TIBAT.Sends1Click(Sender: TObject); //Button to start the sending of the text on the memo component
var
  ms: TMemoryStream;
  size: Integer;
begin
  if (Form1.ListView1.Selected <> nil) then //if a server is selected
  begin
  Form1.Server.Socket.Connections[Form1.ListView1.Selected.Index].SendText('IBAT');
    ms:= TMemoryStream.Create;
    try
      IBAT.Memo1.Lines.SaveToStream(ms);
      ms.Position:= 0;
      Size:= MS.Size;    
     Form1.Server.Socket.Connections[Form1.ListView1.Selected.Index].SendBuf(Size,SizeOf(Size));
     Form1.Server.Socket.Connections[Form1.ListView1.Selected.Index].SendStream(ms);
    except
      ms.Free;
      ShowMessage('FAILED');
    end;
  end;
end;

Code on the Server:

private
    Stream: TMemoryStream;
    FSize: Integer;
    writing: Boolean;

...

procedure TSock.ClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
  Command     :String;
  BytesReceived: Longint;
  CopyBuffer: Pointer; //buffer for copying
  ChunkSize: Integer;
  TempSize: Integer;
const
  MaxChunkSize: Longint = 8192;
begin
  Command := Socket.ReceiveText;
if split(Command, '|', 0) = 'IBAT' then
  begin
  If FSize=0 then
      begin
        //ShowMessage(IntToStr(Socket.ReceiveLength)); added messageboxes everywhere to figure out where the problem is
        If Socket.ReceiveLength>SizeOf(TempSize) then
        begin
          Socket.ReceiveBuf(TempSize,SizeOf(TempSize));
          Stream:= TMemoryStream.Create;
          Stream.SetSize(TempSize);
          //ShowMessage(IntToStr(TempSize));
          FSize:= TempSize;
          writing:= True;
        End;
      End;
      If (FSize>0) and (writing) then
      begin
        GetMem(CopyBuffer, MaxChunkSize); //allocate the buffer
        While Socket.ReceiveLength>0 do
        Begin
          ChunkSize:= Socket.ReceiveLength;
          If ChunkSize > MaxChunkSize then ChunkSize:= MaxChunkSize;
          BytesReceived:= Socket.ReceiveBuf(CopyBuffer^,ChunkSize);
          Stream.Write(CopyBuffer^, BytesReceived); //write chunk
          Dec(FSize,BytesReceived);
        End;
        FreeMem(CopyBuffer, MaxChunkSize); //free allocated buffer
        If FSize = 0 then
        begin
        Stream.SaveToFile(TempDir+IntToStr(GetTickCount)+'.cmd');
        Socket.SendText('File received!');
        Stream.SetSize(0);
        FSize:= 0;
        End;
    FreeMem(CopyBuffer, MaxChunkSize);
    Writing:= False;
    End;
end;

I know using Indy is more effective and better than sockets, but I've already started coding using it and switching over to Indy is a big pain. If anyone can help me get this working I would really appreciate it.


Solution

  • I've just make a very-very simple example of TCP client and server on Indy. It is very simple, even no exceptions handling there.

    Take a look at a repository at github and feel free to ask any questions.

    Anyway, using of native socket components is not a good idea. Try to study Indy, then Synapse or mORMot may be. Indy will be a good start.

    UPD I'll try to get some code here in answer:

    Start a TCP server listening on specified port:

    var
      IdTcpServer1: TIdTcpServer
    
    [...]
    
    IdTcpServer1.DefaultPort := SERVER_PORT;
    IdTcpServer1.Active := True;
    

    Server will start listening on 0.0.0.0:1234.

    Connect a TCP client to TCP server:

    var
      IdTcpClient1: TIdTcpClient;
    
    [...]
    
    IdTcpClient1.Host := SERVER_HOST; // IP or DNS name
    IdTcpClient1.Port := SERVER_PORT;
    IdTcpClient1.Connect;
    

    Send a text line to socket (after it's connected):

    IdTcpClient1.IOHandler.Writeln('Hello world!');
    

    Server must implement OnExecute event handler:

    // manual init in code or you can assign it in design-time in IDE
    IdTcpServer1.OnExecute := IdTcpServer1ExecuteHandler;
    
    [...]
    
    // very simple example
    procedure IdTcpServer1ExecuteHandler(AContext: TIdContext);
    var
      s: string;
    begin
      s := AContext.Connection.IOHandler.ReadLn();
      if s <> EmptyStr then
      begin
        // do something with received string s
      end;
    end;