views:

461

answers:

5

I need the fastest way to see if an ip address is reachable. On each ip address theres a server listening on a specific port so let me know if your method is about to find if a server is listening on a port.

The reason for this is that suppose I have 10 ip addresses with 10 server listening on port 101 on each ip address. I want my client be able to find a Reachable ip address and connect to it as fast as he can(I don't want him to wait 30 seconds to find out if a ip address is reachable and then try the next ip address in the list)

May be it has to be done in treads simultaneously.

A: 

Try to open a socket using the connect() function from a BSD socket library. Its as fast as you can get, if the port is not open it wont respond to the SYN packet.

The key issue, as you realize, is tying up a thread which has to wait for a SYN-ACK before it can do anything else. Luckily, you do not need threads to parallelise IO anymore; however programming asynchronous operations can be subtle; therefore, I would recommend the libevent library for dispatching TCP/IP connect operations in parallel... since the kernel is doing the heavy lifting you only need one thread to do it on. You could probably do 100's or thousands of connects a second using libevent -- depending on your network hardware.

Another alternative is Boost::ASIO which is more complicated. But since you are using C++ might suite you better.

Hassan Syed
+1  A: 

Typically randomly trying to connect with a short time out is sufficient.

Reachability is not very important, the fact that a route exists from you to the server isn't what matters, whether you can connected to said server is. Typically your own code will run just as fast as any other reachability method you can devise.

If you are having problems with it taking too long then try adjusting the length of your response, or having tighter timeouts.

Simple algorithm:

shuffle IP addresses
foreach IP in addresses
    attempt connect
    if succeed then
       break
Guvante
+1: for the shuffle procedure.
jldupont
-1 because his question is about the issue of parallel operations and not psuedocode of how to make a single thread open a socket (which he has demonstrated he knows how to do).
Hassan Syed
-1, I second Hassan, your post is not useful
frunsi
@Downvotes: I am saying that his question is flawed. He himself gave the performance requirements, <30 sec to connect to a random port. By specifying 1-2 sec timeout, my method satisfies that without needlessly opening connections to every possible server. Just because he thought he wanted to open connections in parallel doesn't mean that is the best way to do it.
Guvante
@Guvante: Your method requires very short timeouts, which in some cases may lead to failed connections that could be successful when the timeout was some milliseconds longer. This is especially important for "new" routes through a large network like the internet.
frunsi
@frunsi: With a 3000 ms timeout you meet his performance requirement. Everything is relative.
Guvante
+2  A: 

While you can quickly determine that an IP is reachable, your problem is determining that an IP is not reachable. The reason why is that you can't always definitively determine that an IP is not reachable. While there are some conditions where you will be given an affirmative notice that the IP is not reachable, usually your code will just not hear an answer and after waiting for some amount of time, your code will assume the IP is not reachable.

The problem in deciding the timeout is network topology. If you have a large topology (such as the Internet), you will need a large timeout to deal with potentially high latencies if you try to connect to an IP that is 'far' away.

From your description, the best idea would be to try to connect to all servers at the same time and use the first one that accepts the connection. You can use threads or you can use non-blocking sockets. In a non-blocking connect, the connect call returns immediately and you then use select to efficiently determine when the connect call has completed (either successfully or with an error).

R Samuel Klatchko
Exactly what I was looking for... If you how it works implementation is easy
EBAGHAKI
A: 

Below is code that you can use to create outgoing connections concurrently.

Iterate over your IPs and SpawnOutgoing connections in your loop.
Each connection conn_t* is posted as LParam in a window message - concurrently.

You should monitor the messages and save somewhere only the first connection - ignore (delete) other connections.

#define MSG_NEW_CONNECTION  (WM_USER + 1)

struct conn_t {
    SOCKET       s;
    sockaddr_in  server;
};

static
UINT OutgoingWorker(LPVOID param)
{
  // `param` holds "conn_t*"
  assert(param);
  if (!param) return 0;
  conn_t* c = (conn_t*)param;
  if (SOCKET_ERROR == connect(c->s, (SOCKADDR*)&c->server, sizeof c->server)) {
    closesocket(c->s);
    return 0;
  }
  PostMessage(mainwnd, MSG_NEW_CONNECTION, 0, (LPARAM)c); // <-- mainwnd handle
  return 0;
}

conn_t*
SpawnOutgoing(const char* dest_ip, const USHORT dest_port)
{
  if (!dest_ip) return NULL;
  SOCKET  s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (INVALID_SOCKET == s) {
    return NULL;
  }
  conn_t* c = new conn_t;
  // Create the socket here but connect it in the worker
  memset(&c->server, 0, sizeof sockaddr_in);
  c->server.sin_family = AF_INET;
  c->server.sin_addr.s_addr = inet_addr(dest_ip);
  c->server.sin_port = htons(dest_port);
  c->s = s;

  CreateThread(0, 0, OutgoingWorker, c);
  return c;
}
Nick D
Perfect solution and Perfect implementation, thanks :)
EBAGHAKI
imperfect solution, why on earth are you creating threads ? We have stepped out of the caves and have discovered how to farm dude ! read up on async programming.
Hassan Syed
@Hassan Syed, I'm aware of async programming. Multiplexed or nonmultiplexed design. Blocking or nonblocking sockets, etc. What approach we'll choose depends on the project.
Nick D
@EBAGHAKI, thanks for your kind words.
Nick D
+2  A: 

You could use threads, but it would introduce unnecessary overhead for this task.

Use non-blocking sockets here (and avoid non-blocking sockets wherever you can! really, but they make sense in this case):

// initiate tcp connects...
for( each of your target host+port pairs ) {
    int socket = socket( AF_INET, SOCK_STREAM );
    ...
#ifdef WIN32
    unsigned long mode = 1;
    ioctlsocket( socket, FIONBIO, &mode );
#else
    int value = fcntl( socket, F_GETFL, 0 );
    fcntl( socket, F_SETFL, value | O_NONBLOCK );
#endif
    ...
    int code = connect( s, target, ... );
    if( code == 0 ) { /* ok, this one did connect, DONE */ }
    // now remember the socket in a list ...
}
// now find the first socket that was successfully connected
while( still not found ) {
    struct timeval tval;
    memset( &tval, 0, sizeof(tval) );
    fd_set write_set, error_set;
    int largest_socket = 0;
    // add sockets to write and error set, and determine largest socket no.
    for( each socket that did not fail until now ) {
        FD_SET( socket, write_set );
        FD_SET( socket, error_set );
        if( socket > largest_socket ) largest_socket = socket;
    }
    // now use select to wait until something happens on the sockets
    int code = select( largest_socket+1, NULL, &write_set, &error_set, &tval );
    if( code < 0 ) { something went terribly wrong }
    else {
        for( each socket that did not fail until now ) {
            if( FD_ISSET( socket, write_set ) ) { you've found it! DONE }
            if( FD_ISSET( socket, error_set ) ) { error, remove this socket from list (for next iteration) }
        }
    }
}

Check documentation for connect and select for more details!

frunsi
I wish i could accept two answers as solutions
EBAGHAKI
You should have chosen this answer over the other one.... it contains FAR more information.... have you even looked at mine ?
Hassan Syed