Mostrando postagens com marcador Protocolos. Mostrar todas as postagens
Mostrando postagens com marcador Protocolos. Mostrar todas as postagens

quinta-feira, fevereiro 04, 2021

Memórias dos Anos 80: A Comunicação Micro-a-Micro com o Z

 A principal aplicação do Z era a conexão aos minis e mainframes. Serviços on-line eram poucos e mais voltados para a pessoa física. Mas, à medida que os micros iam ocupando espaço nas empresas, aumentava a necessidade de comunicação entre micros via linha telefônica (lembrem-se, ainda não existia a internet!).


quinta-feira, janeiro 28, 2021

Memórias dos Anos 80: Emulação de Terminal e Transferência de Arquivos com o Z

O principal uso do Z era na conexão de micros a minis e mainframes, o que era feito emulando terminais de vídeo. Um recurso adicional era a transferência de arquivos entre os dois sistemas.

Cada fabricante tinha o seu modelo de terminal de vídeo, alguns mais simples outros complexos. A disponibilidade e qualidade de documentação técnica também variava bastante.

A transferência de arquivos exigia um programa rodando no mini ou no mainframe, e cada um tinha as suas linguagens e suas peculiaridades. Para a carga deste programa, com uma exceção, era usado o recurso do Z de simular cegamente uma digitação (podendo aguardar um tempo fixo entre caracteres e outro tempo fico entre linhas). Foi minha a definição de uma espécie de protocolo padrão para os sistemas mais simples.

Some a tudo isso as dificuldades em arranjar um sistema para desenvolvimento e o fato de não termos micro portáteis e temos todos os ingredientes para uma vida bem animada.

Não lembro mais todos os detalhes e, quando foi feita a minha cópia de lembrança do Zapt, alguns destes filtros já tinham sido descontinuados por falta de cliente, portando devem ter muitas mentiras no resto deste post!

terça-feira, outubro 27, 2020

Sensor Biométrico de Impressão Digital - Parte 2: Comunicando com um módulo FPM10A

Entre os vários sensores de digital disponíveis, acabei comprando um baseado no módulo FPM10A, cujo datasheet você pode ver aqui. Neste post vamos ver como é a comunicação com ele e fazer um primeiro teste.


quarta-feira, agosto 14, 2019

Obtendo a Data e Hora Atual de um Servidor NTP

De vez em quando precisamos da data e hora atual em um projeto com microcontrolador. Uma opção é incluir um relógio não volátil (como eu fiz aqui), mas quando temos conexão à internet podemos obter estas informações de um servidor NTP.


terça-feira, novembro 06, 2018

Conexão de Raspberry Pi a Arduino através de I2C

Embora o Raspberry Pi tenha uma grande capacidade de processamento e uma quantidade respeitável de recursos de interface, às vezes ele é não é suficiente para aplicações de controle devido a:
  • Falta de ADC (Conversor Analógico para Digital)
  • Limitações na latência (tempo de resposta a um evento externo)
  • Limitações em gerar sinais com temporizações precisas
Por estes motivos, alguns projetos com Raspberry utilizam um Arduino para interagir com componentes externos. Nestes casos, o mais comum é o uso da comunicação serial assíncrona, por ser um tipo de interface mais conhecido. Entretanto, o Raspberry é limitado quanto à porta serial, particularmente os modelos com Bluetooth.

Vamos examinar aqui uma outra forma de interconectar um Raspberry Pi a um Arduino: a comunicação serial I2C.



quarta-feira, julho 04, 2018

Franzininho: Timer para TV

Quem nunca acordou com a TV ligada? Você estava vendo aquele programa tão legal... e de repente descobre que dormiu e a TV ficou ligada por horas. Com este projeto o seu Franzininho pode desligar automaticamente a TV após um certo tempo.


quinta-feira, novembro 16, 2017

Raspberry Pi Zero W como Controlador - Parte 2

Concluindo o post anterior, vamos ver como foi implementar o Dispensador de Doces (que eu já implementei com Arduino e ESP8266) com o Raspberry Pi Zero.


quarta-feira, abril 27, 2016

