Posso ignorar com segurança a ordem dos bytes na rede?

24

Estou desenvolvendo um aplicativo servidor-cliente no qual o cliente será executado no Windows e o servidor provavelmente no Linux. Talvez depois eu leve o cliente para o Mac e Linux, mas ainda não.

Todos os computadores domésticos hoje em dia rodam em little-endian. Pesquisei um pouco no Google, mas não consegui encontrar realmente uma lista de dispositivos que rodam em big-endian. Até onde eu sei, alguns chips da Motorola ainda usam big endian e talvez alguns telefones (não pretendo portar o aplicativo para smartphones, então isso não importa para mim). Então, por que eu reorganizaria os bytes de todo número inteiro, todo curto, todo flutuador, duplo e assim por diante, para leitura e gravação , quando eu já sei que servidor e cliente rodam em little-endian?

Isso é apenas um trabalho desnecessário a ser feito. Então, minha pergunta é: Posso ignorar com segurança o endianness e apenas enviar dados little-endian? Quais são as desvantagens?

tkausl
fonte
4
Como as máquinas saberão se estão recebendo dados little-endian em vez dos dados habituais / padrão do big-endian?
Ixrec
2
Você precisa distinguir entre os metadados exigidos pelo protocolo de rede e a carga útil, que é apenas um monte de bytes não interpretados para todos, exceto seu código. Espero que você não esteja rolando sua própria pilha de rede. Consequentemente, suponho que a pergunta seja apenas sobre a carga, correto?
2
@ Delnan sim, apenas falando sobre a carga útil. É claro que ainda falarei em ordem de byte da rede com a própria pilha de rede.
tkausl
3
Apenas um pensamento do lado: é realmente necessário que você trabalhe em um nível de abstração em que a endianidade é uma preocupação? Talvez valha a pena considerar o uso de protocolos para os quais existem bibliotecas apropriadas que encapsulam toda essa "bagunça" de baixo nível. Então, você também tem o bônus adicional de que adicionar mais clientes pode ser feito com muito mais facilidade.
23416 godfatherofpolka
11
@tkausl Apenas mais duas idéias paralelas: como regra geral, a IO é extremamente lenta em comparação com os cálculos, portanto, qualquer sobrecarga introduzida ao trabalhar em um nível de abstração mais alto é provavelmente insignificante. Pode até acontecer que algumas bibliotecas superem as implementações manuais, devido ao pool inteligente de recursos e ao manuseio assíncrono, etc. Então, eu avaliaria cuidadosamente as soluções existentes. Além disso, dada sua descrição, eu também consideraria a escalabilidade e não o desempenho. Aqui você pode se beneficiar novamente do uso de protocolos de nível superior.
23416 godfatherofpolka

Respostas:

29

... por que eu reorganizaria os bytes ... quando eu já sei que servidor e cliente rodam em little endian? Isso é apenas um trabalho desnecessário.

Só é desnecessário se você puder garantir que seu código sempre será executado em arquiteturas little-endian. Se você pretende ter uma vida longa, vale a pena o esforço extra para evitar perturbar o código comprovado daqui a uma década, quando alguma arquitetura big endian se tornar a coisa "in" e você achar que é um bom mercado para sua aplicação.

Há uma ordem de bytes padrão de rede. É big-endian, mas nada diz que você deve cumpri-lo ao projetar seu protocolo. Se você souber antecipadamente que a maioria dos sistemas que executam seu código será pouco endian e o desempenho for crítico, declare que o "tkausl standard byte ordering" e o acompanha. Onde você normalmente chamaria htons()para colocar as coisas na ordem que você precisa, escreva uma macro chamada htots()que não compila condicionalmente em arquiteturas little-endian e faça o re-arranjo em big-endian.

Manter o código para realizar as conversões de entrada e saída não é realmente um grande esforço. Se você tiver um número muito grande de mensagens, encontre uma maneira de expressá-las e escreva um programa para gerar as conversões de entrada e saída.

