I have following problem: remote hostname resolves to multiple addresses and I want to try all one by one until connection is succeeded. But: 1. current policy when multiple addresses given for hostname - try random one. 2. no API for resolving addresses.
I can try to propose patch for this problem but in which direction I should go: provide resolving API or involve sequential trying of addresses?
Hi, Alexey
Good question. I think your problem can be divided into two sub-problems: 1) how to get multiple addresses (when possible) behind a single hostname, 2) how to do SOCKET-CONNECT with the logic (try all one by one until connection is succeed).
For the first sub-problem, I'm afraid that not all CL platform provided such a interface, but for those supported platforms (at least CMUCL, SBCL and LispWorks), you can use a internal USOCKET function called USOCKET::GET-HOSTS-BY-NAME, i.e.
* (usocket::get-hosts-by-name "www.google.com")
(#(72 14 203 104) #(72 14 203 99))
Once you got multiple addresses, you can feed them to SOCKET-CONNECT and do whatever you want.
I think I can patch USOCKET to make USOCKET::GET-HOSTS-BY-NAME return multiple addresses on all platforms, but this is not easy, will need time.
And for the second sub-problem. I think maybe you can try to use some CL condition programming technologies to achieve your goal: some kind of mixup of RESTART-CASE and HANDLER-BIND. However, current USOCKET didn't do condition work well: when SOCKET-CONNECT failed to connect, there's no uniform condition class signaled. I'm working on that, but if you can investigate on this and show me a working example for just any of your favorite CL platforms, I'd like to accept it as a new feature (once other related work was done)
Regards,
Chun Tian (binghe)
在 2011-3-16,22:13, Alexey Martynov 写道:
I have following problem: remote hostname resolves to multiple addresses and I want to try all one by one until connection is succeeded. But:
- current policy when multiple addresses given for hostname - try random one.
- no API for resolving addresses.
I can try to propose patch for this problem but in which direction I should go: provide resolving API or involve sequential trying of addresses?
-- Alexey Martynov
usocket-devel mailing list usocket-devel@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/usocket-devel
Here's what I would consider one idiomatic way to try all addresses on using SB-BSD-SOCKETS.
(defun connect-to-host (socket host port) (let ((addresses (host-ent-addresses (get-host-by-name host)))) (tagbody :connect (let ((addr (pop addresses))) (handler-bind ((socket-error (lambda (e) (when addresses (go :connect))))) (socket-connect socket addr port))))))
Cheers,
-- Nikodemus
Hi, Nikodemus
Thanks very much, your code give me a very good "template".
I think maybe such code should live outside of USOCKET library itself, because the connection logic various among different applications. For example, suppose there's a hostname which can be resolved into more than 10 different addresses, but the network application want to try at most 3 of those addresses before signal a error. (the theory may be this: if 3 of 10 was down, all was down, no time waste to try the other 7)
So, my job (as a USOCKET maintainer) is to make sure SOCKET-CONNECT on all supported CL platforms could report the same USOCKET-defined error type (i.e. USOCKET:CONNECTION-REFUSED-ERROR), so that something like following code could be written in user code:
(defun socket-connect-2 (host port) (let ((addresses (usocket::get-hosts-by-name host))) (tagbody :connect (let ((address (pop addresses))) (handler-bind ((usocket:connection-refused-error #'(lambda (c) (declare (ignore c)) (when addresses (go :connect))))) (usocket:socket-connect address port))))))
Regards,
Chun Tian (binghe)
在 2011-3-17,16:02, Nikodemus Siivola 写道:
Here's what I would consider one idiomatic way to try all addresses on using SB-BSD-SOCKETS.
(defun connect-to-host (socket host port) (let ((addresses (host-ent-addresses (get-host-by-name host)))) (tagbody :connect (let ((addr (pop addresses))) (handler-bind ((socket-error (lambda (e) (when addresses (go :connect))))) (socket-connect socket addr port))))))
Cheers,
-- Nikodemus
Hi Chun, Alexey,
2011/3/17 Chun Tian (binghe) binghe.lisp@gmail.com:
Hi, Alexey
Good question. I think your problem can be divided into two sub-problems: 1) how to get multiple addresses (when possible) behind a single hostname, 2) how to do SOCKET-CONNECT with the logic (try all one by one until connection is succeed).
Now that USOCKET has UDP sockets and this question comes up, it's probably time for me to explain how I envisioned the future of USOCKET.
In my mind, early on with USOCKET when I didn't know it was feasible to create UDP support on all platforms, I envisioned a USOCKET library (which looked very much like what we have now) and a UUDP library. The latter would implement the name resolution routines (a small DNS client) missing in so many platforms, much like Allegro implements one.
The above is also the reason I never exported the name resolution routines from USOCKET and implemented USOCKET to take all forms of IPv4 addresses known at that time.
Hope that adds to the discussion.
Bye,
Erik.