Módulo de Rádio FM com CI RDA5807M - Parte 3

Agora que já conseguimos colocar o módulo funcionar, vamos começar a explorar um recurso interessante: o RDS (Radio Data System).


terça-feira, setembro 22, 2015

Rádio nRF24L01+: Explorando Topologias de Rede e Economia de Energia

Nos dois posts anteriores usamos o nRF24L01+ para comunicação ponto a ponto entre dois dispositivos. Vejamos agora algumas considerações sobre economia de energia e topologias mais sofisticadas.

terça-feira, setembro 15, 2015

Rádio nRF24L01+: Primeiros Experimentos com o Arduino

Existem várias bibliotecas disponíveis para o nRF24L01+ e inúmeros posts e tutoriais na internet que mostram um exemplo curto de uso delas. Como eu gosto de entender os detalhes, vou começar com um experimento simples que acessa diretamente o nRF24L01+, escrito a partir do datasheet e do código da biblioteca RF24.



terça-feira, setembro 08, 2015

Rádio nRF24L01+: Introdução

Comunicação via rádio é sempre uma coisa complicada, a começar pelo hardware. Isto vem continuamente melhorando, graças a componentes que requerem cada vez menos componentes adicionais (e menos críticos). Um deles é o nRF24L01+, que é usado em diversas placas de baixo custo.



terça-feira, abril 07, 2015

RS485 - Um Exemplo com o Arduino

Fechando esta série, vejamos um exemplo simples de comunicação RS485 com o Arduino, usando o transciever SN75176.



terça-feira, novembro 13, 2012

Escolhendo um Shield Ethernet para o Arduino

Se você prestar atenção, perceberá  que os shields para a conexão do Arduino a redes Ethernet se dividem em dois grupos, com faixa de preços diferentes: um baseado no chip W5100 e outro no chip ENC28J60. Para entender a grande diferença entre eles é preciso conhecer um pouco sobre redes locais.



sexta-feira, março 20, 2009

O Protocolo YModem

O protocolo XModem que examinamos nos posts anteriores é voltado à transmissão dos dados de um arquivo de um computador para outro.

O protocolo YModem acrescenta um nível acima do XModem para permitir transferir informações sobre o arquivo, particularmente nome, tamanho e data. Isto possibilita:
  • Receber arquivos com tamanho arbitrário (no XModem o tamanho recebido é sempre múltiplo de 128 bytes).
  • Deixar a recepção mais automática, com o transmissor definindo o nome do arquivo.
  • Realizar a transferência de múltiplos arquivos (modo batch).
O YModem pressupõe a implementação do XModem com pacotes de 1K e CRC.

Uma transferência YModem se inicia da mesma forma que no XModem CRC, com o receptor enviando um caracter 'C' para indicar que está pronto. O transmissor, entretanto, responde com um pacote de número zero que contem as informações sobre o arquivo:
  • nome do arquivo, finalizado por um NUL (0x00)
  • tamanho do arquivo (opcional). O tamanho é transmitido em texto (ex: '1234'); o programa receptor deve desprezar os bytes irelevantes do último pacote recebido.
  • data do arquivo (opcional), separado por um espaço do tamanho. A data do arquivo é transmitida em octal como o número de segundos desde as zero horas GMT de primeiro de janeiro de 1970. A data zero indica que a data não é especificada.
  • outras informações menos usuais.
O receptor deve responder a um pacote zero correto com ACK e em seguida iniciar uma recepção normal XModem CRC, enviando novamente 'C'. A transferência dos dados do arquivo procede normalmente, exceto que o receptor não deve gravar os bytes que ultrapassem o tamanho informado.

Após enviar o ACK do EOT dos dados do arquivo, o receptor deve novamente enviar 'C' e aguardar um novo pacote zero. Um pacote zero com nome de arquivo vazio indica o fim dos arquivos.

Normalmente um programa transmissor YModem permite ao operador especificar uma máscara como nome de arquivo (como 'Arq*.txt'). O programa utiliza o protocolo para enviar todos os arquivos existentes que atendam à máscara. No programa receptor, o usuário apenas inicia a recepção, o programa recebe os arquivos e os salva com os nomes especificados.

