Como faço para prefixar o comprimento da mensagem no TCP / IP

votos
3

Estou enviando mensagens sobre TCP / IP, eu preciso prefixo comprimento da mensagem em uma matriz de char e depois enviá-lo. Como eu faço isso?

Além disso, você pode fornecer um exemplo de como extraí-lo na outra extremidade. E, se possível, por favor, explique.

Eu estou usando C ++ e Winsock.

EDITAR:

string writeBuffer = Hello;

unsigned __int32 length = htonl(writeBuffer.length());

Não está retornando o comprimento correto, em vez de um número muito grande.

Para a parte de recepção, se eu usar ntohl (), então eu também obter um grande número em vez do comprimento correto? Por que? Estou recebendo assim

bool Server::Receive(unsigned int socketIndex)
{
    // Read data from the socket
    if (receivingLength)
    {
        bytesReceived = recv(socketArray[socketIndex - WSA_WAIT_EVENT_0],
            ((char*)&messageLength) + bytesReceived, MESSAGE_LENGTH_SIZE - bytesReceived, 0);

        if (bytesReceived == SOCKET_ERROR)
        {
            return false;
        }

        if (bytesReceived == MESSAGE_LENGTH_SIZE)
        {
            // If uncomment the following line,
            // I won't get the correct length, but a large number
            //messageLength = ntohl(messageLength);
            receivingLength = false;
            bytesReceived = 0;
            bytesLeft = messageLength;
        }
    }
    else
    {
        if (bytesLeft > BUFFER_SIZE)
        {
            return false;
        }

        bytesReceived = recv(socketArray[socketIndex - WSA_WAIT_EVENT_0],
            &receiveBuffer[bytesReceived], bytesLeft, 0);

        if (bytesReceived == SOCKET_ERROR)
        {
            return false;
        }

        if (bytesReceived == messageLength)
        {
            // we have received full message
            messageReceived = true;

            receiveBuffer[bytesReceived] = '\0';

            // wait for next message
            receivingLength = true;
        }

        bytesLeft -= bytesReceived;
    }

    return true;
}
Publicado 27/08/2009 em 07:50
fonte usuário
Em outras línguas...                            


3 respostas

votos
6

Ao enviar um campo de comprimento em um fluxo TCP, você precisa decidir duas coisas:

  1. o comprimento deve ter o comprimento (1 byte, 2 bytes, 4 bytes, comprimento variável)
  2. o que endianness eu deveria usar

I recomendado para usar 4 bytes de comprimento, e da ordem de bytes da rede (isto é, grande-endian). Por ordem de bytes de rede, as macros htonl e ntohl irá converter entre host (nativo) byte order (little-endian, no seu caso), e rede byte ordem.

Para enviar dados, o fragmento deve ficar assim:

size_t length = strlen(data);
uint32_t nlength = htonl(length);
send(sock, &nlength, 4, 0);
send(sock, data, length, 0);

No lado do receptor, você extrai primeiro o comprimento, em seguida, os dados:

uint32_t length, nlength;
recv(sock, &nlength, 4, 0);
length = ntohl(nlength);
data = malloc(length+1);
recv(sock, data, length, 0);
data[length] = 0;

O que este código está faltando é o tratamento de erros: cada um dos enviar e receber chamadas podem falhar; os recvs pode receber menos dados do que o esperado. Mas isso deve dar-lhe uma idéia.

Editar : Para lidar com o caso que o recv retorna muito poucos dados, executá-lo em um loop, mantendo uma contagem do que você leu até agora, por exemplo,

int length_bytes = 0;
while(length_bytes < 4){
   int read = recv(sock, ((char*)&nLength)+length_bytes, 4-length_bytes, 0);
   if (read == -1) some_error_occurred_check_errno();
   length_bytes += read;
}
Respondeu 27/08/2009 em 08:03
fonte usuário

votos
1

Pseudocódigo (manipulação de erro omitido, tenha cuidado):

remetente:

u_long converted = htonl( messageLength ); // convert from local byte order to network byte order
send( socket, (char*)&converted, sizeof( converted ), 0 );

receptor:

u_long messageLength;
recv( socket, (char*)&messageLength, sizeof( messageLength ), 0 );
messageLength = ntohl( messageLength ); convert from network byte order to local byte order
Respondeu 27/08/2009 em 08:01
fonte usuário

votos
0

Eu não tenho certeza se é isso que você está perguntando, mas eu gostaria de enviá-lo como uma seqüência de comprimento fixo (digamos 4 caracteres, real comprimento até você). Em outras palavras, se o comprimento de dados é 164, enviar a string "0164". Isto torna-se sobre quaisquer problemas de ordem byte na outra extremidade, e é fácil de ler e depurar.

Para produzir tais cordas, você pode usar um stringstream:

#include <sstream>
#include <iomanip>

std::string MakeLenStr( int n ) {
  std::ostringstream os;
  os << std::setw(4) << std::setfill('0') << n;
  return os.str();
}

Enviar:

std::string s = MakeLenStr( len );
send( sock, s.c_str(), 4 );

Para lê-lo na outra extremidade, algo como:

char a[5] = {0};
recv( sock, a, 4 );
int n = atoi( a );
Respondeu 27/08/2009 em 08:00
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more