Fonte: José Carlos Macoratti/IMasters
O CRC – Cyclic redundancy check, ou verificação de redundância cíclica, é um código detector de erros cujo algoritmo é muito usado em protocolos de comunicação diferentes, empacotamento e desempacotamento de algoritmos para garantir a robustez dos dados.
A ideia por trás do CRC é simples – calcular um checksum original (sequência de verificação de quadros) para cada quadro de dados, baseada em seu conteúdo, e colar o checksum no final de cada mensagem. Uma vez que os dados são recebidos, é possível realizar o mesmo cálculo e comparar os resultados – se os resultados são semelhantes, a mensagem é válida.
O CRC é calculado e anexado na informação a transmitir (ou armazenar), sendo verificado após a recepção ou o acesso, para confirmar se não ocorreram alterações.
O CRC é popular por ser simples de implementar em hardware binário, simples de ser analisado matematicamente, e pela eficiência em detectar erros típicos causados por ruído em canais de transmissão.
A utilidade do CRC advém das seguintes propriedades:
- Como todos os bits são usados no cálculo do CRC, a mudança em apenas um bit provoca uma mudança no CRC.
- Mesmo mudanças pequenas nos dados levam a CRCs muito diferentes. Experiências com o CRC-32 (usando polinômios de 32 bits) mostram que é muito raro que a introdução de erros nos dados não seja detectada pelo CRC.
- A probabilidade de qualquer dos 232 valores possíveis para o CRC é praticamente uniforme.
Existem diferentes tipos de CRC que podem ser calculados: CRC-32, CRC-16, CRC-12, CRC-8 etc.
Para realizarmos cálculos envolvendo o CRC temos que utilizar o namespace System.Security.Cryptography e neste artigo eu vou calcular o CRC de strings e arquivos.
Vamos abrir o Visual C# 2010 Express Edition e, no menu File-> New Poject, selecione o Template Windows Forms Applicacion com o nome Calculando_CRC.
No menu Project -> Add Class, inclua uma classe com o nome CrcStream com o seguinte código:
using System.IO; namespace Macoratti { /// <summary> /// Encapsula um <see cref="System.IO.Stream" /> para calcular o checksum CRC32 /// em tempo de execução /// </summary> public class CrcStream : Stream { /// <summary> /// Encapsula um <see cref="System.IO.Stream" />. /// </summary> /// <param name="stream">O stream para calcular o checksum.</param> public CrcStream(Stream stream) { this.stream = stream; } Stream stream; /// <summary> /// Obtem o stream. /// </summary> public Stream Stream { get { return stream; } } public override bool CanRead { get { return stream.CanRead; } } public override bool CanSeek { get { return stream.CanSeek; } } public override bool CanWrite { get { return stream.CanWrite; } } public override void Flush() { stream.Flush(); } public override long Length { get { return stream.Length; } } public override long Position { get { return stream.Position; } set { stream.Position = value; } } public override long Seek(long offset, SeekOrigin origin) { return stream.Seek(offset, origin); } public override void SetLength(long value) { stream.SetLength(value); } public override int Read(byte[] buffer, int offset, int count) { count = stream.Read(buffer, offset, count); readCrc = CalculateCrc(readCrc, buffer, offset, count); return count; } public override void Write(byte[] buffer, int offset, int count) { stream.Write(buffer, offset, count); writeCrc = CalculateCrc(writeCrc, buffer, offset, count); } uint CalculateCrc(uint crc, byte[] buffer, int offset, int count) { unchecked { for (int i = offset, end = offset + count; i < end; i++) crc = (crc >> 8) ^ table[(crc ^ buffer[i]) & 0xFF]; } return crc; } static private uint[] table = GenerateTable(); static private uint[] GenerateTable() { unchecked { uint[] table = new uint[256]; uint crc; const uint poly = 0xEDB88320; for (uint i = 0; i < table.Length; i++) { crc = i; for (int j = 8; j > 0; j--) { if ((crc & 1) == 1) crc = (crc >> 1) ^ poly; else crc >>= 1; } table[i] = crc; } return table; } } uint readCrc = unchecked(0xFFFFFFFF); /// <summary> /// Obtem o checksum CRC dos dados que foram lidos pelo stream /// </summary> public uint ReadCrc { get { return unchecked(readCrc ^ 0xFFFFFFFF); } } uint writeCrc = unchecked(0xFFFFFFFF); /// <summary> /// Obtem o checksum CRC dos dados que foram escritos para o stream /// </summary> public uint WriteCrc { get { return unchecked(writeCrc ^ 0xFFFFFFFF); } } /// <summary> /// Reseta a leitura e escrita dos checksums. /// </summary> public void ResetChecksum() { readCrc = unchecked(0xFFFFFFFF); writeCrc = unchecked(0xFFFFFFFF); } } }
autor: http://www.codeproject.com/Members/reinux
Agora vamos definir no formulário form1.cs uma interface bem simples, na qual iremos informar o nome do arquivo e calcular o seu CRC.
Abaixo, vemos o formulário que usa os controles TextBox e Button:
No evento Click do botão de comando – Calcula CRC -, vamos incluir o código que usa a classe CrcStream para calcular o CRC do arquivo:
using System; using System.Windows.Forms; using System.IO; using Macoratti; namespace Calculando_CRC { public partial class Form1 : Form { public Form1() { InitializeComponent(); } FileStream file = null; CrcStream stream = null; private void btnCalculaCRC_Click(object sender, EventArgs e) { if (txtArquivo.Text == string.Empty) { MessageBox.Show("Informe o nome do arquivo."); return; } string arquivo = txtArquivo.Text; //Abre um fluxo de stream e o encapsula em um CrcStream try { file = new FileStream(arquivo, FileMode.Open); stream = new CrcStream(file); } catch (Exception ex) { MessageBox.Show("Erro ao acessar o arquivo : " + ex.Message); return; } //Usa o arquivo - neste caso le o arquivo como uma string StreamReader reader = new StreamReader(stream); string texto = reader.ReadToEnd(); //Imprime o checksum calculado txtCRC.Text = stream.ReadCrc.ToString("X8"); } } }
Executando o projeto, iremos obter para um determinado arquivo informado:
Pegue o projeto completo aqui: Calculando_CRC