Referência

XMODEM/YMODEM PROTOCOL REFERENCE
- Edited by Chuck Forsberg

quarta-feira, março 18, 2009

Aperfeiçoamentos do Protocolo XModem

No post anterior, vimos o XModem "básico". Este protocolo possui algumas limitações:
  • Não prevê como interromper uma transmissão
  • Utiliza caracteres simples (e portanto frágeis) para sinalizações
  • Possui um overhead alto (4 bytes de overhead para 128 bytes de dados)
  • Utiliza uma detecção de erros frágil (checksum)
  • Não informa o tamanho do arquivo
  • Não permite retomar uma transferência interrompida
Veremos aqui alguns aperfeiçoamentos que resolvem algumas destas limitações.

Interrupção da Transmissão

Uma convenção frequente é usar o caracter CAN (0x18) para solicitar a interrupção da transmissão. Quando suportado, o CAN é aceito pelo transmissor no lugar de ACK e NAK e no receptor no lugar de um pacote.

O suporte ao CAN agrava o próximo problema, o uso de caracteres isolados.

Uso de Caracteres Isolados

Idealmente, um protocolo deveria utilizar sempre pacotes para garantir que apenas comunicações validas são interpretadas. O XModem utiliza os seguintes caracteres isolados:
  • ACK: um ACK que for perdido ou danificado causará uma retransmissão, o que é perfeitamente aceitável. Se um ACK espúrio for recebido pelo transmissor isto causará uma dessincronização do número dos pacotes e consequente interrupção da transferência.
  • NAK: se um NAK for perdido ou danificado causará um timeout (salvo se for convertido em ACK). Um NAK espúrio recebido pelo transmissor causará uma retransmissão. Ambos os casos são perfeitamente aceitáveis.
  • EOT: se o EOT for perdido ou danificado ocorrerá uma retransmissão. Entretanto se um ACK espúrio for recebido pelo receptor ele considerará a transmissão encerrada com sucesso, o que é problemático. Para evitar isto, algumas implementações respondem com NAK ao primeiro EOT recebido e aguardam a sua retransmissão para confirmar o fim da transferência.
  • CAN (se usado): se um CAN for perdido ou danificado, a solicitação de interrupção não será bem sucedida. Se um CAN espúrio for recebido, a transferência será interrompida. Embora estas duas situações não sejam críticas elas são desagradáveis. Uma solução é enviar uma sequência de CANs e só interromper ao receber dois (ou mais) CANs consecutivos.
Overhead e XModem-1K

O pacote do XModem possui 132 bytes, dos quais 128 são de dados. Para reduzir o overhead existe o XModem-1K, que possui 1024 bytes de dados nos pacotes. Para distinguir os pacotes com 1024 bytes dos com 128, o byte inicial é substituído de SOH (0x01) para STX (0x02). O resto do protocolo é idêntico.

O receptor do XModem-1K deve estar preparado para receber pacotes dos dois tipos. Isto permite que o transmissor otimize o final da transferência, selecionando o tamanho mais apropriado para a quantidade de bytes a enviar.

XModem-CRC

O checksum usando no XModem é frágil. Intuitivamente, temos somente 256 valores possíveis para o checksum o que propicia acertar o checksum "por acaso".

O XModem-CRC substitui o checksum por um CRC de 16 bits, aumentando o tamanho do pacote de um byte. Explicar o funcionamento do CRC é uma tarefa longa (inclusive assunto de livros), porém basta considerar que não somente estamos falando de 65536 valores possíveis mas que o algorítmo garante a detecção de todos os erros de um ou dois bits no pacote.

O CRC utilizado é o chamado CRC CCITT, baseado no polinômio X^16 + X^12 + X^5 + 1. No pacote do XModem o CRC é transmitido com o byte mais significativo primeiro. Existem várias formas de calcular o CRC, há anos utilizo um método que dispensa o deslocamento bit-a-bit e o uso de tabelas, descrito no artigo The Great CRC Mystery de Terry Ritter, publicado na revista Dr Dobb's de Fevereiro de 1986.

