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# 소스코드
댓글 없음:
댓글 쓰기