1

サンプルの TCP 接続関数を作成しました。これは、タイムアウト付きのノンブロッキング ソケットを使用します。私たちのラボではコードは正常に動作していますが、一部のネットワークでは TCP 接続がエラー 115 で失敗し、これは EINPROGRESS を意味します。デバッグが見つかった後getsockopt()、iValopt を 113 に設定し、optvalueゼロに等しいことを確認します。そうでない場合は失敗を返します。

OS: 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
  • Bind アドレスを 174.66.45.22 に設定して 174.88.69.33 に接続すると、常に成功します。
  • Bind アドレスを 174.66.45.22 に設定して 58.46.22.33 に接続すると失敗します。
  • しかし、最初の実行でバインド アドレスを 58.68.445.112 に設定し、2 回目の実行でバインド アドレスを 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、接続は失敗したため、 で呼び出してソケットの保留中のエラーをフェッチする必要がgetsockoptありSO_ERRORます。

  2. read長さ 0 の呼び出し。 が失敗したread場合、 はconnect失敗し、errnofromreadは接続失敗の理由を示します。接続が成功すると、read0 が返されます。

  3. connectもう一度電話してください。失敗するはずです。 が の場合errorEISCONNソケットは既に接続されており、最初の接続は成功しています。

を使用して最初のものを試し、次に で失敗した場合getpeernameにのみエラーをフェッチしましたが、次のようなものでした。getpeernameENOTCONN

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 に答える