O uso de CRC é uma iniciativa do receptor. Para isto ele transmite inicialmente o caracter 'C' ao invés de NAK. Para compatibilidade com o XModem Checksum, após algumas tentativas o receptor deve transmitir NAKs e utilizar checksum.

O uso de pacotes de 1K e do CRC são independentes. Entretanto, por motivos de confiabilidade não é recomendado usar pacotes de 1K com checksum.

O Tamanho do Arquivo

Uma vez que o XModem não informa o tamanho do arquivo e utiliza pacotes de tamanho fixo, o arquivo recebido tem sempre tamanho múltiplo de 128 bytes. Isto vem de uma característica do sistema CP/M-80 (onde o XModem foi inicialmente implementado).

Para resolver esta limitação e acrescentar algumas facilidades adicionais, foi criado o protocolo YModem, que é uma evolução direta do XModem e veremos no próximo post.

Retomada de Transferências.

Caso uma transferência XModem (ou YModem) seja interrompida, ela tem que ser recomeçada do seu início. Isto criava uma certa tensão ao ultrapassar a marca dos 70% na transferência de arquivos grandes (tensão esta que sobrevive nos tempos da internet devido ao não suporte universal para a retomada no protocolo FTP).

Isto viria a ser corrigido em outros protocolos, notadamente no ZModem. Entretanto, apesar do nome, o ZModem tem pouca semelhança com o XModem e é bastante complexo e provavelmente não vou abordá-lo nesta série de posts.

Referência

XMODEM/YMODEM PROTOCOL REFERENCE
- Edited by Chuck Forsberg

segunda-feira, março 16, 2009

O Protocolo XModem

O protocolo XModem é o pai de uma série de protocolos de transferência de arquivos que foram muito populares nos tempos da comunicação "banda magra" e que ainda são usados em dispositivos embarcados.

Já falei um pouco sobre ele antes (aqui e aqui) mas desta vez vou entrar em um pouco mais de detalhes.

A Vida On-Line Antes da Internet

A comunicação entre computadores via linha discada é algo bastante antigo. No mundo dos computadores pessoais, em 1978 (pouco depois do surgimento dos primeiros micros e muito antes de Tim Berners-Lee inventar a World Wide Web), Ward Christensen estabeleceu a primeira BBS.

Nos anos 80 um acessório cool era o modem. Inicialmente uma caixa externa e posteriormente uma placa de expansão, os modems da época permitiam comunicar via linha telefônica a velocidades como 300, 1200 e (mais tarde) 2400 bps (o que corresponde respectivamente a 30, 120 e 240 bytes por segundo).

Placa Listic TD1222/BV: 300, 1200, 1275 e 2400 bps!

As conexões eram bastante precárias. Não era incomum precisar algumas tentativas para os modems das duas pontas se entenderem. Conseguida a conexão, eram comuns três tipos de ocorrência:
  • a "geração espontânea" de caracteres, quando ruídos da linha eram errôneamente decodificados pelo modem;
  • o "sumiço" de caracteres, quando a comunicação entre as pontas era momentaneamente perdida; e
  • a alteração de caracteres em outros, devido à distorção do sinal.
E é claro que ocorriam também desconexões inesperadas.

O Protocolo XModem

Nas palavras de seu criador, o protocolo XModem surgiu de forma não planejada, para resolver um problema imediato de transferência de arquivos:

"It was a quick hack I threw together, very unplanned (like everything I do), to satisfy a personal need to communicate with 'some other' people"

O protocolo XModem supõe que exista um canal transparente entre os dois computadores, que permita o tráfego de qualquer byte de 0x00 a 0xFF. Alguns caracteres são utilizados pelo protocolo porém podem estar presentes dentro dos dados transferidos (o que é uma das vulnerabilidades do protocolo):

SOH 0x01
EOT 0x04
ACK 0x06
NAK 0x15

Na transferência de dados é utilizado um pacote com tamanho fixo de 132 bytes

