views:

331

answers:

5

I have a client application that should send notify messages to an optional server application. The client should not be influenced by whether the server appliction exists or not. It should try to connect to the server application and send the notify message and in case of errors it should just silently ignore all errors and continue work.

I'm using Indy for the TCP communication but all attempts to avoid error messages showing up (i.e. when ther server application closes while connected to the client) failed.

Is there a way to really make this robust?

Current code looks like this:

if (not Client.Connected) then
  begin
  Client.Host := ServerName;
  Client.Port := ServerPort;
  Client.ConnectTimeout := ConnectTimeout;
  try
    Client.Connect;
  except
    Exit;
  end;
  end
try
  Client.IOHandler.WriteLn ('NOTIFYCHANGE "' + Param + '"');
  Client.IOHandler.WriteBufferFlush;
except
  try
    Client.Disconnect;
  except
    { ignore errors while disconnecting }
  end;
  try
    Client.Connect;
  except
    { ignore errors while connecting }
  end;
end;
+2  A: 

Do you really get error messages from your program? It's common that when debugging that the debugger detects exceptions and interrupts your program, and some people confuse the debugger's message for a message from their own program. Are you sure that's not the case here? I've written about this situation before. Here's the summary of ways to avoid it:

  • Use "advanced breakpoints" to disable the debugger's exception-interception behavior for a region of code.
  • Configure the debugger to ignore certain classes of exceptions. (This is especially common to do with Indy since it tends to throw many exceptions.)
  • Set the debugger to never interrupt the program on exceptions.
  • Disable integrated debugging altogether.

If the message really is coming from your program and not the debugger, then go back and use the debugger to figure out where the message is coming from. When the message appears, pause the program and look at the call-stack window to find the area of your code that's displaying the message, since it clearly isn't in the code you've shown. The code you've shown is thoroughly suppressing (not handling) all exceptions, even the ones that aren't related to Indy, such as EAccessViolation and EOutOfMemory.

Rob Kennedy
Hi Rob, thanks for the answer. The message does appear when not running the application in the debugger. I fixed it by connecting and disconnecting on every notification (doesn't happen too often). It seems to be pretty robust now.
Smasher
A: 

If possible, consider using UDP. It's "connectionless", so sender will just send the message and receiving application will receive it if it is listening to the port. However the sender doesn't get any confirmation about the delivery unless the server sends some kind of acknowledgement.

Harriv
I want to use an existinc communication protocol and therefore want to stick with TCP. Thanks anyway.
Smasher
Indy's had UDP since forever. Or do you mean by "existing communication protocol" that you wanted to fit in with what your application's already doing?
Frank Shearar
A: 

Harriv response is OK, you are describing the behaviour of an UDP connection. I'm using in a similar situation.

Francis Lee
A: 

For simple TCP communications task I've used Synapse package, it's not as bloated as Indy and feels "cleaner" to use.

From my recent code:

procedure SendMessage(m: string);
var
  sock : TTCPBlockSocket;
  response : string;
begin
  Sock := TTCPBlockSocket.Create;
  try
    Sock.SetTimeout(200);
    Sock.Connect(PrinterServerAddr, IntToStr(PrinterServerPort));

    Sock.SendString(m);
    response := Sock.RecvString(1000);
  finally
    Sock.Free;
  end;
end;

..
try
  SendMessage(NewMessage);
except
 //..handle exception..
end;

Wrap that inside TThread if you want to avoid blocking your current thread.

Harriv
A: 

You don't say whether you're using Indy 9 or 10. I use 9, myself, so the below assumes this.

Why not just this?:

procedure Send(Target: String; Port: Integer; S: String);
var
  C: TIdTcpClient;
begin
  C := TIdTcpClient.Create(nil);
  try
    try
      C.Host := Target;
      C.Port := Port;
      C.Connect;
      try
        C.Write(S);
      finally
        C.Disconnect;
      end;
    except
      // Ignore Indy exceptions
      on EIdException do;
    end;
  finally
    C.Free;
  end;
end;

It's easy enough to turn this into a procedure that uses a pre-existing TIdTcpClient.

Frank Shearar