2012년 6월 5일 화요일

TCP 소켓 Connection 타임아웃 주기(윈도우/리눅스 공통) - TCP Socket Connection Timeout


TCP 소켓통신에서 서버로 연결할때 상대서버가 동작하지 않거나 연결에 문제가 생겼을때
connect 함수에서 블러킹이 걸려 한참 동안 빠져나오지 못할때가 있다.
이런 현상을 방지하려면 소켓을 non-blocking 으로 만든다음 타임아웃을 주고 connect 후
select 함수에서 연결결과를 감지하면 된다.

 #ifdef WIN32  
   
 #include <WinSock2.h>  
 #include <ws2tcpip.h>  
   
 #define closeSocket     closesocket  
 #define EWOULDBLOCK     WSAEWOULDBLOCK  
 #define EINPROGRESS WSAEWOULDBLOCK  
 #define EINTR          WSAEINTR  
     
 #else  
   
 #include <sys/socket.h>  
 #include <netinet/in.h>  
 #include <netinet/tcp.h>  
 #include <arpa/inet.h>  
 #include <unistd.h>  
 #include <fcntl.h>  
 #include <errno.h>  
   
 #define closeSocket               close  
 #define WSAGetLastError()     errno  
   
 #include <ctype.h>  
 #include <stdlib.h>  
 #endif  
   
 #ifdef WIN32  
 #define WS_VERSION_CHOICE1 0x202/*MAKEWORD(2,2)*/  
 #define WS_VERSION_CHOICE2 0x101/*MAKEWORD(1,1)*/  
 int initializeWinsockIfNecessary(void) {  
      /* We need to call an initialization routine before  
      * we can do anything with winsock. (How fucking lame!):  
      */  
      static int _haveInitializedWinsock = 0;  
      WSADATA     wsadata;  
   
      if (!_haveInitializedWinsock) {  
           if ((WSAStartup(WS_VERSION_CHOICE1, &wsadata) != 0)  
                && ((WSAStartup(WS_VERSION_CHOICE2, &wsadata)) != 0)) {  
                     return 0; /* error in initialization */  
           }  
           if ((wsadata.wVersion != WS_VERSION_CHOICE1)  
                && (wsadata.wVersion != WS_VERSION_CHOICE2)) {  
                     WSACleanup();  
                     return 0; /* desired Winsock version was not available */  
           }  
           _haveInitializedWinsock = 1;  
      }  
   
      return 1;  
 }  
 #else  
 #define initializeWinsockIfNecessary()     1  
 #endif  
    
 int makeSocketNonBlocking(int sock)  
 {  
 #ifdef WIN32  
      unsigned long arg = 1;  
      return ioctlsocket(sock, FIONBIO, &arg) == 0;  
 #else  
      int curFlags = fcntl(sock, F_GETFL, 0);  
      return fcntl(sock, F_SETFL, curFlags|O_NONBLOCK) >= 0;  
 #endif  
 }  
   
 static int reuseFlag = 1;  
   
 int setupStreamSock(short port, int makeNonBlocking)  
 {  
      if (!initializeWinsockIfNecessary()) {  
           socketErr("[%s] Failed to initialize 'winsock': ", __FUNCTION__);  
           return -1;  
      }  
   
      int newSocket = socket(AF_INET, SOCK_STREAM, 0);  
      if (newSocket < 0) {  
           DPRINTF("%s:%d\n",__FUNCTION__,__LINE__);  
           socketErr("[%s] unable to create stream socket: ", __FUNCTION__);  
           return newSocket;  
      }  
  
      if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEADDR,  
           (const char*)&reuseFlag, sizeof reuseFlag) != 0) {  
                socketErr("[%s] setsockopt(SO_REUSEADDR) error: ", __FUNCTION__);  
                closeSocket(newSocket);  
                return -1;  
      }  
 
      struct sockaddr_in c_addr;  
      memset(&c_addr, 0, sizeof(c_addr));  
      c_addr.sin_addr.s_addr = INADDR_ANY;  
      c_addr.sin_family = AF_INET;  
      c_addr.sin_port = htons(port);  
   
      if (bind(newSocket, (struct sockaddr*)&c_addr, sizeof c_addr) != 0) {  
           socketErr("[%s] bind() error (port number: %d): ", __FUNCTION__, port);  
           closeSocket(newSocket);  
           return -1;  
      }  
   
      if (makeNonBlocking) {  
           if (!makeSocketNonBlocking(newSocket)) {  
                socketErr("[%s] failed to make non-blocking: ", __FUNCTION__);  
                closeSocket(newSocket);  
                return -1;  
           }  
      }  
   
      return newSocket;  
 }  
   
 int connectToServer(char *svrIP, int svrPort)  
 {  
      int sock = setupStreamSocket(0, 1);  
      int ret, err;  
   
      struct sockaddr_in svr_addr;  
      memset(&svr_addr, 0, sizeof(svr_addr));  
      svr_addr.sin_addr.s_addr = inet_addr(svrIP);  
      svr_addr.sin_family = AF_INET;  
      svr_addr.sin_port = htons(svrPort);  
   
      fd_set set;  
      FD_ZERO(&set);  
      timeval tvout = {2, 0};  // 2 seconds timeout  
   
      FD_SET(sock, &set);  
   
      if ((ret=connect(sock, (struct sockaddr *)&svr_addr, sizeof(svr_addr))) != 0) {  
           err = WSAGetLastError();  
           if (err != EINPROGRESS && err != EWOULDBLOCK) {  
                printf("connect() failed : %d\n", err);  
                return -1;  
           }  
           if (select(sock+1, NULL, &set, NULL, &tvout) <= 0) {  
                printf("select/connect() failed : %d\n", WSAGetLastError());  
                return -1;  
           } 
           err = 0;
	   socklen_t len = sizeof(err);
	   if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&err, &len) < 0 || err != 0 ) {
		printf("getsockopt() error: %d\n", err);
		return -1;
	   }
      }  
      return 0;  
 }  

c# 소스코드

댓글 없음:

댓글 쓰기