본문 바로가기

Developement/C/C++

리눅스에서 빠르게 TCP socket 서버 찾기.


역시 본문과 상관 없는 구형 AnyStreaming 개발 보드...

 리눅스에서 자신의 IP 영역대에서 (A.B.C.n, n = 1~자기자신~255) 다른 서버가 있는지 찾기 위해서는 여러 방법이 있긴 하겠지만, 대부분 이 방법으로 찾는게 아닐까 합니다.

 아래 코드는 해당 ip 에 TCP socket 이 열려 있는지 찾는 간단한 방법 으로 실제 AnyStreaming Client 에서도 아래와 같은 방법으로 (물론 Windows용으로 바꾼 코드) AnyStreaming 을 찾습니다.

 실제론 FD_ISSET() 이후에 socket 을 다시 blocked 로 바꾼 다음 send() 를 통해 특정 명령이 전달 되는지 까지 검사하면 더 좋을 듯 합니다.

 이 방법은 TCP socket 의 connect() 함수가 blocking 으로 동작 하는것을 감안해서 만든 날림 코드지만 꽤 나쁘지 않은 방법이기도 한 듯 합니다.

 connect 이후에 usleep() 을 준 것은 제가 쓰는 민트 리눅스에서 connect() 에서 return 을 음수로 받는 경우가 생기는데, 이때 errno 가 EINPROGRESS 인 경우가 많고, 이때 잠시 연결을 기다려 줘야 제대로 동작 하기 때문 입니다.

 시스템에 따라 이 부분이 빠져도 되므로 참고 하시기 바랍니다.


#include <sys⁄types.h>
#include <sys⁄stat.h>
#include <sys⁄socket.h>
#include <sys⁄ioctl.h>
#include <fcntl.h>
#include <errno.h>

#include <unistd.h>
#include <net⁄if.h>
#include <netinet⁄in.h>
#include <arpa⁄inet.h>
#include <signal.h>

#include <cstdio>
#include <cstdlib>
#include <cstring>

bool test_tcp_conn( const char* ip, unsigned short port, unsigned wsec )
{
    if ( ( wsec == 0 ) || ( ip == NULL ) )
        return false;

    int             fdSockTest = -1;
    sockaddr_in     addrSvr;
    fd_set          fdset;
    fd_set          fdwset;
    struct timeval  tv;

    tv.tv_sec = wsec;
    tv.tv_usec = 0;

    fdSockTest = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );

    if ( fdSockTest < 0 )
    {
        return false;
    }

    ⁄⁄ Set socket to non-block.
    int flags = fcntl( fdSockTest, F_GETFL, 0);
    flags |= O_NONBLOCK;

    if ( fcntl( fdSockTest, F_SETFL, flags ) < 0 )
    {
        return false;
    }

    memset( &addrSvr, 0, sizeof( addrSvr ) );

    addrSvr.sin_family = AF_INET;
    addrSvr.sin_addr.s_addr = inet_addr( ip );
    addrSvr.sin_port = htons( port );

    int reti = connect( fdSockTest, (sockaddr*)&addrSvr, sizeof(addrSvr) );

    if ( reti < 0 )
    {
        if ( errno != EINPROGRESS && errno != EWOULDBLOCK )
        {
            close( fdSockTest );
            return false;
        }
        else
        {
            usleep( 10000 );
        }
    }

    FD_ZERO( &fdset );
    FD_SET( fdSockTest, &fdset );
    fdwset = fdset;

    if ( select( fdSockTest + 1, &fdset, &fdwset, NULL, &tv) == 0 )
    {
        ⁄⁄ May timed out.
        return false;
    }

    if ( FD_ISSET( fdSockTest, &fdwset ) > 0 )
    {
		close( fdSockTest );
		return true;
    }

    close( fdSockTest );
    return false;
}