Link to home
Start Free TrialLog in
Avatar of fjocke
fjocke

asked on

TWSocket, DataAvailable, Receive Stream

Hi everyone. Im trying to make a Filesharing program. I previously worked with Indy but pretty fast hit the wall where Indy's components simply were not good enough.

It was not that hard to send streams with Indy, cause it has functions for it.
However, now i use FPiette Components.

I think was able to send Data from my server to my client by:

Procedure TThrdSrvForm.SendUserListToUser(Client : TMyClient);
var
        StringList: TStringList;
        i: integer;
        Buffer: array [1..BLOCK_SIZE] of char;
        FileStream: TMemoryStream;
        Count:  Integer;
begin
        StringList := TStringList.Create;
        for i := 0 to TreeView1.Items.Count - 1 do
        begin
        if TreeView1.Items[i].Level = 0 then
        begin
        StringList.Add(TreeView1.Items[i].Text)
        end;
        end;
        FileStream := TMemoryStream.Create;
        StringList.SaveToStream(Filestream);
        //StringList.SaveToStream(Filestream);
        //ListBox1.Items.SaveToStream(Filestream);
        FileStream.Position := 0;
        //StringList.SaveToStream(Filestream);
        Client.SendStr('::UserList::' + #13#10);
        Client.Sendstr(IntToStr(FileStream.Size) + #13#10);
        //AContext.Connection.IOHandler.WriteLn('::UserList::');
        //AContext.Connection.IOHandler.WriteLn(IntToStr(FileStream.Size));
        //AContext.Connection.IOHandler.Write(FileStream);

         Count := FileStream.Read(Buffer[1], BLOCK_SIZE);
      if Count > 0 then
         Client.Send(@Buffer[1], Count);
      if Count < BLOCK_SIZE then
        // Done := True;
        FileStream.Free;
end;

Since i do receive the name and size to the client.
However.

My Client also recieves commands from the server. Hence i have a Command Parser:
//Taken from the Demo examples.
procedure TClientForm.CliSocketDataAvailable(Sender: TObject; ErrCode: Word);
var
    Len : Integer;
begin
    { We use line mode, we will receive a complete line }
    Len := CliSocket.Receive(@Buffer, SizeOf(Buffer) - 1);
    if Len <= 0 then
        Exit;

    Buffer[Len]       := #0;              { Nul terminate  }
    ProcessCommand(StrPas(Buffer));       { Pass as string }

end;

I do realize i have to put in the Stream Parser code here. Question is, how am i suppost to seperate Stream from TextCommands?

I tried to simply do like this:

procedure TClientForm.ProcessCommand(Cmd : String);
    var
      Pc : PChar;
  Len : integer;
begin
    { Here you should write your command interpreter.                       }
    { For simplicity, we just display received command !                    }
    { First remove EndOfLine marker                                         }
    if (Length(Cmd) >= Length(CliSocket.LineEnd)) and
       (Copy(Cmd, Length(Cmd) - Length(CliSocket.LineEnd) + 1,
             Length(CliSocket.LineEnd)) = CliSocket.LineEnd) then
        Cmd := Copy(Cmd, 1, Length(Cmd) - Length(CliSocket.LineEnd));
    { Then display in memo                                                  }
    if Cmd = '::UserList::' then
    begin
  if not Assigned(ReceivedFile) then
  ReceivedFile := TFileStream.Create('C:\TEMP\RecFile.xxx', fmOpenRead);
  Pc := AllocMem(10);
  Len := CliSocket.Receive(Pc, 10);
  ReceivedFile.Write(Pc[0], Len);
  FreeMem(Pc);
  ReceivedFile.Free;

end;

    Display(Cmd);
end;


Where i hoped to be able to lock the recieved text after "::UserList:" is sent from the server.
To pick out the Filesize and the Buffer.

Can anyone show me an efficient way to send and receive files, by using these types of commands
to start the actual file transfer?

What im trying todo here, is to send an Userlist from the server, to the client that connects.
Meaning i try to transform a StringList into a Stream and then send it to the client, for the client to Load the stream into a ListBox.

I need a clean slution to handle both streams and command strings.
Since the server and client exchange commands such as Nickname, Disconnect, Transfer File etc etc.

I'll try work on this myself as usual, but i can't really find a solution at the moment, since this is new stuff for me!

Avatar of TheRealLoki
TheRealLoki
Flag of New Zealand image

take a look at my demo to another user here
https://filedb.experts-exchange.com/incoming/ee-stuff/7180-ICSTCPLokiDemo.zip
(actual questions were
https://www.experts-exchange.com/questions/23484649/I-need-a-bug-fixed-and-a-minor-change-the-code.html and https://www.experts-exchange.com/questions/23399636/I-need-some-modifications-done-to-this-code.html
)
the trick is to set the LineMode property at the appropriate time, and deny sending of other commands while the streaming stuff is going on.
I used a "ClientStage" property for this...
oh, also to know the stream size, and _only_ read to the end of that size with linemode = false.
then you can set the linemode back to true and the command text is there waiting
Avatar of fjocke
fjocke

ASKER

So does this mean that you cant recieve commands while you are recieving a file?
And what if you are supposted to recieve sevral files at the same time? :S

Anyhow.. ugh. I guess the main problems with those examples are that they purly are used to send files or screenshots with, i can't really get to how to time the both functions together...
Avatar of fjocke

ASKER

I get you are setting the client to stage modes here. The problem is that you want it to continue to listen to commands and other file requests. This wont happen when we do it like this, right?
ASKER CERTIFIED SOLUTION
Avatar of TheRealLoki
TheRealLoki
Flag of New Zealand image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of fjocke

ASKER

I do understand what you mean. The other way would be to issue a packet-handler to use your model.
Creating new connections as they are required.

Yes, the model is a hub network. However, information shall be encrypted fully.
The main purpose with the program is to send information between a group of people over the internet secure.

Client joins Server. Client sends -> ::Nickname:: ClientName
Server sends back -> ::Userlist:: STREAMDATA
Client takes the StreamData into a ListBox.

Client sees other users in the list, Right clicks them, Downloads their Shared files List
CMD -> ClientName ASKS for sharedfilelist from ClientName2.

On the server side, each name is bound to an IP. in a TreeList.
Like: Donald Duck
                 +- 222.209.111.2

The server will will then rely then command to that ip. And that Client will prepare to send the sharedfileslist to the server, which will tunnel it back to ClientName..

Client selects the files he wants to download
Client sends to server -> ClientName WANTS file.zip from ClientName2. Server sends to ClientName2
ClientName2 Sends the command: ClientName2 SENDSFILE file.zip TO ClientName
All the data is being passed through the server and then sent to the other client.

Yes, this is a bottleneck i know, but its intended for small group networks, where you want the transactions secure.

ClientName gets ready to receive the file and then saves it.

Thats the model :)
Do you want the server to receive the FULL file before passing it on to the other client, or do you want it to forward each (packet) on as it is received?
With TWSocket, How did you plan on having client B's "incoming stream" sent to client A and handle async?
Is file size/speed an issue? i.e. do you mind bloating the file with packet header information to distinguish it from a command (and probably encoding it to) ?
I'd still recommend using a 2nd connection for the file sending/receiving to get around this
Avatar of fjocke

ASKER

No, server should only pass on each packet.

Well i guess this could be solved with your way of creating a new dynamic TWSocket.
Which only purpose is to be created to receive a file.
The only trick here that i can see, is to make sure the server then passes the the packets to the CORRECT
new connection of the client.

Is there any direct problem you can see with this solution?

I'd really really want to skip using a ServerSocket on the client side, security matters :)
> I'd really really want to skip using a ServerSocket on the client side, security matters :)
yeah, you have to get the clients to open pinholes in their firewalls etc... pain if they dont know how
Avatar of fjocke

ASKER

Anyhow, i guess you are obligated to your points for your idea assistance. Here you go laddie :)