Winsock accept () возвращает WSAENOTSOCK (код 10038) - PullRequest
0 голосов
/ 15 марта 2011

надеюсь, у тебя хороший день.Еще одна проблема с сокетом, в другой день:)

Наконец-то у меня установлена ​​IDE MicroSoft Visual C ++ (MSVC ++), а также Platform SDK, так что я могу скомпилировать приложения winsock.

Здесь пропущено множество вещей,В функции ServerSocket :: accept () он создает новый экземпляр ClientSocket и устанавливает дескриптор файла сокета равным тому, который был принят accept (), я также проверил там, и он распознает, что дескриптор также действителен там.

В моей функции ClientSocket :: recv () я вызываю (очевидно) функцию recv () из библиотеки winsock.Проблема, с которой я сталкиваюсь, заключается в том, что дескриптор сокета, который я использую, распознается recv () как недействительный, но только на серверном экземпляре ClientSocket, возвращенном из моего ServerSocket :: accept () - на клиентском экземпляре ClientSocket нетпроблемы.Я вставил несколько операторов отладки, дескриптор действителен.

Самое странное в этом то, что если я скомпилирую этот точно такой же код с MinGW gcc / g ++ для Windows, он будет работать нормально!Эта проблема возникает только при использовании 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 ().Это происходит на стороне сервера, на экземпляре ClientSocket, возвращенном из моего ServerSocket :: accept () (да, я тоже проверил дескриптор - это нормально).Клиентская сторона заявляет «WSAECONNRESET (10054)» или «WSAECONNABORTED (10053)».

Я не могу думать ни о чем другом, что может быть неправильным.Хуже всего то, что он отлично работает, используя MinGW gcc / g ++ для Windows и Linux.

Если вы хотите увидеть всю библиотеку, она вставляется в: (осторожно: 600+ строк!)
Socket.cxx - http://paste.pocoo.org/show/353725/
Socket.hxx - http://paste.pocoo.org/show/353726/

Спасибо !!!

Обновление - В соответствии с решением Бена я сейчас использую: void ServerSocket::accept(ClientSocket& sock); и реализую как: ClientSocket mysock; server.accept(mysock);

Большое спасибо !!!

Ответы [ 2 ]

4 голосов
/ 15 марта 2011

Похоже, вы не следуете правилу Трех .Каждый раз, когда у вас есть деструктор, вам нужно написать или отключить как конструктор копирования, так и оператор присваивания.

В вашем примере использования:

ClientSocket client = server.accept();

Переменная client является копиейпостроено из возвращаемого значения.Затем деструктор запускает временную переменную, закрывая сокет.

В C ++ 0x вы можете добавить Move-конструктор и решить эту проблему.На данный момент вы должны реализовать swap и использовать его:

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

Или передать client в качестве параметра server.accept:

ClientSocket client;
server.accept(client);

Вы можете написать движущуюся копию-конструктор для ClientSocket, в стиле auto_ptr, но я бы не советовал.Люди не ожидают, что копировальный конструктор украдет ресурсы.

0 голосов
/ 15 марта 2011

То, что переменная сокета не установлена ​​в INVALID_SOCKET, не означает, что дескриптор сокета является допустимым с точки зрения WinSock.Очевидно, что это не так, иначе WinSock не будет жаловаться на это.Сокет закрывается до того, как вы сможете вызвать recv() (что также видно на клиентской стороне, получающей ошибки).

Основная причина этого в том, что ServerSocket::accept() возвращает новый ClientSocket экземпляр по значению.Компилятор должен выделить вторую копию объекта для возвращаемого значения, но ваш класс ClientSocket не определяет конструктор копирования.Исходный дескриптор сокета будет скопирован из первого экземпляра ClientSocket во второй экземпляр, затем исходный экземпляр освобождается при выходе, закрывая сокет до того, как второй экземпляр сможет его использовать.Вам нужно определить конструктор копирования, который будет владеть исходным дескриптором сокета и установить дескриптор исходного экземпляра на INVALID_SOCKET, чтобы его деструктор больше не мог закрывать сокет.

В связи с этим ваш класс ClientSocket имеетутечка ручки в нем.Вы вызываете WSAStartup() и socket() внутри конструктора ClientSocket (что не является лучшим местом для любого из этих вызовов).Когда ServerSocket::accept() принимает нового клиента, вы вызываете ClientSocket::setFd() с новым дескриптором сокета, который заменяет оригинальный дескриптор сокета, который был размещен в конструкторе ClientSocket, без его освобождения.Вы должны определить второй ClientSocket конструктор, который принимает существующее описание сокета в качестве входных данных, а затем иметь вызов этого конструктора setFd() вместо socket().Это устранит утечку, и тогда конструктор копирования при необходимости может стать владельцем этого одного выделенного дескриптора сокета.

...