使用getsockopt时TCP无块连接失败OptValue 113请注意

时间:2012-04-23 15:49:23

标签: c linux sockets networking tcp

我已经创建了一个示例TCP连接函数。这使用带有超时的非阻塞套接字。代码在我们的实验室中正常工作,但在某些网络中,TCP连接失败并出现错误 115这意味着EINPROGRESS。调试发现getsockopt()后,将iValopt设置为113 我们检查optvalue是否等于零,如果不是则返回失败。

OS:LINUX Suse10

我的问题是:

  1. 代码粘贴在这里吗?
  2. 为什么getsockopt() Opt值设置为113而errno设置为115?
  3. 我也在下面注意到。

    • 客户有多个IP:174.66.45.22,58.68.445.112
    • 服务器有多个IP:174.88.69.33,58.46.22.33
    • 如果我将Bind地址设置为174.66.45.22并连接到174.88.69.33则总是成功。
    • 如果我将Bind地址设置为174.66.45.22并且连接到58.46.22.33将失败。
    • 但是如果我在第一次运行中将Bind地址设置为58.68.445.112并且在第二次运行中我将Bind地址更改为174.66.45.22然后连接到58.46.22.33就是成功!

    你能解释一下原因吗?

    我在这里粘贴的确切代码

    int tcp_sockconnect_linux(int iSocket, const struct sockaddr* pstSockAddr, unsigned int uiSockAddrLen, unsigned int uiConnectionTimeout)
    {
        int iRet  = 0;
        //int iValopt = 0;
        int iLength = 0; 
        struct timeval stTv; 
        fd_set write_fds;
    
        (void)fcntl((int)(long)iSocket, F_SETFL,  O_NONBLOCK);
    
        iRet = connect((int)(long)iSocket, (struct sockaddr*) pstSockAddr, uiSockAddrLen); 
    
        if (iRet < 0)
        { 
            if (errno == EINPROGRESS) 
            { 
                stTv.tv_sec = uiConnectionTimeout; 
                stTv.tv_usec = 0; 
                FD_ZERO(&write_fds); 
                FD_SET((int)(long)iSocket, &write_fds); 
    
                if (0 < select(((int)(long)(iSocket)) + 1,NULL, &write_fds, NULL, &stTv))
                { 
                    iLength = sizeof(int); 
    
                    if (0 > getsockopt((int)(long)iSocket, SOL_SOCKET,SO_ERROR, (void*)(&iValopt), &iLength))
                    {
                    return -1;
                    }
    
                    if (0 != iValopt)
                    { 
                    return -1;
                    }
    
                    return 2;
                } 
                else
                { 
                return -1; 
                } 
            } 
            else
            { 
            return -1; 
            } 
        } 
    
        return 2;
    }
    
    
    int create_socket(void) 
    {
        int iRet, sock_sd; 
        struct sockaddr_in saServer;
        struct sockaddr_in saLocal;     
    
    #ifdef WIN32
        WSADATA wsaData;
        if(WSAStartup(0x101, &wsaData)) 
        {
            printf("Unable to initialize WinSock library.\n");
        }
    #endif
    
        memset (&saLocal,'\0', sizeof(saLocal));  
        saLocal.sin_family      = AF_INET;
        saLocal.sin_addr.s_addr = inet_addr(strBindAddr);
        saLocal.sin_port        = htons(0);
    
        sock_sd = socket(AF_INET,SOCK_STREAM,0);
        memset (&saServer,'\0', sizeof(saServer));  
        saServer.sin_family      = AF_INET;
        saServer.sin_addr.s_addr = inet_addr (strServerIP); 
        saServer.sin_port        = htons(uiServerPort);  
    
        iRet = bind((unsigned int)sock_sd,(struct sockaddr*) &saLocal,sizeof(saLocal));
    #ifdef WIN32
        if (iRet ==  SOCKET_ERROR)
        {
            printf("\nTCP Bind Failed to LocalIP %s\n",strBindAddr);
        }
    #else
        if (-1 == iRet)
        {
            printf("\nTCP Bind Failed to LocalIP %s\n",strBindAddr);
        }
    #endif
    
    
    
    #ifdef WIN32
        iRet = tcp_sockconnect_win(sock_sd, (struct sockaddr*) &saServer,sizeof(saServer),uiConnectionTimeOut); 
        if (iRet ==  SOCKET_ERROR)
        {
            printf("\nTCP Socket Connect Failed with  ErrorNo = %d  iValopt = %d  LineNo = %d \n",WSAGetLastError(),iValopt,iLineNo);
        }
        else if (2 == iRet)
        {
            printf("\nTCP Socket Connect Success \n");
        }
    #else
        iRet = tcp_sockconnect_linux(sock_sd, (struct sockaddr*) &saServer,sizeof(saServer),uiConnectionTimeOut);
        if(-1 == iRet)
        {
            printf("\nTCP Socket Connect Failed with  ErrorNo = %d   iValopt = %d  LineNo = %d\n",errno,iValopt,iLineNo);
        }
        else if (2 == iRet)
    
        {
            printf("\nTCP Socket Connect Success %d\n",iLineNo);
        }
    
    #endif
    
    
    #ifdef WIN32
        closesocket (sock_sd);
    #else
        close(sock_sd);
    #endif
    
        return 0;
    }
    
    int main ()
    {
        int iRet = 0;
    
        printf ("Enter the Bind Address (Local IP): ");
        scanf("%s" ,&strBindAddr);
    
        printf ("\nEnter the IP Address of Server : ");
        scanf("%s" ,&strServerIP);
    
        printf ("\nEnter the PORT Number of Server : ");
        scanf("%d" ,&uiServerPort);
    
        printf ("\nEnter the Connection timeout in Seconds : ");
        scanf("%d", &uiConnectionTimeOut);
    
        iRet = create_socket();
    
        return 0;
    }
    

