[racket] tcp exceptions and connection reestablishment...

From: Neil Van Dyke (neil at neilvandyke.org)
Date: Wed Jun 20 10:50:42 EDT 2012

Rüdiger Asche wrote at 06/20/2012 09:21 AM:
> I have tried call-with-exception-handler and handlers in different 
> variations but haven't been able to produce code that allows me to 
> gracefully return to reestablishing the connection. Does anyone have a 
> code snippet that helps me?

This is quick off-the-cuff suggestion to consider, not the only way to 
do this (nor any kind of canonical recipe for doing this sort of thing, 
nor necessarily the best way):

(let loop-connect ()
   (log-debug "Attempting connection..")
   (with-handlers ((exn:fail:network?
                    (lambda (exn)
                      (log-warning (string-append "network error: "
                                                  (exn-message exn)))
                      (let ((sleep-seconds (+ 3 (random 3))))
                        (log-debug
                         (format
                          "sleeping for ~A seconds before reconnect attempt"
                          sleep-seconds))
                        (sleep sleep-seconds)))))
    (let-values (((inport outport)
                  (tcp-connect somehostname somehostport)))
      (let loop-while-connected ()
        ....
        (read-something inport)
        ....
        (write-something outport)
        ...
        (loop-while-connected))))
   (loop-connect))

If you're hardcore: might want to also catch "exn:fail:network" within 
the "letrec-values", too, and try to close ports (perhaps in a different 
thread to close, or start a new thread for the new connection?) if not 
already closed, and then perhaps re-raise the exception so the handler 
above (within "loop-connect") attempts a new connection.  As you know, 
but I'll mention it for the record: trying to close the ports might be a 
good idea, especially if your TCP stack is not great about timing them 
out, but if you have particular failure scenarios in an embedded system 
or similar, might help to test the scenarios and look at the IP traffic 
and how many times you fail to reconnect.  You might also want to 
increase your retry delay on failures, in case you can flood the network 
or exhaust open ports on client or server.  Say, each failure increments 
a counter by 2, and a successful read-write loop decrements the counter 
if greater than 1, then you use that number as the exponent in delay, or 
similar.  (This complexity is not specific to Racket; the 
Racket-specific part is that you can catch a network error with 
"with-handlers".)  BTW, if you have nothing else running on this 
processor, you might as well get in a GC cycle or two before sleeping.  
There are probably more details that could be covered if needed for this 
app, but one would have to work through it more closely.

Side comment on the internal-"define" controversy: I left the 
named-"let"s without arguments in this example, but one might find 
arguments to put in them by the time the program is finished and robust, 
such as for retry delay info, buffer management info, etc.  Converting 
the named-"let"s to internal-"define", if the reader is so inclined, is 
left as an exercise for the reader.  But perhaps internal-"define" 
people can see some of the appeal of named-"let" in this case.

Neil V.


Posted on the users mailing list.