onde
  • SOH (0x01) indica o início do pacote
  • PAC é o número do pacote. Este número começa com 1 e é incrementado a cada pacote. Após o pacotes 255 segue o pacote 0 (ou seja, o número do pacote é um byte que é incrementado ignorando o overflow);
  • ~PAC é o complemento de um do número do pacote. Pode ser calculado como 255-PAC ou invertendo cada bit
  • Dados são os dados transferidos, com tamanho fixo de 128 bytes.
  • CHK é o checksum, calculado somando todos os bytes de dados igonorando o overflow.
A dinâmica de comunicação é bastantes simples:
  • O receptor envia periodicamente NAK para indicar ao transmissor que está pronto para receber (isto permite disparar as pontas em qualquer ordem).
  • Ao escutar o NAK, o transmissor envia o primeiro pacote (de número 1)
  • Ao receber com sucesso um pacote, o receptor envia ACK. Se o pacote foi recebido incorretamente, envia NAK
  • Ao receber NAK o transmissor deve retransmitir o último pacote. Ao receber ACK o transmissor incrementa o número do pacote e envia o pacote seguinte (se existir)
  • Ao receber ACK do último pacotes, o transmissor envia EOT. O receptor deve confirmar o recebimento do EOT através de ACK.
A figura abaixo mostra esta dinâmica.


Considerações do Transmissor

O transmissor é bastante simples. O pacote a transmitir deve ser preferialmente montado antes do inicio da sua transmissão, para evitar pausas no meio da transmissão do pacote. Os momentos adequados para montar um pacote é no início da comunicação e após receber o ACK de um pacote. Antes de enviar o pacote (ou melhor ainda, antes de enviar o último caracter do pacote) o transmissor deve limpar a sua fila de recepção para diminuir a possibilidade de "lixo" ser interpretado como a resposta do receptor.

Embora costumeiramente o transmissor costume dar um timeout na resposta do receptor, isto não é obrigatório. Os timeouts no receptor são suficiente para recuperar as situações de erro.

Considerações do Receptor

Logo antes de enviar uma resposta o receptor deve limpar a sua fila de recepção, para diminuir a possibilidade de "lixo" ser interpretado. Na recepção do pacote, deve ignorar caracteres até receber a marca de início (SOH), dando timeout caso a marca não seja recebida dentro de um certo tempo. A partir deste ponto deve aguardar todos os caracteres do pacote, dando timeout caso o caracter seguinte não seja recebido dentro de 1 segundo. Em todos os casos de timeout o receptor deve enviar NAK.

Recebido um pacote, deve ser verificada a sua consistência, conferindo o número do pacote com seu complemento e o checksum dos dados. Se o pacote não estiver íntegro, o receptor deve enviar NAK.

Recebido um pacote íntegro, o receptor deve examinar o número do pacote:
  • Se for o pacote esperado, gravar os dados, incrementar o número do pacote esperado e enviar ACK.
  • Se for o pacote anterior ao esperado, enviar ACK. Isto ocorre se um ACK anterior foi extraviado ou danificado, causando a retransmissão do pacote.
  • Se for um pacote diferente do esperado ou o anterior, abortar a transferência. O transmissor e receptor perderam o sincronismo na numeração dos pacotes, possivelmente pelo transmissor achar que recebeu o ACK de um pacote que não foi recebido corretamente.
Concluindo

O protocolo XModem "básico" que foi apresentado é suficiente para muitas aplicações, principalmente as que envolvem conexões com baixas taxa de erro e volumes não elevados de dados. Ele possui, entretanto, algumas limitações. No próximo post da série vamos ver alguns aperfeiçoamentos populares para o XModem.

Referência

XMODEM/YMODEM PROTOCOL REFERENCE
- Edited by Chuck Forsberg

segunda-feira, setembro 17, 2007

PC Assembler: A Odisséia

Como comentei no post anterior, ao escrever o post sobre a minha nova impressora eu tive a idéia de postar o texto do livro PC Assembler. Parecia fácil, mas foi uma verdadeira odisséia!

Procurando a Midia

Eu estava certo de ter uma cópia no HD. Não achei. Bem, com certeza está um dos muitos CDs de backup. Também não. Nos disquetes? Nada.

