2017년 3월 6일 월요일

socket - connect() 함수 timeout 설정

전체 과정 요약


socket 을 nonblocking 설정하고 connect() 과정이 끝나면 다시 blocking 으로 설정한다
  • fcntl() 을 이용해 blocking 또는 nonblocking 설정이 가능하다
timeout 확인은 select() 의 timeout 을 이용하고 connect() 동작의 성공 여부는 getsockopt() 를 이용해 SO_ERROR 값을 얻어 확인한다


순서

  1. fcntl(sock, F_GETFL) : 현재 fcntl flag 얻기
  2. (flags | O_NONBLOCK) : 현재 fcntl flag 에 O_NONBLOCK flag 를 추가하여 nonblocking fd 로 설정
  3. fcntl(sock, F_SETFL, flags) : 소켓에 fcntl flag 설정
  4. connect(sock, res->ai_addr, res->ai_addrlen) : nonblocking fd 이기 때문에 바로 return 함
  5. select(sock+1, NULL, &writefds, NULL, &timeout) : 0 보다 작거나 같으면 timeout
  6. getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&err, &len) : connection 과정의 에러 확인
  7. (flags & ~O_NONBLOCK) : 현재 fcntl flag 에 O_NONBLOCK flag 를 취소하여 다시 blocking fd 로 설정

include 한 파일들


#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>



예제 코드

static int connect_with_timeout(int sock, struct addrinfo * res, unsigned long timeout_milli)
{
    int err;
    socklen_t len;
    fd_set writefds;
    struct timeval timeout;
    int flags;
    FD_ZERO(&writefds);
    flags = fcntl(sock, F_GETFL);
    flags = (flags | O_NONBLOCK);
    if (fcntl(sock, F_SETFL, flags) != 0)
    {
        perror("fcntl() error\n");
        return -1;
    }
    if (connect(sock, res->ai_addr, res->ai_addrlen) != 0)
    {
        if (errno != EINPROGRESS)
        {
            perror("connect() error\n");
            return -1;
        }
    }
    timeout.tv_sec = timeout_milli / 1000;
    timeout.tv_usec = (timeout_milli % 1000) * 1000;
    FD_SET(sock, &writefds);
    if (select(sock+1, NULL, &writefds, NULL, &timeout) <= 0)
    {
        perror("connection timeout\n");
        return -1;
    }
    len = sizeof(err);
    getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&err, &len);
    if (err)
    {
        perror("fcntl() error\n");
        return -1;
    }
    fcntl(sock, F_GETFL);
    flags = (flags & ~O_NONBLOCK);
    if (fcntl(sock, F_SETFL, flags) != 0)
    {
        perror("fcntl() error\n");
        return -1;
    }
    return 0;
}



예제 프로그램


int main(int argc, char *argv[])
{
    const char * url = "google.com";
    int sockfd = -1, sResult = -1;
    struct addrinfo hints, * res = NULL;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    sResult = getaddrinfo(url, "http", &hints, &res);
    if (sResult < 0)
    {
        perror("getaddrinfo() error %s\n");
        goto exit;
    }

    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (sockfd == -1)
    {
        perror("socket() error");
        goto exit;
    }

    if (connect_with_timeout(sockfd, res, 5000) < 0)
    {
        printf("connect_with_timeout() failed");
        goto exit;
    }

    printf("Successfully connected to '%s' \n", url);

exit:
    if (res)
    {
        freeaddrinfo(res);
    }

    close(sockfd);

    return 0;
}

댓글 없음:

댓글 쓰기