1 个答案:

答案 0 :(得分:0)

几天前,即使我面临同样的问题。以下是我可以解决的问题。

在WR Stevens的书中,UNIX®网络编程第1卷,第3版:套接字网络API - 16.4非阻塞连接:白天客户端,它讨论了各种连接的不兼容性,我们可以得到一些答案:

  

正如我们之前所说,各种套接字实现和非阻塞连接存在可移植性问题。首先,在调用select之前,可以完成连接并使数据从对等体到达。在这种情况下,套接字在成功时将是可读写的,就像连接失败一样。我们在图16.11中的代码通过调用getsockopt并检查套接字的挂起错误来处理这种情况。

     

接下来是确定连接是否成功完成,如果我们不能假设可写性是返回成功的唯一方法。已经向Usenet发布了各种解决方案。这些将取代我们对图16.11中getsockopt的调用。

     
      
  1. 致电getpeername而非getsockopt。如果此操作因ENOTCONN而失败,则连接失败,然后我们必须使用getsockopt调用SO_ERROR以获取套接字的待处理错误。

  2.   
  3. 调用read的长度为0.如果read失败,则connect失败,errno read表示原因连接失败。连接成功后,read应返回0.

  4.   
  5. 再次致电connect。它应该失败,如果errorEISCONN,则套接字已经连接并且第一个连接成功。

  6.   

我使用getpeername尝试了第一个,然后仅在getpeername失败并显示ENOTCONN时才会获取错误,并且它对我有效,如下所示:

if(getpeername(ivSocketId, &addr, &addrlen) < 0)
{
  if (errno == ENOTCONN)
  {
    int sockErr = 0;
    socklen_t sockErrLen = sizeof(sockErr);
    if(getsockopt(ivSocketId, SOL_SOCKET, SO_ERROR,
        &sockErr, &sockErrLen) < 0) 
    {
        // Handle errors, may be throw some exceptions
    }
    if(sockErr == 0)
    {
        //Handle Success scenario..
        return;
    }
    else
    {
        // Handle errors, may be throw some exceptions
    }
  }
  else
  {
      // Handle errors, may be throw some exceptions
  }
}