Hora de enfrentar o pó da última gaveta do armário de roupas, onde escondi algumas lembranças para escapar do lixo. E achei o disco aí ao lado. Alguns vão reconhecer que é um disquete de 5 1/4", mas poucos vão perceber que tem o corte de desproteção contra gravação nos dois lados. Para quem não entende de arqueologia, este é um disquete de Apple ][.
Parenteses: o mundo em geral pode idolatrar o Steve Jobs mas para os engenheiros Steve Wozniac é o cara. Uma das suas maiores criações foi a interface de disco do Apple ][, que permitiu a comercialização da unidade de disco por um preço extremamente baixo para a época (mesmo com a margem imensa colocada pelo Jobs). Embora a unidade seja para disquetes de face simples, o fato dela não usar o furo de índice (o furo pequeno redondo à direita do centro), permitia usar o segundo lado fazendo o corte na lateral e colocando o disco de ponta cabeça.

O Micro

Na época que eu escrevi o livro, meu computador principal era um Unitron apII TI. Alguns anos depois, evoluí para um TK3000//e da Microdigital que corresponde ao Apple //e. Foi este segundo equipamento que fui buscar na casa do meu pai para ler o disquete. Na foto ao lado, ele já foi devidamente limpo (?).

Uma das várias idéias que o Apple ][ popularizou (e a IBM seguiu) foi o uso de slots para placas de expansão. Como pode ser visto na foto ao lado, o meu micro tem uma quantidade grande de placas.

Separada das demais, na esquerda, está a placa "80 colunas" (o default do Apple ][ é apresentar na tela 40 caracteres por linha). No slot 1 a interface para impressora paralela. No slot 4 um clone do Softcard Z80, um dos primeiros hardwares de sucesso da Microsoft (essencial pois o disco está em formato CPM/80). No slot 6 a mencionada interface de disco, Por último, no slot 7, a minha expansão de memória de 128 KBytes (uma extravagância, que eu usava como RamDisk).

Para completar, o meu primeiro monitor: uma TV muito mal adaptada (fiz a adaptação de forma provisória quando cheguei em casa com o Unitron, funcionou e ficou deste jeito até hoje). No lado direito, duas unidades de disco.

Hora de cruzar os dedos, colocar um disco de CP/M e ver o que acontece. Ao lado está o resultado, aquele "A>" lembra alguma coisa?

Exceto por um monte de maus contatos (principalmente no teclado), está tudo funcionando!

Obs.: Para o caso de alguém estar prestando a atenção, a tela está com 40 colunas, devido a um mau-contato na placa "80 colunas". Neste momento a placa Z80 estava no slot 3 (esquecimento meu). Quando a placa "80 colunas" passou a funcionar a Z80 parou. Perdi pelo menos uma hora, até lembrar que a placa Z80 devia ir no slot 4 para não conflitar com a "80 colunas".

Recuperando os Dados

Ok, eu consigo ler os disquetes antigos, e agora? É hora de transferir os dados pela serial. A placa ao lado é uma ICA (Interface de Comunicação Assíncrona), lembrança dos tempos da Humana Informática e dos posts nas BBSs, comunicando a 300 bps ou nos esquisitos 1200/75 bps.

Com a placa no slot 2, é hora de usar o meu programa de comunicação (reparar no copyright na tela ao lado). O programa trabalha no capenga Xmodem Checksum, o que exige um pouco de esforço do Hyperterminal (que insiste em tentar primeiro o Xmodem CRC e só passa para o Checksum depois de um l_o_n_g_o tempo). Após alguns arquivos a paciência acabou e preferí alterar um dos meus inúmeros utilitários de PC para suportar o Xmodem Checksum.

Mas, Espere: Isto Não É Tudo!

Ao final tenho 20 arquivo recebidos no PC (como no CP/M são apenas 56K de Ram, o texto era quebrada em pedaços de no máximo 20K para evitar que o editor ficasse acessando o disco o tempo todo). A tela ao lado mostra (em hexa) o início de um deles (clique para ampliar). Que codificação é essa? Os textos foram gerados com o WordStar, que era um editor WYSIWYG... se você está escrevendo em inglês numa impressora não gráfica. À medida que o texto é editado, o WordStar automaticamente distribui espaços para justificar o texto dentro das margens. Para isto ele usa o bit 7 dos caracteres para marcar o final das palavras e os espaços, hifens e quebras de linha introduzidos. O que realmente estraga tudo é que para gerar as acentuações eu digitava sequências "caracter backspace acento" (felizmente tanto o Unitron como o TK3000 tinham teclados programáveis, o que me permitia digitar estas sequências de uma forma mais automática).

Reparar também no ".he xxxx". A formatação para impressora era um passo separado, as linhas começando com '.' são comandos para definir cabeçalho, rodapé, pular página, etc. Para conseguir obter um TXT com o conteúdo foi preciso fazer um programa para limpar tudo isto.

Lembrando, o resultado final pode ser baixado daqui.

01/08/13: O texto, em formato de eBook pode ser baixado  do SkyDrive através do ícone no alto à direita ("Arquivos do Blog") ou pelos ícones abaixo:



terça-feira, setembro 26, 2006

Pacotes

Uma das tarefas dos protocolos do nível de transporte, como o TCP, é garantir a entrega dos pacotes na ordem em que forem enviados. À primeira vista, isto parece desnecessário. Afinal, como os pacotes podem chegar no destino em ordem diferente da em que foram enviados? A resposta é que numa rede complexa existem vários caminhos entre a origem e o destino e nada obriga pacotes consecutivos a seguir o mesmo caminho.

Embora não tenha chegado ao extremo de chegar fora de ordem, meu último pedido na Amazom mostrou como pacotes consecutivos podem experimentar condições bem diferentes. Como mencionei antes, um dos livros foi enviando em um dia e o resto do pedido no dia seguinte. O livro que veio isolado estabeleceu um novo record, chegando em sete dias corridos. Já o resto do pedido chegou somente ontem (26 dias corridos, o que ainda é um prazo bom para o standard shipping).

segunda-feira, março 13, 2006

Protocolos de Comunicação

Um dos trabalhos que andei fazendo envolveu implementar o protocolo XModem em coletores de dados. Os protocolos de comunicação são algo que me facinam há muito tempo (afinal, os meus dois primeiros empregos estavam relacionados a comunicação de dados).

Um protocolo de comunicação é um conjunto de convenções usado para mover informações entre dois equipamentos. Os principais objetivos de um protocolo é cuidar do endereçamento (no caso de ligações multiponto) e garantir a integridade das informações. Embora isto não seja muito visível atualmente, a comunicação à distância está sujeita a grande quantidade de erros. A maioria dos equipamentos atuais (inclusive modems) possuem protocolos embutidos que detectam e recuperam erros de forma quase transparente. No começo da comunicação via modem, os erros de comunicação eram mais evidentes. Antes da internet, a vida on-line era feita através das BBSs (computer Bulletin Board Systems) que enviavam os textos sem protocolo, era comum caracteres sumirem, mudarem ou mesmo surgirem do nada. Um bom protocolo de comunicação precisa ter uma boa resistência a estas ocorrências.

A maioria dos protocolos de comunicação se baseiam nos mesmos conceitos. O primeiro destes conceitos é um pacote. O início de um pacote é normalmente marcado por um caracter especial. O final de um pacote pode ser determinado de três formas básicas: tamanho fixo em função do tipo de pacote, um campo de tamanho dentro do pacote ou um caracter especial no fim. Para detectar erros, é comum acrescentar-se ao final do pacote um ou mais bytes que são calculados a partir dos demais. O receptor recalcula estes bytes e confere com os recebidos, se não forem iguais é porque um erro ocorreu. Exemplos comuns de detecção de erro são os checksums (soma desprezando vai-um) e os crcs (cyclic redundancy codes). Alguns sistemas utilizam códigos de correção de erro (ECC) que além de detectar erros pode corrigir uma parte deles. O mais comum entretanto, é recuperar uma situação de erro solicitando a retransmissão do pacote. Na forma mais simples, a cada pacote recebido o receptor envia um caracter que indica se o pacote foi recebido corretamente (ACK - Acknoledge) ou com erro (NAK - NonAcknoledge). Para tratar o caso em que um ACK, NAK ou pacote é perdido no meio do caminho normalmente os pacotes são numerados e as recepções são temporizadas. Por exemplo, se o transmissor envia o pacote 31 e o receptor o recebe corretamente mas o ACk é perdido, o transmissor irá dar um timeout e re-enviar o pacote 31. O receptor ignora o pacote duplicado e re-envia o ACK para que o transmissor envie o pacote seguinte.

Como descrito acima, é comum os protocolos darem significado especial para alguns códigos. A tabela ASCII reflete isto, reservando os primeiros 32 códigos para os caracteres de controles, alguns como nome como Start of Transmission (STX), Start of Header (SOH), End of Text (ETX), Acknoledge (ACK) e Non-Acknoledge (NAK). Existem dois tratamentos comuns para os códigos de controle presentes no interior dos pacotes (nos dados). O mais comum é permitir a presença de caracteres de controle dentro de pacotes; isto é mais simples e eficiente porém pode causar problemas quando ocorre algum erro e um caracter de dado é tratado erroneamente como caracter de controle. Outra opção é utilizar um caracter de prefixo (o código ASCII tem o DLE com esta finalidade). Por exemplo, um protocolo pode convencionar que para enviar um SOH nos dados deve ser enviado DLE A e que para enviar DLE deve ser enviado DLE P ou DLE DLE.

Uma outra coisa impressionante é como determinados protocolos criados de forma despretenciosa acabam virando padrões e se mantendo vivos por décadas. O protocolo XModem foi criado nos anos 70, por uma pessoa que queria passar arquivos de um micro para outro e permanece em uso até hoje, apesar de ter uma série de limitações.

A implementação de protocolos envolve conceitos e técnicas interessantes de programação. Em ambientes sem multiprogramação muitas vezes o programa precisa analisar os caracteres recebidos um a um; após examinar cada caracter precisa retornar a um loop principal onde outras funções são tratados. Exemplificando em um pseudo C:

// loop principal
while ( )
{
  TrataTeclado ();
  AtualizaTela ();
  if (RecebeuCaracter)
    TrataCaracter ();
  ...
}

Neste caso é inevitável implementar o protocolo como uma máquina de estados. Por exemplo, o pacote básico do XModem começa com um caracter SOH e tem 132 caracteres:

enum { ESPERANDO_SOH, RECEBENDO_PACOTE } estado = ESPERANDO_SOH;
byte pacote [132];
int i;

TrataCaracter (byte c)
{
  switch (estado)
  {
    case ESPERANDO_SOH:
      if (c == SOH)
      {
        estado = RECEBENDO_PACOTE;
        i = 0;
        pacote[i++] = c;
      }
      break;

    case RECEBENDO_PACOTE:
      pacote[i++] = c;
      if (i == 132)
      {
        TrataPacote();
        estado = ESPERANDO_SOH;
      }
      break;
  }
}

Em um ambiente multitarefa, é possível fazer algo mais legível como:

byte pacote [132];
int i;

RecebePacote ()
{
  While (RxCar() != SOH)
    ;
  pacote[0] = SOH;
  for (i = 1; i < 132; i++)
    pacote[i] = RxCar ();
  TrataPacote ();
}

(Obviamente estes exemplos são artificialmente simples, existem variações do XModem , não estou tratando timeouts, etc.)

No caso de você estar desenvolvendo para um sistema sem sistema operacional (um com sistema capenga como o DOS), você provavelmente vai ter uma fila para sincronizar a recepção em tempo de interrupção com o consumo dos bytes. Se você quiser implementar multitarefa por contra própria, vai ter que implementar um semáfaro para a fila (neste caso o RxCar() acima faz uma operação P na fila e fica bloqueado se a fila estiver vazia, até que a interrupção coloque um caracter na fila e faça um operação V).

Se tudo isto parece longe do seu dia a dia, lembre-se que você está lendo isto em um site através (no mínimo) dos protocolos TCP/IP.