[racket] tcp exceptions and connection reestablishment...
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.