Blrfl
fonte
10
A redação when designing your protocolé importante, porque também diz implicitamente que essa opção existe apenas ao projetar um novo protocolo e não ao implementar algum protocolo existente. E mencionar a necessidade de uma htots(e realmente uma família inteira de funções) também deixa claro que escolher uma ordem de bytes diferente não é algo que se faz para tornar o código mais simples, mas pode torná-lo um pouco mais rápido.
kasperd
4
Há (não-padrão, mas muito comum nos dias de hoje) funções htole32(), htole16(), le16toh(), etc., bem como funções disponíveis. Infelizmente, o arquivo a ser incluído para obtê-los é ainda menos padrão: <endian.h>ou <sys/types.h>dependendo da plataforma.
Torek
Essa resposta é boa, mas acho que a suposição de que o desempenho pode ser crítico no caso em questão é provavelmente uma suposição errada, baseada mais em superstição do que em fatos.
Doc Brown
11
@DocBrown: Eu sempre gosto de ressaltar que o protocolo X suporta a escolha de seu próprio pedido de bytes há 30 anos e, por mais limitados que fossem os recursos da época, ninguém se queixava de que era um problema.
Blrfl
7

É o seu protocolo.

Você não pode ignorá-lo com segurança. Mas você pode rotulá-lo com segurança. Você controla o cliente e o servidor. Você controla o protocolo. Não faz sentido não se importar se é big endian ou little endian, desde que você saiba se os dois lados concordam?

Isso significa sobrecarga. Agora você tem que marcar seu endianness de alguma forma. Faça isso, e eu posso ler em qualquer coisa.

Se você não deseja sobrecarga de dados e sua CPU está entediada e procurando algo para fazer, faça a conformidade .

candied_orange
fonte
6

Então, minha pergunta é: Posso ignorar com segurança a endianess e apenas enviar dados little-endian?

Existem duas interpretações disso:

  • Se você projetar seus aplicativos / protocolos para sempre enviar 1 little-endian, NÃO estará ignorando a endianess.

  • Se você projetar seus aplicativos / protocolos para enviar / receber qualquer endianess nativa, eles funcionarão desde que você execute seus aplicativos em plataformas com a mesma endianess nativa.

    Isso é "seguro" 2 ? Isso é para você julgar! Mas certamente existem plataformas de hardware comuns que usam little-endian, big-endian ou ... bi-endian.

    Referência:

Quais são as desvantagens?

A desvantagem óbvia de ignorar endianess é que, se você / seus usuários precisam executar seus aplicativos / protocolos entre plataformas com diferentes endianess nativos, você tem um problema. Os aplicativos serão interrompidos e você precisará alterá-los para corrigir o problema. E lide com problemas de compatibilidade de versões, etc.

Claramente, a maioria das plataformas de geração atual é nativamente pouco endian, mas 1) algumas não, e 2) podemos apenas adivinhar o que acontecerá no futuro.


1 - Sempre ... inclusive em plataformas nativas big-endian.

2 - De fato, o que significa "seguro"? Se você está nos pedindo para prever a direção futura das plataformas de hardware ... receio que isso não seja objetivamente responsável.

Stephen C
fonte
3

Endianness não é a única consideração. Há o tamanho de números inteiros, há pacotes de estruturas que você pode enviar ou receber, e assim por diante.

Você pode ignorar tudo isso. Ninguém pode forçar você. Por outro lado, a maneira segura e confiável é documentar um formato externo e, em seguida, escrever um código que leia ou grave o formato externo corretamente, independentemente do seu processador, linguagem de programação e implementação da linguagem de programação.

Normalmente, não há muito código. Mas tem um grande benefício: as pessoas que lêem seu código não suspeitam que você não tem noção, não sabem nada sobre troca de dados externos e escrevem código que geralmente não é confiável.

gnasher729
fonte
3

A pilha padrão BSD rede em C tem a hton/ ntohfuncionalidade ( network-to-host/ host-to-network) que expanda para não-ops em máquinas de rede-nativa (grande endian). Você precisaria de suas próprias contrapartes para elas no cenário em que a ordem de bytes nativos da rede é pouco endian.

Essa é a maneira robusta de fazer isso.

Seria não convencional, mas não vejo nada de errado nisso. Os computadores em rede sempre recebem bytestreams e precisam concordar com os protocolos sobre como interpretar esses bytes. Isso é apenas parte disso.

PSkocik
fonte
3

Vários protocolos usados ​​para transmitir dados entre servidores usam pequenos números endian:

  1. BSON
  2. Buffers de protocolo
  3. Capn Proto

Veja https://en.wikipedia.org/wiki/Comparison_of_data_serialization_formats , para obter detalhes sobre vários formatos, alguns dos quais com números little-endian e alguns com números big-endian.

