1

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

操作系统:Linux Suse10

我的问题是:

  1. 这里粘贴的代码对吗?
  2. 为什么getsockopt()Opt 值设置为 113 而 errno 设置为 115?

我也注意到下面。

  • 客户端有多个IP:174.66.45.22、58.68.445.112
  • 服务器有多个IP:174.88.69.33、58.46.22.33
  • 如果我将绑定地址设置为 174.66.45.22 并连接到 174.88.69.33 总是成功的。
  • 如果我将绑定地址设置为 174.66.45.22 并连接到 58.46.22.33 将失败。
  • 但是,如果我只是在第一次运行时将绑定地址设置为 58.68.445.112,在第二次运行时我将绑定地址更改为 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;
}
4

1 回答 1

0

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

在 WR Stevens 的书,UNIX® Network Programming Volume 1, Third Edition: The Sockets Networking API - 16.4 Non-blocking connect: Daytime Client,其中谈到了各种连接的不兼容性,我们可以得到一些答案:

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

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

  1. 调用getpeername而不是getsockopt. 如果使用 失败ENOTCONN,则连接失败,然后我们必须调用getsockoptwithSO_ERROR以获取套接字的未决错误。

  2. 调用read长度为0。如果read失败,connectfailed和errnofromread表示连接失败的原因。当连接成功时,read应该返回 0。

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

我尝试了第一个使用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
  }
}
于 2014-02-06T05:42:43.577 回答