2014-09-28 63 views
0

有兩個套接字服務器,一個是不是始終打開的主服務器,另一個是備份服務器。切換回阻塞套接字後無法連接到套接字

我的程序將嘗試使用非阻塞連接來連接主服務器(以便可以應用超時值),如果失敗,它將使用阻塞連接來連接備份服務器。

然而第二連接函數將返回在大部分時間「無效參數」錯誤代碼:正如我在上面的代碼註釋

#define SERVER_URL "example.com" 
#define SERVER_PORT_PRIMARY "1234" 
#define SERVER_PORT_BACKUP "5678" 

struct addrinfo *result = NULL; 
struct addrinfo hints; 

ZeroMemory(&hints, sizeof(hints)); 
hints.ai_family = AF_INET; 
hints.ai_socktype = SOCK_STREAM; 
hints.ai_protocol = IPPROTO_TCP; 

if (getaddrinfo(SERVER_URL , SERVER_PORT_PRIMARY , &hints, &result) != 0) { 
    WSACleanup(); 
    return; 
} 

SOCKET socketClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
if (socketClient == SOCKET_ERROR){ 
    WSACleanup(); 
    return; 
} 

//set the socket in non-blocking 
unsigned long iMode = 1; 
iResult = ioctlsocket(socketClient, FIONBIO, &iMode); 
if (iResult != NO_ERROR){ 
    closesocket(socketClient); 
    WSACleanup(); 
    return; 
} 

if (connect(socketClient, result->ai_addr, (int)result->ai_addrlen) == SOCKET_ERROR){ 
    if (WSAGetLastError() != WSAEWOULDBLOCK){ 
     closesocket(socketClient); 
     WSACleanup(); 
     return; 
    } 
} 

//switch it back to blocking socket 
iMode = 0; 
iResult = ioctlsocket(socketClient, FIONBIO, &iMode); 
if (iResult != NO_ERROR){ 
    closesocket(socketClient); 
    WSACleanup(); 
    return; 
} 
fd_set Write, Err; 
FD_ZERO(&Write); 
FD_ZERO(&Err); 
FD_SET(socketClient, &Write); 
FD_SET(socketClient, &Err); 
TIMEVAL Timeout; 
Timeout.tv_sec = 10; 
Timeout.tv_usec = 0; 
select(0, NULL, &Write, &Err, &Timeout); 
if (FD_ISSET(socketClient, &Write) == false){ 
    //unable to connect to primary server within 10s, try to connect backup server 
    ZeroMemory(&hints, sizeof(hints)); 
    hints.ai_family = AF_INET; 
    hints.ai_socktype = SOCK_STREAM; 
    hints.ai_protocol = IPPROTO_TCP; 
    if (getaddrinfo(SERVER_URL , SERVER_PORT_BACKUP, &hints, &result) != 0) { 
     closesocket(socketClient); 
     WSACleanup(); 
     return; 
    } 
    iResult = connect(socketClient, result->ai_addr, (int)result->ai_addrlen); 
    if (iResult == SOCKET_ERROR){ 
     int a = WSAGetLastError(); ///<----Problem here, a == WSAEINVAL (Invalid argument) 
     closesocket(socketClient); 
     WSACleanup(); 
     return; 
    } 
} 

,第二個「連接」的電話將在大部分時間和WSAGetLastError返回SOCKET_ERROR ()返回WSAEINVAL(無效參數)。

如果我刪除了非阻塞代碼,它將連接沒有錯誤。那麼我的代碼有什麼問題?

回答

0

您必須將套接字置於非阻塞模式,而connect()仍處於忙連接狀態。如果connect()返回WSAEWOULDBLOCK,則只需撥打select(),並且您還需要檢查返回值select()。您也在泄漏由getaddrinfo()返回的內存。

嘗試一些更喜歡這個:

int connectTo(SOCKET s, const char *host, const char *port) 
{ 
    struct addrinfo *result = NULL; 
    struct addrinfo hints; 

    ZeroMemory(&hints, sizeof(hints)); 
    hints.ai_family = AF_INET; 
    hints.ai_socktype = SOCK_STREAM; 
    hints.ai_protocol = IPPROTO_TCP; 

    int ret = getaddrinfo(host, port, &hints, &result); 
    if (ret != 0) { 
     return ret; 
    } 

    if (connect(socketClient, result->ai_addr, result->ai_addrlen) != SOCKET_ERROR) { 
     freeaddrinfo(result); 
     return 0; 
    } 

    ret = WSAGetLastError(); 
    freeaddrinfo(result); 

    if (ret != WSAEWOULDBLOCK) { 
     return ret; 
    } 

    fd_set Write, Err; 
    FD_ZERO(&Write); 
    FD_ZERO(&Err); 
    FD_SET(s, &Write); 
    FD_SET(s, &Err); 

    TIMEVAL Timeout; 
    Timeout.tv_sec = 10; 
    Timeout.tv_usec = 0; 

    ret = select(0, NULL, &Write, &Err, &Timeout); 
    if (ret == SOCKET_ERROR) { 
     return WSAGetLastError(); 
    } 

    if (ret == 0) { 
     return WSAETIMEDOUT; 
    } 

    if (FD_ISSET(s, &Err)) { 
     u_long err; 
     if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&err, sizeof(err)) == SOCKET_ERROR) { 
      return WSAGetLastError(); 
     } 
     return (int) err; 
    } 

    return 0; 
} 

然後,你可以這樣做:

#define SERVER_URL "example.com" 
#define SERVER_PORT_PRIMARY "1234" 
#define SERVER_PORT_BACKUP "5678" 

SOCKET socketClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
if (socketClient == SOCKET_ERROR) { 
    WSACleanup(); 
    return; 
} 

//set the socket in non-blocking 
u_long iMode = 1; 
iResult = ioctlsocket(socketClient, FIONBIO, &iMode); 
if (iResult == SOCKET_ERROR) { 
    closesocket(socketClient); 
    WSACleanup(); 
    return; 
} 

if (connectTo(socketClient, SERVER_URL, SERVER_PORT_PRIMARY) != 0) { 
    if (connectTo(socketClient, SERVER_URL, SERVER_PORT_BACKUP) != 0) { 
     closesocket(socketClient); 
     WSACleanup(); 
     return; 
    } 
} 

//switch it back to blocking socket 
iMode = 0; 
iResult = ioctlsocket(socketClient, FIONBIO, &iMode); 
if (iResult == SOCKET_ERROR) { 
    closesocket(socketClient); 
    WSACleanup(); 
    return; 
} 

// communicate with the server as needed ...