Isso é mais como uma questão conceitual. Preciso de alguns esclarecimentos.
Hoje eu estava aprendendo algumas coisas de programação de soquete e escrevi um servidor de bate-papo simples e um cliente de bate-papo baseado no Guia de programação de rede de Beej . (servidor de chat recebe mensagens de clientes e envia mensagens para todos os outros clientes)
Copiei o servidor de bate -papo e escrevi meu próprio cliente de bate-papo .
O cliente de bate-papo é apenas um programa para enviar stdin
entrada para o servidor e imprimir dados de soquete do servidor.
Mais tarde notei que o guia diz que posso usar apenas telnet
para me conectar ao servidor. Eu tentei e funcionou.
Eu não estava familiarizado com telnet e por muito tempo não sei exatamente o que é.
Então agora minha experiência me confunde:
O telnet não é apenas um simples programa de envio/eco de TCP? O que torna tão especial ser uma coisa de protocolo ? Meu programa cliente de bate-papo burro não cria um protocolo [aplicativo].
De Wikipedia Communication_protocol :
Em telecomunicações, um protocolo de comunicação é um sistema de regras que permite que duas ou mais entidades de um sistema de comunicação transmitam informações por meio de qualquer tipo de variação de uma quantidade física.
Que regras o Telnet cria? telnet host port
, abra um soquete de fluxo TCP para entrada/saída bruta? Isso não é uma regra.
O Telnet é definido no RFC 854 . O que o torna (e qualquer outra coisa) um protocolo é um conjunto de regras/restrições. Uma dessas regras é que o Telnet é feito sobre TCP e atribuído à porta 23 - essas coisas podem parecer triviais, mas precisam ser especificadas em algum lugar.
Você não pode simplesmente enviar o que quiser, existem limitações e significados especiais para algumas coisas. Por exemplo, ele define um "Terminal Virtual de Rede" - isso ocorre porque quando o telnet foi estabelecido, poderia haver muitos terminais diferentes: uma impressora, um monitor preto/branco, um monitor colorido que suportasse códigos ANSI, etc.
Além disso, há coisas assim:
Nos tempos modernos, a maioria das coisas não é mais tão importante (por outro lado, o telnet como um protocolo não está mais sendo muito usado, não apenas porque não tem segurança), então na prática tudo se resume a enviar / eco, a menos que você tem que realmente interagir com os terminais.
O fato de que
telnet
“simplesmente funciona” ao conectar dois terminais é em si uma decisão de protocolo: os projetistastelnet
decidiram usar um modelo com “terminais virtuais de rede” em cada extremidade de uma conexão TCP e decidiram modelar esses terminais virtuais no terminal básico características (teletipos realmente).Além disso,
telnet
é mais do que um simples serviço de eco baseado em TCP; suporta códigos de controle, enviados em banda, para diversos fins. Está codificado em RFC 854 .Isto é do RFC 15, que é mencionado na wikipedia "telnet". Este artigo também começa chamando o telnet de " protocolo de aplicação ".
A RFC 15 é de setembro de 1969 - há poucos dias ouvi uma entrevista interessante com o cara que fez a primeira conexão "internet", exatamente 50 anos atrás. Sua história foi assim:
"Ao contrário de N. Armstrong, não havíamos pensado em nenhuma mensagem especial. Mas depois de digitar "lo" (para "login"), o sistema travou. Então "lo" (como em "lo e eis") foram as duas primeiras personagens transmitidos pela internet. Em retrospecto, não poderíamos ter pensado em algo melhor."
(Aqui está um pouco dessa entrevista: wiki arpanet Kleinrock )
O Telnet é anterior ao TCP/IP. RFC 15 fala de "primitivas de rede". Por causa do TCP/IP, o telnet ficou apenas para cuidar do "resto", a camada de aplicação (as "opções negociadas" na RFC854).
Sim, o telnet é bastante simples, mesmo sem TCP. RFC 15 termina com:
"Protocolo" é usado com telnet porque inerentemente dois sistemas (diferentes) precisam falar em um "idioma".
A RFC 854 também explica o conceito:
O protocolo telnet é um protocolo muito simples, mas existe. Por "existe" quero dizer que um cliente telnet não envia todos os bytes que recebe para o programa que o lê (geralmente um shell).
O protocolo é
Então sim, o protocolo é extremamente simples, quase trivial. Mas não é uma simples passagem tcp.
Comandos Telnet
Se você estiver interessado em quais comandos você pode enviar para o telnet (em vez do software/servidor do outro lado), você pode ler o RFC: https://www.rfc-editor.org/rfc/rfc854 . Basicamente telnet define:
Observe que os comandos acima são especificamente comandos Telnet. Eles não fazem parte de nenhum protocolo de terminal do lado do cliente ou do servidor. Por exemplo, se você deseja encerrar um processo no unix, normalmente digitaria
ctrl-c
. Isso normalmente é enviado0x03
do terminal para o shell, que será enviadoSIGINT
para o processo que o shell está executando. Mas o próprio telnet define0xff, 0xf4
para enviarSIGINT
para o processo que é um protocolo específico do telnet para fazê-lo. Da mesma forma, os terminais normalmente enviam0x1b, 0x4b
para limpar a linha atual, mas o telnet define0xff, 0xf8
.As respostas existentes já explicam que
telnet
pode fazer mais do que apenas transmitir dados de entrada sobre TCP inalterados; Em vez disso, gostaria de me concentrar no "Meu programa cliente de bate-papo idiota não cria um protocolo [aplicativo]". - parte da pergunta. (Como um aparte, para "dados brutos sobre TCP" reais, você pode usarnetcat
em vez detelnet
)Só porque um protocolo é simples ou ruim - ou nem mesmo bem especificado em qualquer lugar - não significa que não seja um protocolo. Dois sistemas não podem se comunicar sem ter um protocolo acordado, mesmo que esse protocolo seja apenas "dados brutos sobre TCP". Se você tentar falar FTP de um lado e HTTP do outro, terá problemas, de uma forma ou de outra - se tiver sorte, não funcionará; se você não for, você terá um comportamento errado. (Pelo menos costumava ser o caso que, se você executasse um daemon FTP em uma porta não padrão no mesmo host de um servidor HTTP, em alguns navegadores você poderia usá-lo para executar XSS e ignorar as proteções CSRF apontando o navegador da vítima para POST no servidor FTP, que ecoaria o conteúdo POSTado em mensagens de erro, e o navegador o interpretaria como HTTP/0.
É uma pergunta perfeitamente válida perguntar "Que protocolo essa coisa que você chama
my dumb chat client program
usa? Eu quero falar com ela do meu programa." e uma resposta possível seria "dados brutos sobre TCP". Não é uma resposta exaustiva, no entanto.select()
é para erros, a maioria das pessoas nem parece saber que existem dados fora de banda - mas mesmo que seja um exemplo ruim, eu espero que você veja o que estou chegando)close()
ou usashutdown()
para garantir que todos os dados pendentes sejam entregues primeiro?Você disse "abrir um soquete de fluxo TCP para entrada/saída bruta? Isso não é uma regra.", mas é realmente assim? "Você deve usar TCP", "você deve enviar os dados como estão" e "você deve usar a porta XXX" soam como regras para mim.
(PS, fiquei um pouco envolvido ao escrever isso; O resto desta resposta é um pouco tangencial e tenta responder à pergunta natural de acompanhamento de "Ok, então tudo é um protocolo - isso tem alguma implicação?")
Portanto, tenha em mente que toda vez que você faz dois sistemas se comunicarem, você está usando um protocolo existente ou criando um novo protocolo: Documente seus protocolos antes que seja tarde demais! (Ou seja, o mais tardar, quando o protocolo cresce além do simples, ou começa a ter mais usuários do que apenas os desenvolvedores) Ex. muitos dos primeiros protocolos P2P diziam que "a fonte do cliente oficial é a documentação", o que é extremamente irritante em um projeto complexo, e apenas leva os clientes a falarem protocolos sutilmente diferentes e serem incompatíveis aqui e ali.
Existe o velho ditado "seja rigoroso no que você produz e liberal no que você aceita" - o sentimento central é bom, mas francamente, pelo menos da maneira como ele tende a ser aplicado, acho que é besteira, e só leva a mais problemas de manutenção e compatibilidade. Eu digo: Especifique em detalhes (tente pensar em cada caso de canto), e seja extremamente rigoroso no que você produz e aceita, e você provavelmente prevenirá/resolverá bugs reais.
Um exemplo, sobre rigor na resolução de bugs: Anos atrás, um certo rastreador de bittorrent começou a ter um problema: alguns de seus torrents não funcionavam em alguns clientes; os clientes não reclamaram, apenas receberam um infohash diferente e, portanto, não conseguiram encontrar o torrent. Eles não conseguiram descobrir o que estava acontecendo e declararam que os clientes afetados devem estar com bugs - pare de usá-los. O Bittorrent usa a codificação, que é muito rigorosa e bem especificada - e por um bom motivo: a mesma entrada sempre resulta na mesma string de saída codificada, de modo que o hash [info] é sempre o mesmo para os mesmos dados de entrada. Quando me deparei com um desses torrents, tentei alimentá-lo no meu próprio - muito estrito - analisador, e imediatamente ele disse:
Error: Bencoded dictionary keys not sorted (last='sha1', key='private')
.Então o que aconteceu? O rastreador começou a adicionar automaticamente a chave "privada" a cada torrent que foi carregado, mas apenas a adicionou ao final, em vez de manter as chaves do dicionário classificadas, como especifica o bencoding - não era rigoroso no que produzia. Alguns clientes decodificaram o torrent apenas parcialmente e calcularam o hash para a string não codificada e inalterada. Alguns clientes decodificaram todo o torrent e recodificaram a parte que precisavam fazer o hash, classificando as chaves corretamente no processo (criando um bencoding válido) e obtiveram um hash diferente. Ao meu conhecimento/lembrança, nenhum cliente reclamou que os dados eram inválidos - os clientes não eram rigorosos no que aceitavam. Então, quais hashes estavam corretos? Nenhum! Ambas as formas de calcular o hash estão corretas, mas os torrents foram corrompidos! Se até mesmo um dos clientes iria' Já reclamei sobre isso, o bug provavelmente teria sido corrigido no mesmo dia, em vez de levar meses. (IIRC Eu também relatei um bug idêntico em outro tracker completamente não relacionado alguns anos depois; o software do primeiro foi feito internamente, então também não era a mesma base de código)
Outro exemplo, desta vez sobre casos de canto: O protocolo P2P eDonkey2000 usava um hash personalizado chamado ed2k; A especificação não especificou o comportamento no caso de canto do comprimento dos dados de entrada sendo exatamente divisível pelo tamanho do bloco, então surgiram duas variantes incompatíveis, que produzem um hash diferente para alguns arquivos e são incompatíveis. (Eu disse "especificação", mas como esse era um protocolo P2P inicial, tenho certeza de que não havia uma especificação; o caso especial provavelmente foi perdido ao fazer engenharia reversa do protocolo)
Sobre a necessidade de especificações detalhadas: Muitas vezes, ao tentar escrever um cliente para um protocolo ou um analisador para um formato de arquivo, encontrei uma situação em que mesmo os programadores originais não têm ideia de como alguns dados são realmente codificados, porque eles Já usei alguma biblioteca e nunca verifiquei detalhadamente o que ela realmente faz. Dois exemplos:
Uma certa API usou um protocolo UDP personalizado, com pacotes criptografados de [tamanho variável] - a documentação especificou AES128, mas não mencionou o preenchimento usado. Quando os desenvolvedores foram questionados sobre isso, a resposta foi ao longo das linhas de:
O site de um determinado dispositivo tem a dizer sobre a interface com seus savefiles:
(Observação: na realidade, porém, não leva muito tempo para fazer engenharia reversa o suficiente do formato do arquivo para poder exportar os dados)
Alguns dos exemplos eram sobre codificações, formatos de arquivo ou algoritmos personalizados (embora ainda sejam usados dentro de protocolos, exceto pelo último), mas eu diria que tudo isso se aplica a todos eles. Não importa se você está falando de um protocolo, um formato de arquivo, uma codificação ou um algoritmo personalizado: