сокет Recv api возвращает неожиданное значение - PullRequest
1 голос
/ 11 марта 2011

это клиентский code.server отправляет данные непрерывно. Здесь я просматриваю 13-байтовое сообщение (длина заголовка), получая размер данных, которые закодированы в самом сообщении, и создавая размер буфера MESSAGE_HEADER_LENGTH + DATA-LENGTH.

после получения общего размера сообщения я использую второе recv для чтения данных из сокета. во время первоначальной настройки соединения и обмена приветственным сообщением между сервером и клиентом оно работает нормально, но когда сервер отправляет непрерывный поток, скажем (когда мы нажимаем кнопку на клиентском сервере, отправляем непрерывное сообщение фиксированной длины с промежутком 5000 мс), recv не работает должным образом и буфер recv получает исходное сообщение, перед которым стоит мусор или некоторая пустая строка.

#define MESSAGE_HEADER_LENGTH 13

...

while {
    nSelectRetVal = select(NULL, &fdRead,NULL, &fdExcept, &tv);
    if(nSelectRetVal > 0) {
        if(FD_ISSET(pControlMgr->GetConnectionMgr()->GetUDPSocket(),
                    &fdRead)) {
            try {
                char chHeaderBuffer[MESSAGE_HEADER_LENGTH + 1];
                memset(chHeaderBuffer,0,MESSAGE_HEADER_LENGTH + 1);
                int nMsgPeek = recv(pControlMgr->GetConnectionMgr()->GetUDPSocket(),
                                    chHeaderBuffer, MESSAGE_HEADER_LENGTH, MSG_PEEK);
                chHeaderBuffer[MESSAGE_HEADER_LENGTH] = '\0';
                if(nMsgPeek == SOCKET_ERROR)
                    return 0;
                CProtocolMgr objProtocolMgr;
                int Bufferlength = objProtocolMgr.ProtocolMsgSize(chHeaderBuffer) + MESSAGE_HEADER_LENGTH;
                pRecvBuffer = new char[Bufferlength];
                memset(pRecvBuffer, 0, Bufferlength);
                int nRecvRetVal = recv(pControlMgr->GetConnectionMgr()->GetUDPSocket(),
                                       pRecvBuffer, Bufferlength, 0);
                if(nRecvRetVal > 0) {
                    if(pControlMgr->HandlePacket(pRecvBuffer,
                                                 pControlMgr->GetConnectionMgr()->GetServerAddress()) == -1) {
                        if(NULL != pRecvBuffer) {
                            delete [] pRecvBuffer;
                            pRecvBuffer = NULL;
                            return 0 ;
                        }
                    } catch(...) {
                        if(NULL != pRecvBuffer) {
                            delete [] pRecvBuffer;
                            pRecvBuffer = NULL;
                        }
                    }
                }
            }
        }
    }
}

1 Ответ

1 голос
/ 11 марта 2011

В вашем коде есть несколько проблем:

  1. Знаете ли вы, что delete[] является NOP, когда передан нулевой указатель?
  2. Почему в первой проверке около pRecvBufferбудучи нулевым указателем, вы получите return 0, а в (ужасном) catch(...) вы этого не сделаете?
  3. Знаете ли вы, что перехватывать все с помощью catch(...) - ужасная идея в целом и особенноесли не бросить?Существуют платформы (MS), в которых с помощью catch(...) вы даже перехватываете ошибки сегмента.Как вы думаете, что безопасно делать после того, как вы поймали segfault в вашей программе?

В любом случае, я думаю, что источник проблем, которые вы наблюдали, находится в строке

if(nRecvRetVal > 0)

Причина в том, что вы просто проверяете, что действительно могли прочитать некоторые байты, но не проверяете, сколько байтов вы действительно получили (recv возвращает количество прочитанных байтов, которое может быть меньше числа, указанного ввызов).Когда вы передаете полученный буфер функции обработки, вы фактически обрабатываете случайную память, как если бы она была получена из сети.

Вы также можете найти полезные инструменты, такие как wireshark чтобы на самом деле точно проверить, какие пакеты ваша программа получает из сети.

...