Não há absolutamente nada de errado em usar um protocolo baseado em pequenos números endian. Uma grande máquina endian é tão capaz de ler pequenos números endian quanto uma pequena máquina endian pode ler grandes números endian. Muitas pessoas fizeram isso especificamente para evitar o custo extra de computação da decodificação de números big-endian em pequenas máquinas endian.

Se você constrói seu protocolo sobre um desses protocolos existentes, nem precisa se preocupar com o problema, pois ele já está resolvido. Quando você decide executar seu código em uma plataforma big endian, as bibliotecas que implementam esses protocolos cuidam automaticamente para garantir que você decodifique os valores corretamente.

Winston Ewert
fonte
2

Um exemplo de um sistema big endian é o MIPS usado em roteadores. O ARM e o MIPS são selecionáveis ​​pelo endian, mas geralmente o MIPS é o big endian porque facilita o hardware de rede (a parte mais significativa de uma palavra é a parte que você recebe primeiro e pode tomar uma decisão de roteamento antes de receber o restante a palavra, em vez de precisar armazenar a palavra inteira).

Portanto, depende do que você quer dizer com 'Linux', mas se você quiser executar seu aplicativo de servidor em um sistema menor, como um roteador executando o OpenWRT, talvez seja necessário considerar o grande apoio endian.

Como sempre, fazer suposições simplificadoras é uma otimização perfeitamente sensata até o momento em que você atinge algo que não se encaixa nas suposições. Só você pode dizer o quão doloroso seria desenrolá-los se você se deparar com esse problema.

user1908704
fonte
0

Não acho que nenhuma das respostas seja suficientemente precisa. De acordo com a Wikipedia, endianness é a ordem dos bytes que compõem uma palavra.

Vamos pegar 4 bytes e interpretá-los como um int. Em um sistema um pouco endiano, os bytes serão interpretados da direita para a esquerda e vice-verca em um grande sistema endiano. Obviamente, é importante concordar sobre qual extremidade interpretar um int.

Permite reduzir um pouco os protocolos de rede modernos que podem estar usando json ou xml. Nenhum desses formatos transferirá um int como 4 bytes. Eles transferirão os dados como texto, que serão analisados ​​como int no lado receptor.

Portanto, no final, o endianness não importa ao usar json ou xml. Ainda precisamos usar o big endian para cabeçalhos tcp, motivo pelo qual é chamado de ordem de bytes da rede, mas a maioria dos programadores não precisa mexer com eles diariamente.

A codificação mais usada hoje em dia é o utf-8, que também parece ser imune a problemas relacionados à continuidade .

Então eu diria que sim. É seguro ignorar endianness ao usar formatos baseados em texto transferidos usando utf-8.

Esben Skov Pedersen
fonte
dois votos negativos e nenhum comentário. Ótimo.
Esben Skov Pedersen
11
Eu não era o menos votado, mas essa resposta parece estar ignorando / descartando uma pergunta perfeitamente válida. Só porque alguns protocolos são baseados em texto não significa que todos os protocolos devam ser.
Peter Green
2
Votei isso de positivo porque toca no fato de que o formato da carga útil não tem nada a ver com os protocolos subjacentes. Algumas pessoas adoram cavar problemas inventados.
Zdenek
0

Os sistemas big endian parecem estar saindo. Muitos dos unixes tradicionais usavam big endian, mas estão em declínio há anos a favor do linux no x86.

arm é bi-endian, mas a variante big endian parece raramente ser vista.

mips existe em ambas as variantes. A ameaça da variante big endian é vista principalmente em aplicativos de rede (por razões históricas, os protocolos da Internet geralmente usam big endian).

O ppc era tradicionalmente big endian, com algumas partes suportando ambos, mas a IBM parece agora estar pressionando o modo little endian para o ppc de 64 bits (eles recentemente lançaram portas ppc64el no Debian e Ubuntu).

O sparc é normalmente big endian, mas novamente parece estar em declínio.

Se você estiver implementando um protocolo existente, obviamente precisará seguir as especificações. Se você deseja que a IETF abençoe seu novo protocolo, é provável que o big endian seja mais fácil, porque é isso que eles já usam em seus protocolos existentes, mas a IMO para um novo design "greenfield" de protocold é o caminho a seguir.

Você pode inserir macros desde o início, que não serão utilizadas em sistemas little endian ou não poderá se preocupar até / a menos que precise portar para um sistema big endian.

Peter Green
fonte