Send Files using LNET lazarus / 5-Lazarus Extra Components ---------------------------------------------------------------------- paulolferreira · 2008-03-23 13:32 I've been playing around with Lnet and already made a server and a client and both communicate sending text to one another! I've also been trying to send files but didn't quite get the hang of it. Is there any tutorial out there on how to do that? Or maybe some sample code. Anything would be nice! Thanks. ---------------------------------------------------------------------- LazMoz · 2008-03-31 23:32 Hi, you can please post the source of your project, I try to learn lnet in Lazarus and I don't find how. Thanks. ---------------------------------------------------------------------- Almindor · 2008-04-02 15:51 Hello, sending files isn't particularly complicated but it requires a bit of understanding. lNet is designed to be "bufferless" by definition, which means that when you issue a basic TCP.Send or TCP.SendMessage, you are doing a non-blocking send on some data. If the data is sufficiently small (usually < 65kB) then the return value will equal the size if the data you're trying to send (meaning it all went through). However sending big stuff like files requires a bit more work. What you need to do is understand the "Can I send" event principle. Basically it works like this, say you want to send a file with relatively big size, say few MB. You first read some chunk out of this file, say 1MB and try to send that in a cycle via lNet, until the return value of the .Send or .SendMessage returns 0. When this happens it means one of 2 things. Either there was an error on sending (and OnError will get called by lNet) or you filled the OS send buffer. When this happens (the filled buffer, not the error ) you should stop trying to send stuff for the moment (it's too fast for the network) and continue doing whatever else you were doing, unitl a "OnCanSend" event is called again by lNet. This event means that the send buffer is again ready for sending and you should repeat the procedure. Here's a simple code, for simplicity, I'm trying to send a huge string stored in FBuffer variable, FTCP is the TLTcp, and TMain is my main class (form). FSendIndex is a helper index I use to know where inside the buffer I am (what I already sent), I also expect you use "visual lnet", and that this is a client, not server (thus has only one socket): Code: [Select] { This event if properly assigned in TLTcp will be called   whenever the OS send buffer is "again ready to be filled" } procedure TMain.OnCanSend(aSocket: TLSocket); var   n: Integer; // will store results of .Send() begin   { repeat the sending until we sent the whole buffer, or .Send return 0 -- means we filled the send buffer for now. NOTE: FSendIndex  has to begin at 1 for a string! }   repeat     { try to send the rest of the buffer we didn't send yet }     n := FTCP.Send(FBuffer[FSendIndex], Length(FBuffer) - FSendIndex);     { if we sent something, move us in the buffer so we don't re-send the same part }     Inc(FSendIndex, n);   until (n = 0) or (FSendIndex > Length(FBuffer)); end; So, first you assign all the events (OnError etc.), then you read some data, say from a file into the FBuffer (best if it's big, MB or more). Then you initialize sending of the buffer by manually calling the TMain.OnCanSend(). This will try to send as much data as possible until either the send buffer is filled (n = 0), or you finished sending. IF the buffer was filled before all data could be sent, then you will eventually get the TMain.OnCanSend() called again, this time automatically by lNet. That's all to it really, all you need to add to this code is some "main program" logic, and you probably also want to read the data from file into the buffer "gradually", that is, not read a 1GB file at once into memory, but get say 10M, send that and then repeat. ---------------------------------------------------------------------- BlackCat · 2008-09-03 18:51 Hi boys...!! What about use FTP, I need use, but I can't understand wich event I need use exactly, i mean, for the ftpclientcomponent I can use all the events (onconnect, oncontrol,onerror,onfailure,onreceive,onsent onsucces), all works right? what is the order to use??.I think is something like this: 1. tcp.connect then  this active the event Onconnect, 2. onConnect  I need authentifycate the user and password, (Can I use an opendialog here? to copy my file to send to the server?) 3.call tcp.CallAction 4.if i had an error, then the OnError event display messages, etc. 5. but if the conexion are correct, then....(what event takes the control??, onControl, onSent, onSuccess?) 6. and I add in onSend event mi ftp.put(my_file_name_with_the_path) or i need send in a buffer in parts or something like almindor says??? 7. this is for upload my file, but... what about download the file in the server to my computer? Please, explain me... I try with this but, I don't have any results. Thanks a lot.... BlackCat ---------------------------------------------------------------------- Almindor · 2008-09-05 14:05 Ok first thing to understand that CallAction is only valid in non- visual lNet, that is if you don't use the visual components from the component palette. Client FTP is more or less like this: FTP.Connect; // say we specified the ip and port in either OnError or OnConnect gets called, depending on if we connected or not. If we connected, in the OnConnect, you should issue the authentication commands Either OnFailure or OnSuccess will get called based on the auth result (make sure you have the auth commands in the StatusSet property, otherwise it won't be reported) As for sending, you should use OnCanSend as I wrote above in my previous post. Hope this helps. WARNING: I just found out that I forgot to change defaults of the TLFtpClient.StatusSet to "full set" so that it by default watches all commands for success or failure. So you have to put the enums in this set yourself. This was an oversight as I changed this default in SMTP before. I've fixed it in trunk and will release 0.6.3 shortly. ---------------------------------------------------------------------- vice · 2008-09-26 14:41 Quote from: "Almindor2"Hello, sending files isn't particularly complicated but it requires a bit of understanding. lNet is designed to be "bufferless" by definition, which means that when you issue a basic TCP.Send or TCP.SendMessage, you are doing a non-blocking send on some data. If the data is sufficiently small (usually < 65kB) then the return value will equal the size if the data you're trying to send (meaning it all went through). However sending big stuff like files requires a bit more work. What you need to do is understand the "Can I send" event principle. Basically it works like this, say you want to send a file with relatively big size, say few MB. You first read some chunk out of this file, say 1MB and try to send that in a cycle via lNet, until the return value of the .Send or .SendMessage returns 0. When this happens it means one of 2 things. Either there was an error on sending (and OnError will get called by lNet) or you filled the OS send buffer. When this happens (the filled buffer, not the error ) you should stop trying to send stuff for the moment (it's too fast for the network) and continue doing whatever else you were doing, unitl a "OnCanSend" event is called again by lNet. This event means that the send buffer is again ready for sending and you should repeat the procedure. Here's a simple code, for simplicity, I'm trying to send a huge string stored in FBuffer variable, FTCP is the TLTcp, and TMain is my main class (form). FSendIndex is a helper index I use to know where inside the buffer I am (what I already sent), I also expect you use "visual lnet", and that this is a client, not server (thus has only one socket): Code: [Select] { This event if properly assigned in TLTcp will be called   whenever the OS send buffer is "again ready to be filled" } procedure TMain.OnCanSend(aSocket: TLSocket); var   n: Integer; // will store results of .Send() begin   { repeat the sending until we sent the whole buffer, or .Send return 0 -- means we filled the send buffer for now. NOTE: FSendIndex  has to begin at 1 for a string! }   repeat     { try to send the rest of the buffer we didn't send yet }     n := FTCP.Send(FBuffer[FSendIndex], Length(FBuffer) - FSendIndex);     { if we sent something, move us in the buffer so we don't re-send the same part }     Inc(FSendIndex, n);   until (n = 0) or (FSendIndex > Length(FBuffer)); end; So, first you assign all the events (OnError etc.), then you read some data, say from a file into the FBuffer (best if it's big, MB or more). Then you initialize sending of the buffer by manually calling the TMain.OnCanSend(). This will try to send as much data as possible until either the send buffer is filled (n = 0), or you finished sending. IF the buffer was filled before all data could be sent, then you will eventually get the TMain.OnCanSend() called again, this time automatically by lNet. That's all to it really, all you need to add to this code is some "main program" logic, and you probably also want to read the data from file into the buffer "gradually", that is, not read a 1GB file at once into memory, but get say 10M, send that and then repeat. I have a form whith a TEdit, TButton, TLTCPComponent and TStatusBar: Code: [Select] unit Unit1; {$mode objfpc}{$H+} interface uses   Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,   lNetComponents, lNet, StdCtrls, ComCtrls; type   { TForm1 }   TForm1 = class(TForm)     Send: TButton;     Edit1: TEdit;     LTCPComponent1: TLTCPComponent;     StatusBar1: TStatusBar;     procedure LTCPComponent1Accept(aSocket: TLSocket);     procedure LTCPComponent1Connect(aSocket: TLSocket);     procedure LTCPComponent1Disconnect(aSocket: TLSocket);     procedure LTCPComponent1Error(const msg: string; aSocket: TLSocket);     procedure LTCPComponent1Receive(aSocket: TLSocket);     procedure SendClick(Sender: TObject);     procedure FormCreate(Sender: TObject);     procedure OnCanSend(aSocket: TLSocket);   private     FBuffer: string;     FSendIndex: integer;     { private declarations }   public     { public declarations }   end; var   Form1: TForm1; implementation { TForm1 } procedure TForm1.FormCreate(Sender: TObject); begin   FSendIndex := 1;   LTCPComponent1.Connect('127.0.0.1',9923); end; procedure TForm1.SendClick(Sender: TObject); begin   FBuffer := Edit1.Text;   LTCPComponent1.OnCanSend(LTCPComponent1.Iterator); end; procedure TForm1.OnCanSend(aSocket: TLSocket); var   n: Integer; begin   repeat     n := LTCPComponent1.Send(FBuffer[FSendIndex], Length(FBuffer) - FSendIndex);     Inc(FSendIndex, n);   until (n = 0) or (FSendIndex > Length(FBuffer)); end; procedure TForm1.LTCPComponent1Accept(aSocket: TLSocket); begin   StatusBar1.panels[0].text := 'Accepted'; end; procedure TForm1.LTCPComponent1Connect(aSocket: TLSocket); begin   StatusBar1.panels[0].text := 'Connected'; end; procedure TForm1.LTCPComponent1Disconnect(aSocket: TLSocket); begin   StatusBar1.panels[0].text := 'Disconnected'; end; procedure TForm1.LTCPComponent1Error(const msg: string; aSocket: TLSocket); begin   StatusBar1.panels[0].text := 'Error'; end; procedure TForm1.LTCPComponent1Receive(aSocket: TLSocket); begin   StatusBar1.panels[0].text := 'Receive'; end; initialization   {$I unit1.lrs} end. Can you help me to complete the program? Thanks! ---------------------------------------------------------------------- tpglemur · 2008-10-14 09:27 Quote from: "LazMoz"Hi, you can please post the source of your project, I try to learn lnet in Lazarus and I don't find how. Thanks. The lnet file which you downloaded (to install the components) got some nice examples in it.