전체 과정 요약
socket 을 nonblocking 설정하고 connect() 과정이 끝나면 다시 blocking 으로 설정한다
- fcntl() 을 이용해 blocking 또는 nonblocking 설정이 가능하다
순서
- fcntl(sock, F_GETFL) : 현재 fcntl flag 얻기
- (flags | O_NONBLOCK) : 현재 fcntl flag 에 O_NONBLOCK flag 를 추가하여 nonblocking fd 로 설정
- fcntl(sock, F_SETFL, flags) : 소켓에 fcntl flag 설정
- connect(sock, res->ai_addr, res->ai_addrlen) : nonblocking fd 이기 때문에 바로 return 함
- select(sock+1, NULL, &writefds, NULL, &timeout) : 0 보다 작거나 같으면 timeout
- getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&err, &len) : connection 과정의 에러 확인
- (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>
예제 코드
{
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;
}
댓글 없음:
댓글 쓰기