Delphi and Indy sockets?



  • Well, I decided it's time to switch from VB6 to something else. By looking at the few tutorials I could find, and so on, I have a basic client that can connect to a server and send/receive data. However, there are two issues: 1. the program is laggy when connected, and, 2. when disconnected, my disconnect code is not triggered, I get an error box saying "Connection Gracefully Disconnected" or something similar.


    Here is the code I'm using:

    procedure TForm1.Button1Click(Sender: TObject);

    begin

    Button1.Enabled := False;

    IndyClient.Host := 'lightbringer.furcadia.com';

    IndyClient.Port := 6500;

    Memo1.Lines.Add('Connected.');

    IndyClient.Connect; try

    while IndyClient.Connected do

    begin

    Memo1.Lines.Add(IndyClient.ReadLn);

    end;

    finally IndyClient.Disconnect; end;

    Button1.Enabled := True;

    Memo1.Lines.Add('Disconnected.');

    end;

    procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);

    begin

    if Key = #13 then

    begin

    IndyClient.WriteLn(Edit1.Text);

    Edit1.Text := '';

    end;

    end;


    Can anyone help?

    Edit: If it helps, I'm using Delphi 7 Personal and Indy 9.

    On a side note, nice enterprisey forum making me create the whole post in the HTML Source Editor, because I can't stand having <p> tags which add unnecessary spacing AND are used as well as <br> tags.



  • I could not edit this post due to the stupid time limit.

    I have managed to fix the code by adding exception handling. However, the program is still laggy and typing does not appear instantly in an Edit control after I've pressed the appropiate key. Help? 



  • @Treeki said:

    I could not edit this post due to the stupid time limit.

    I have managed to fix the code by adding exception handling. However, the program is still laggy and typing does not appear instantly in an Edit control after I've pressed the appropiate key. Help? 

     

    Try a Memo1.Update after adding each line. 



  • I'm not sure you're doing this right.

    First of all (nitpick):

    <FONT face="Courier New">Memo1.Lines.Add('Connected.');
    IndyClient.Connect;</FONT>

    <FONT face="Courier New">Should be</FONT>

    <FONT face="Courier New">IndyClient.Connect;
    </FONT><FONT face="Courier New">Memo1.Lines.Add('Connected.');</FONT>

    <FONT face="Courier New">Otherwise the message is misleading.</FONT>

    <FONT face="Courier New">On to more serious stuff: the message you are getting (Connection closed gracefully), is probably because in some instances you are calling Disconnect on an already disconnected socket. Your code goes like this:</FONT>

    <FONT face="Courier New">try
        while Connected do stuff;
        { Not connected here }
    finally
        Disconnect;</FONT>

    <FONT face="Courier New">You can see the "finally Disconnect" part is superfluous. Better would be to use 'except': close the connection in case of an error, not in case of normal operation.</FONT>

    <FONT face="Courier New">And why is your application "laggy"? Because Indy sockets are normally blocking. The code as you're writing it right now is suitable if you need to receive or send a short burst of information, but it looks as if you're trying to write an interactive prompt. The reason this won't work (or if it works at all, won't work well), is because the program will be stuck in Button1's event handler until the connection is closed. Look at the code: it will not return before that. So until the connection is closed, Edit1's event handler (to write to the socket) will never be triggered. Interactive prompt will not work.</FONT>

    <FONT face="Courier New">The right (but hard) way to work around this, is to use the blocking socket in a thread, but that gets complicated awful fast, especially if you have no experience with it.</FONT>

    <FONT face="Courier New">The slightly less right (but acceptable in most cases) way is to separate the code into multiple event handlers. Let the button connect, but nothing else. Add a timer that checks periodically (every 10ms) for input on the socket, let it read that and add it to the memo. Let another button disconnect. You can leave the Edit1 event handler like it is, but it should check if the socket is connected before writing.</FONT>

    <FONT face="Courier New">The lazy (and hackish) way to make it work is to just add a call to Application.ProcessMessages in your inner loop. This will make sure that other event handlers are called (such as Edit1's keypress event, and memo1's paint event) while you're technically still in Button1's event handler. However, be advised that this will only work each time a line is read from the socket (since the socket is blocking, the call to ReadLn will not return before a line is read or the connection is lost)... so this might still freeze your application if no line is received for a long time (which may very well be the case in an interactive prompt-type of application).</FONT>

    I'd recommend solution 2 for now, since it is most correct for the amount of effort you'd have to put in. You don't need to switch to asynchronous sockets. You can, but they're much less nice to code against. Good luck.



  • Eek! A furry RPG?



  • I'm assuming asynchronous sockets are similar to what VB and the WinSock control uses, correct?

    I cannot just receive or send a short burst of information, because Furcadia requires me to constantly receive/send data, possibly for as long as days, if you wish to keep your character connected that long. I might just go back to VB, if this is so complicated.. 


Log in to reply