1

ソケットライブラリを作成していますが、特定のアドレスとポートにバインドするクライアントソケットで問題が発生しています。現在、winsock TCPライブラリをテストしていますが、*nixで同じ問題が発生しない理由はないと確信しています。これまでのところ、すべてが正常に機能しています(最初にconnect()電話をかけなくても、できます)。bind()send()recv()

192.168.1.99:XXXX(私のマシンのアドレス)をリッスンしているサーバーがあります。

次に、ソケットを取得して192.168.1.99:0にバインドします。WSAEADDRNOTAVAIL接続しようとすると、ソケットエラー10049( )が発生します。0.0.0.0:0にバインドしようとすると、同じことが起こります。

127.0.0.1:0またはlocalhostにバインドしようとするか、ホスト名をNULLのままにしようとすると(これが可能な場合でも、AI_PASSIVEフラグを使用する必要がありますか?)、接続すると10061(WSACONNREFUSED)が返されます。

ここでこれを行う正しい方法を知っている人はいますか?

EDIT3:問題は、bindを呼び出す前にソケットを呼び出していて、connectを呼び出す前にもう一度ソケットを呼び出していたことでした。bindを呼び出す前にソケットのみを呼び出すように切り替えました。他の誰かがこの概念全体に混乱しているかどうかわからないので、誰かがそれを閉じるべきだと感じない限り、これをここに残しておきますか?

編集2:ソケットを2回呼び出すという問題はありますか?

編集:ここにいくつかの機能があります

int main() {
    WSADATA wsaData;   // if this doesn't work

    if(WSAStartup(MAKEWORD(2,0), &wsaData) != 0) {
        cout << WSAStartup failed << endl;
        return -1;
    }

    ClientSocket* cs = new ClientSocket("192.168.1.99", "XXXX");

    cs->bind("", "0"); //error 10049 (WSAEADDRNOTAVAIL)
    //cs->bind("", "60000"); //error 10049 (WSAEADDRNOTAVAIL)

    //cs->bind("0.0.0.0", "0"); //error 10049 (WSAEADDRNOTAVAIL)
    //cs->bind("0.0.0.0", "60000"); //error 10049 (WSAEADDRNOTAVAIL)

    //cs->bind("192.168.1.99", "0"); //error 10061 (WSACONNREFUSED)
    //cs->bind("192.168.1.99", "60000"); //error 10061 (WSACONNREFUSED)

    //cs->bind("127.0.0.1", "0"); //error 10061 (WSACONNREFUSED)
    //cs->bind("127.0.0.1", "60000"); //error 10061 (WSACONNREFUSED)

    //cs->bind("localhost", "0"); //error 10061 (WSACONNREFUSED)
    //cs->bind("localhost", "60000"); //error 10061 (WSACONNREFUSED)


    cs->connect(); //connects to the info passed in ctor

    Socket* s = cs->getSocket();

    if(s == NULL) {
        cs->close();
        return -1;
    }

    .
    .
    .
    .
}

//these are the methods exposed by my API
int ClientSocket::bind(std::string hostname, std::string port) {
    if(bound) {
        return -1;
    }
    if(connected) {
        return -2;
    }

    //this defines what specialSockOp will do
    ssop = &ClientSocket::BaseDef::bind;

    info->hostname = hostname;
    if(hostname.compare("") == 0) {
        info->AI_FLAG_PASSIVE = true;
    }
    info->port = port;

    //calls SpecialSocket::socket()
    socket();

    bound = true;

    return 0;
}

int ClientSocket::connect() {
    if(connected) {
        return SOCKET_ERROR;
        //throw exception
    }
    if(bound) {
        ssop = &ClientSocket::BaseDef::connect;
    }
    socket();
    return 0;
}

void SpecialSocket::WinImp::socket() {
    struct addrinfo *results = NULL, *p = NULL, hints;
    int rv;

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = (containerClass->info->ipType == IP_DUAL ? AF_UNSPEC :
        containerClass->info->ipType == IPV6 ? AF_INET6 : AF_INET);
    hints.ai_socktype = (containerClass->info->protocol == TCP ? SOCK_STREAM : 
        SOCK_DGRAM);
    hints.ai_protocol = 0;
    if(containerClass->info->AI_FLAG_PASSIVE) {
        hints.ai_flags |= AI_PASSIVE;
    }
    if(containerClass->info->AI_FLAG_CANONNAME) {
        hints.ai_flags |= AI_CANONNAME;
    }

    //cStringHostname returns NULL if an empty string has been passed in
    if((rv = getaddrinfo(containerClass->info->cStringHostname(),
         containerClass->info->cStringPort(), &hints, &results)) != 0) {
            //throw exception
    }

    for(p = results; p != NULL; p = p->ai_next) {
        if((containerClass->info->sock_fd = ::socket(p->ai_family,
                 p->ai_socktype, p->ai_protocol)) == INVALID_SOCKET) {
            closesocket(containerClass->info->sock_fd);
            continue;
        }

        //for a ClientSocket specialSockOp will call either bind or connect
        //corresponding to the function called by ClientSocket
        if(containerClass->specialSockOp(p->ai_addr) == SOCKET_ERROR) {
            closesocket(containerClass->info->sock_fd);
            continue;
        }

        break; //successfully connected
    }

    if(p == NULL) {
        //none of the connections in results were good
        //throw exception - could not connect
    }

    freeaddrinfo(results);
}

int SpecialSocket::WinImp::bind(const void* addr) {
    struct sockaddr* ai_addr = (struct sockaddr*)addr;

    return ::bind(containerClass->getSockFd(), ai_addr, sizeof(*ai_addr));

}

int ClientSocket::WinImp::connect(const void* addr) {
    struct sockaddr* ai_addr = (struct sockaddr*)addr;

    int result = ::connect(containerClass->getSockFd(), ai_addr, sizeof(*ai_addr));

    if(result == SOCKET_ERROR) {
        //these are where my errors are showing up
        cout << "error after connect() is - " << WSAGetLastError() << endl;
        return SOCKET_ERROR;
    }

    containerClass->servInfo->sock_fd = containerClass->getSockFd();

    //I separated the functionality here - Socket can only send/recv
    containerClass->s = new Socket();
    //I use getpeername here to get information about the remote socket

    //Long story but basically sets up the Socket
    containerClass->initRWSocket(containerClass->s, containerClass->servInfo);

    return result;
}

その後、ユーザーは電話をかけることができ、それClientSocket.getSocket()を返すSocketことができsend/recvます。私の場合、インスタンス化される前に戻るため、インスタンス化されないため、これNULLが返されます。SocketSpecialSocket::connect

4

1 に答える 1

4

connect のターゲットアドレスに接続できる有効なローカルアドレスにバインドする必要があります。つまり、そこへのルートがあります。この場合、明らかに:

  • 192.168.1.99 はローカル アドレスではなく、リモート アドレスです
  • 127.0.0.1 はローカル アドレスですが、そこからは 127.0.0.1 にしか到達できず、リモート アドレスには到達できません
  • 0.0.0.0 は、リスニング ソケットの有効なバインド アドレスですが、アクティブな (発信) ソケットのバインド アドレスではありません。

これは、アクティブなソケットをバインドすることが悪い考えであるいくつかの理由の 1 つです。マルチホーム ホストでは、適切なアドレスを選択するためにローカル アドレスを反復処理する必要があります。つまり、TCP が自動的に行うルーティングを複製する必要があります。 bind() をまったく実行しないでください。

あなたの編集で与えられた説明は、あなたが得たエラーと一致しません。

于 2013-02-01T20:02:02.797 に答える