希望您过得愉快。另一个套接字问题,改天:)

我终于安装了MicroSoft Visual C ++(MSVC ++)IDE,以及Platform SDK,以便可以编译winsock应用程序。

在这里错过了很多东西。在ServerSocket :: accept()函数中,它创建一个ClientSocket的新实例,并将其套接字文件描述符设置为accept()的套接字文件描述符,我也在此处进行了检查,并且它也知道该描述符在此处也是有效的。

在我的ClientSocket :: recv()函数中,我(显然)从winsock库中调用recv()函数。我遇到的问题是recv()将我使用的套接字描述符识别为无效,但仅在从我的ServerSocket :: accept()返回的服务器端ClientSocket实例上-客户端ClientSocket实例没有问题。我插入了多个调试语句,该描述符有效。

最奇怪的一点是,如果我在Windows上用MinGW gcc / g ++编译完全相同的代码,它运行得很好!仅使用MSVC ++会出现此问题。

string ClientSocket::recv(int bufsize) {
    if (!isConnected()) throw SocketException("Not connected.");

    cout << "SocketRecv: " << (sockfd == INVALID_SOCKET) << " " << sockfd << endl;
    vector<char> buffer(bufsize+1, 0);
    cout << "SocketRecv1: " << (sockfd == INVALID_SOCKET) << " " << sockfd << endl;
    int ret = ::recv(sockfd, &buffer[0], bufsize, 0);
    cout << "SocketRecv2: " << (sockfd == INVALID_SOCKET) << " " << sockfd << endl;

    // ret is apparently -1 because of "invalid" socket descriptor, but the
    // above statements print zero (false) on the (sockfd == INVALID_SOCKET) ! :\
    if (ret < 0) {
        #ifdef _WIN32
        switch((ret = WSAGetLastError())) {
        #else
        switch(errno) {
        #endif
            case DECONNREFUSED: // The 'd' prefix means _I_ defined it, i.e. from windows it's
                                // set to 'WSAECONNREFUSED', but from linux it's set to 'ECONNREFUSED'
                throw SocketException("Connection refused on recover.");
                break;
            case DENOTCONN:
                throw SocketException("Not connected.");
                break;
            case DECONNABORTED:
                throw SocketException("Software caused connection abort.");
                break;
            case DECONNRESET:
                throw SocketException("Connection reset by peer.");
                break;
            default:
                //usually this itoa() and char/string stuff isn't here... needed it in
                //order to find out what the heck the problem was.
                char tmp[21];
                string tmp4 = "Unknown error reading socket. ";
                string tmp3 = tmp4 + itoa(ret, tmp, 10);
                //this throw keeps throwing "Unknown error reading socket. 10038"
                throw SocketException(tmp3);
                break;
        }
    } else if (ret == 0) {
        connected = false;
        return "";
    }

    return &buffer[0];
}


附加信息:套接字处于阻止模式,即尚未设置为非阻止。我已经成功调用了WSAStartup()。这是在服务器端发生的,在从我的ServerSocket :: accept()返回的ClientSocket实例上(是的,我也在那儿检查了描述符-很好)。客户端要求“ WSAECONNRESET(10054)”或“ WSAECONNABORTED(10053)”。

我想不出其他任何可能出错的地方。最糟糕的是,在Windows和linux上使用MinGW gcc / g ++都可以正常工作。

如果要查看整个库,请将其粘贴在:(警告:超过600行!)
Socket.cxx-http://paste.pocoo.org/show/353725/
Socket.hxx-http://paste.pocoo.org/show/353726/

谢谢!!!

更新-根据Ben的解决方案,我现在使用:void ServerSocket::accept(ClientSocket& sock);,并实现为:ClientSocket mysock; server.accept(mysock);

非常感谢!!!

最佳答案

看起来您没有遵循Rule of Three。每当有析构函数时,都需要编写或禁用复制构造函数和赋值运算符。

在您的示例用法中:

ClientSocket client = server.accept();


变量client是从返回值复制构造的。然后,析构函数在临时变量上运行,关闭套接字。

在C ++ 0x中,可以添加一个移动构造函数并解决此问题。现在,您应该实现swap并使用它:

ClientSocket client;
server.accept().swap(client);


或将client作为server.accept的参数传递:

ClientSocket client;
server.accept(client);


您可以按照ClientSocket的样式为auto_ptr写一个移动拷贝构造函数,但是我不建议这样做。人们不希望复制构造函数会窃取资源。

10-08 11:50