PTC29008: Projeto 1: Sincronização e enquadramento

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar

Próxima aula




O enquadramento é uma função do protocolo de enlace responsável por delimitar quadros na interface com a camada física. Deve-se ter em mente que a camada física oferece um serviço de envio e recepção de sequências de bytes sem qualquer estrutura. Cabe à camada de enlace delimitar as unidades de dados de protocolo (PDU) dentro dessas sequências de bytes.

Existe mais de uma abordagem para delimitar quadros (ver mais no capítulo 11 de Data Communications and Computer Networks, de Behoruz Forouzan, e capítulo 5 de Redes de Computadores e a Internet, de James Kurose e Keith Ross):

Abordagem Descrição Exemplos
Quadros de tamanho fixo / duração definida Quadros têm sempre mesmo comprimento ou duração ATM, TDMA-based
Sentinela padrão de bits/bytes delimita quadros PPP, HDLC
Contador / duração Cabeçalho contém duração ou comprimento do quadro IEEE 802.11
Presença/ausência de portadora Ausência de portadora delimita quadros IEEE 802.11, IEEE 802.3

Atividade

  1. Escolha uma abordagem viável para o protocolo de enlace a ser desenvolvido
  2. Modele a abordagem usando uma máquina de estados finitos de comunicação, de forma a facilitar sua implementação. Alguns textos introdutórios sobre MEF:
  3. Implemente a abordagem escolhida

Implementação do enquadramento

A técnica de enquadramento escolhida é a do tipo sentinela. Mais especificamente, escolheu-se a versão dessa técnica implementada pelo protocolo PPP:

  • Usa-se a flag 7E (01111110) como delimitador de quadros
  • Usa-se um byte de escape 7D (01111101) para preenchimento de octeto
  • O transmissor deve fazer pelo menos o escape dos bytes 7E e 7D que aparecerem no conteúdo do quadro
    • Cada byte que sofrer o escape deve ser modificado por meio de um XOR 20. Ex: se o byte a sofrer escape for 7E, ele deve ser modificado para 5E (7E XOR 20 = 5E).


Uma máquina de estados para o receptor é esta:

PTC-20162-Fsm-rcv.jpg

Versão alternativa da MEF

PTC-Sentinela2.jpg


Além disso, uma possível implementação dessa função do protocolo poderia ser esta:

#ifndef FRAMING_H
#define FRAMING_H

#include <cstdint>
#include "Serial.h"

class Enquadramento {
 public:
  Enquadramento(Serial & dev, int bytes_min, int bytes_max);
  ~Enquadramento();
 
  // envia o quadro apontado por buffer
  // o tamanho do quadro é dado por bytes 
  void envia(char * buffer, int bytes);
 
  // espera e recebe um quadro, armazenando-o em buffer
  // retorna o tamanho do quadro recebido
  int recebe(char * buffer);
 
 private:
  int min_bytes, max_bytes; // tamanhos mínimo e máximo de quadro
  Serial & porta;  
  char buffer[4096]; // quadros no maximo de 4 kB (hardcoded)
 
  enum Estados {Ocioso, RX, ESC};
 
  // bytes recebidos pela MEF até o momento  
  int n_bytes; 
 
  // estado atual da MEF
  int estado;
 
  // aqui se implementa a máquina de estados de recepção
  // retorna true se reconheceu um quadro completo
  bool handle(char byte);
 
};

#endif


A implementação da máquina de estados

A declaração acima sugere implementar a MEF a partir do método handle da classe Enquadramento. Esse método deve ser executado para cada byte recebido, representando o tratamento de um evento pela MEF. Uma forma usual e direta de implementar uma MEF faz uso de uma estrutura do tipo switch-case. Essa abordagem se baseia em um modelo de programação estruturada. Basicamente ele depende de um algoritmo que executa um procedimento do sistema dependendo de seu estado atual e do evento. A seleção do procedimento se faz com uma estrutura switch-case. O exemplo abaixo mostra o esqueleto de uma MEF implementada usando essa técnica.

// o tratador de eventos de uma MEF hipotética
// A MEF aqui representada nada faz de útil ... 
bool Enquadramento::handle(char byte) {
  switch (estado) {
    case Ocioso: // estado Ocioso
      estado = RX; // muda para RX
      break;
    case RX: // estado RX
      estado = ESC; // muda para ESC
      break;
    case ESC: // estado ESC
      estado = Ocioso; // muda para Ocioso
      break;
  }
}


Atividade

algumas coisas para ajudar
#include <iostream>
//#include <iomanip>
#include <fstream>
#include <stdio.h>
#include <errno.h>
#include "Enquadramento.h"

using namespace std;

void dump(char * buffer, int len) {
   int m = 0, line = 0;

    while (m < len) {
        printf("%02X: ", line*16);

        for (int n=0; n < 16 and m < len; n++, m++) {
            int x = (unsigned char)buffer[m];
            printf("%02X ", x);
        }
        puts("");
        line++;
    }        
}

int main(int argc, char * argv[]) {
  Serial dev("/dev/ttyUSB0", B9600);

  Enquadramento proto(dev, 8, 32);
  char quadro[32];

  proto.envia("1234567890", 10);

  // ou:
  // int bytes = proto.recebe(quadro);
  // dump(quadro, bytes);
}
main.cpp para enviar ou receber um único quadro


  1. Implemente a MEF de recepção do enquadramento
  2. Escreva um programa que transmita pela serial sequências de caracteres como estas:
    ~abcedf1234567~
    ~abcedf}^1234567~
    ~abcedf012}]9876~
    ~}]}^}]}]}]}^}^}^}]}]}]~
    
    ... e use seu programa de recepção para recebê-las pela serial e identificar os quadros recebidos. Use o serialemu para conectar as seriais desses dois programas.
  3. Escreva o método envia da classe Enquadramento, e modifique o programa de teste de envio para usá-lo para transmitir sequências arbitrárias de bytes.
  4. Teste a capacidade de recuperar o sincronismo da técnica de enquadramento escolhida. Para isso, modifique o programa de teste para que, propositalmente, transmita quadros com erro de delimitação.