Check for an Internet Connection with Async DNS
How to determine you're system is connected without hanging your script.
A Simple Check
An Asynchronous Check
A Complete Function
How to Run the Examples
To run any of the examples that are shown below, open a text editor and create a REBOL header line such as:
REBOL [Title: "Example"]
Then simply cut the example text and paste it after this line. Save the text file and give it a name, such as example.r. Then run the file just as you would run any REBOL file.
The connected? function returns true if your system is currently capable of accessing the Internet. For example, try:
If it prints TRUE, then your system has been configured to access the Internet.
Unfortunately, this check means very little, because of the way the Internet works. For instance, you may be configured to access the Internet, but your system may not be connected physically to the net. For example, you may have unplugged your laptop computer from its phone line or ethernet connection.
It is also possible that you are connected to a local network that does not have access to the Internet. Or, perhaps it uses a proxy server, and that server is down. Or, a router is down, your ISP is down, the backbone is down, or the site you need to connect to is down. There are many possibilities. That's why the connected? function does not mean much.
A Simple Check
The easiest way to find out if your system is connected to the Internet is to actually read something from the network. An easy way to do this is to read a web page. For example:
will tell if you are connected. So, you could write:
either error? try [read http://www.rebol.com][ print "Not connected!" ][ print "Connected!" ]
The problem is, if you are not connected, then the READ will hang until TCP times out. This will cause your REBOL user interface to hang as well. That can be a frustrating situation for users.
You can shorten the timeout period with a line such as:
But, now you have another problem. You may have a slow connection or the route to the HTTP host is slow, and the connection times out, even though you have access to it. Also, just because you have a fast connection to the Internet, does not mean that someone else running your script has a fast connection.
An Asynchronous Check
There is no perfect solution to the connection problem because the problem is inherent in the design of the Internet. You don't know if you can connect until you actually connect.
However, here is a solution that works most of the time. It accesses the network by performing a DNS lookup for a host computer on the network. The lookup is performed in an asynchronous manner, allowing your code to continue to execute while the lookup is happening.
The code starts by opening an asynchronous DNS port. Here is the code:
port: open/no-wait dns:///async
This operation depends on TCP/IP functionality that may or may not be supported by some operating systems. (Fortunately, most support it.) It is a good idea put an error catch around it:
if error? try [port: open/no-wait dns:///async][ alert "Async DNS not supported by your OS." ]
In the case of an error, you could also switch to doing a normal DNS lookup. That way it will work on all REBOL platforms. This will be shown in a example below.
Next, send the DNS the name of the host that you want to lookup. For example:
insert port "www.rebol.com"
Note that this is not a URL, it is just a host name string.
Now your code can check the response from the DNS server without hanging. This is done with a line such as:
if not error? try [ip: copy port] [print ip]
The error check must be performed because the port may not yet have the response from the server. The COPY function would cause an error in that case. (Note that this is a different result than is produced by other /no-wait ports in REBOL. In the case of async DNS, the COPY causes an error rather than returning an empty string.)
Your code can also wait for the response from the DNS server. If you add a timeout to the wait, your program will get control often enough to do other operations. For example, here is a loop that waits 60 seconds, but wakes up after every second:
repeat n 60 [ print n if not error? try [ip: copy port] [break] wait [port 1] ]
Note that during the WAIT, the user interface will continue to respond to user actions.
This wait loop can be used to update a progress bar or check for a close event. Here is an example that displays a progress bar and allows the user to cancel the operation:
view/new layout [ vh2 "Connecting..." pb: progress 200x16 button "Cancel" [close port quit] ] repeat n 60 [ pb/data: n / 60 show pb if not error? try [ip: copy port] [break] wait [port 1] ] unview
When you are done with the lookup operation, be sure to close the port:
Otherwise the port will remain open.
A Complete Function
Here is a complete function for performing an asynchronous lookup. Note that if the async DNS open fails, it will fall back to normal DNS operation.
find-host: func [host /local port pb ip] [ if error? try [port: open/no-wait dns:///async][ return read join dns:// host ] insert port host view/new layout [ vh2 "Connecting..." pb: progress 200x16 button "Cancel" [unview close port port: none] ] ip: none repeat n 60 [ pb/data: n / 60 show pb if not error? try [ip: copy port] [break] wait [port 1] if none? port [return none] ] close port unview ip ]
If the user clicks on the cancel button, the port is closed and the port variable is set to NONE. This condition is detected after the WAIT by checking the port variable.
Here is an example of using the function:
if find-host "www.rebol.com" [print "it's there"] if not find-host "www.rrebol.com" [print "Not there."]