Protocolo para transmissão de arquivos via porta serial.

Esse foi um dos trabalhos da disciplina de Redes de Computadores I em que o objetivo era a implementação de um protocolo para transferir arquivos via porta serial. Abaixo está a descrição de como deveria ser a implementação e a construção do cabo serial.

TRABALHO PRÁTICO – REDES I

Objetivo: Elaborar um protocolo de rede baseado em três camadas: física (cabo), enlace e aplicação. A camada física consistirá de um cabo serial conectado às portas serias de dois microcomputadores. A confecção do cabo ficará a cargo da dupla. O desenvolvimento do protocolo deverá seguir a implementação da camada física, através da concepção do cabo de comunicação, a camada de enlace, através da implementação das características desta camada (Controle de fluxo, Detecção e correção de erros) e da implementação da camada de aplicação através do desenvolvimento de um software de transferência de arquivos.

O protocolo de comunicação (com suas camadas) poderá ser implementado tanto na linguagem de programação C, C++ e Java, ficando restrito aos sistemas operacionais Linux e FreeBSD.

Para a construção do cabo siga o modelo:

Instruções para compilar e executar o programa:

Primeiramente você devera conectar dois computadores através do cabo serial descrito acima. Após isso copie os arquivos fontes para os dois computadores. Antes de compilar você devera escolher o arquivo a ser transferido e inserir seu nome com a extensão no arquivo fonte main.c. Veja a imagem:

Para implementação foi usado Linux (Ubuntu). Caso queira rodar em sistemas Windows você deverá fazer as alterações necessárias.

Para compilar utilize o gcc pela linha de comando, entre em um terminal e digite:

# gcc -o protocolo main.c base64.c base64.h declaracoes.h definicoes.c serial.c serial.h

Após compilar os arquivos será gerado o executável, ainda no terminal digite:

# ./protocolo

Você devera ver uma tela como essa:

Você deverá escolher uma opção para o primeiro computador e outra opção para o segundo computador. De preferência escolha a opção 2″ Receber” para o primeiro computador e a opção “1 Enviar” para o segundo computador. Após isso o protocolo iniciara a transferência do arquivo, quando terminado, será impressa uma mensagem  de transferência completa.

Para maiores informações a respeito do funcionamento do protocolo e detalhes da sua implementação, consulte o “Documento de Requisito”  que  está disponivel ao final desse post.

Código Fonte do Protocolo.

Arquivo main.c

/*********************************
Autores: Fernando Krein Pinheiro
         Clovis Reschke
Data: 20/05/2009
Linguagem: C
========= IMPORTANTE ===========
O código esta livre para usar,
citar e compartilhar desde que
mantida sua fonte e seus autores.
Obrigado.
********************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "declaracoes.h"
#include "base64.h"
#include "serial.h"

int main()
{
        char nome_arq[TAM_DADOS];
        char buffer[TAM_DADOS+(sizeof(int)*3)];
        int op=0;
        FILE * saida;
        char *ptr_arq;

	system("clear");
        printf("\n\t========================  SFTP  ==========================\n");
	printf("\t=====  Protocolo de Transferencia de Arquivos via Serial ====\n");
	printf("\t=========================================================\n\n\n");
	printf("Digite a Opcao:\n1 Enviar\n2 Receber\n[ ]\b\b");
	scanf("%d",&op);

	switch (op)
		{
		case 1 :
			{

			    int tamanho=0,indice=0,tipo=0,status=0,aux=0,cont=0;
			    pacote *p = (pacote*) malloc (sizeof(pacote));
			    strcpy(nome_arq,"Nome_do_seu_arquivo.rar");
                            char *ptr_arq = abre_arq(nome_arq);

                            p =  monta_pacote(indice,ptr_arq,1,nome_arq);
			    	   tamanho = p->tamanho;
                        	    pacote_to_string(p,buffer);
				    status = escrever_porta(buffer);
				    bzero(buffer, TAM_DADOS);
				    while(cont != 5)
				{
					sleep(1);
					status = ler_porta(buffer);
					if(status > 0)
					   cont=5;
					else
					{
					printf("Sem comunicação\n");
					cont++;
					}
				}
				    if(!teste_ACK(p))
				     {
					printf("Chegou ACK");
				     }
				    else
				    {
					printf("Manda novamente");
				    }
			break;
			}
	   case 2:
		{
			int tamanho=0,indice=0,tipo=0,status=0,aux=0,cont=0;
			pacote *p = (pacote*) malloc (sizeof(pacote));

			do{
			 	while(cont != 5)
				{
					sleep(3);
					status = ler_porta(buffer);

					if(status > 0)
					   cont=5;
					else
					{
					printf("Sem comunicação\n");
					cont++;
					}
				}

			p = string_to_pacote(buffer);

			if(!testa_crc(p,buffer))
			{
				if(p->tipo == 1)
				{
					tamanho = p->tamanho;
		 			ptr_arq = aloca_mem(tamanho);
					strcpy(nome_arq,p->dados);
					indice=0;
				}
				else if(p->tipo == 2 )
				{
					for(aux=0;aux < 100;aux++,indice++)
					ptr_arq[indice] = p->dados[aux];
        			}
				   sleep(2);
			           p =  monta_pacote(indice,ptr_arq,3,nome_arq);
                         	   pacote_to_string(p,buffer);
				   status = escrever_porta(buffer);

		         }
			printf("\n\n%d",indice);
		   }while(indice < tamanho);
			escreve_arquivo(ptr_arq,nome_arq,tamanho);
		 break;
		}
}
return 0;
}

Arquivo serial.c

Esse fonte é responsável por toda configuração da porta serial, caso tenha alguma duvida consulte os arquivos de configuração da porta serial no seu Linux.

#ifndef SERIAL_C
#define SERIAL_C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <shadow.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include "declaracoes.h"
#include "base64.h"

int abrir_porta()
   {
        int fd;
        fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
        if (fd == -1)
        {
            printf("\nErro em /dev/ttyS0\n");
            return -1;
        }
        else
           fcntl(fd, F_SETFL, 0);
        return (fd);
  }

void configure_port(int fd)
{
    struct termios arq_novo;

    tcgetattr(fd, &arq_novo);
    cfsetispeed(&arq_novo, B19200); // seta taxa de transmição
    cfsetospeed(&arq_novo, B19200); // seta de recepção
    arq_novo.c_cflag |= (CLOCAL | CREAD);
    arq_novo.c_cflag &= ~PARENB; // sem paridade
    arq_novo.c_cflag &= ~CSTOPB;
    arq_novo.c_cflag &= ~CSIZE;
    arq_novo.c_cflag |= CS8;
    arq_novo.c_cflag &= ~CRTSCTS;// desabilita controle de fluxo
    arq_novo.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);// raw input, sem mexer nos chars
    arq_novo.c_oflag &= ~OPOST;// raw output
    arq_novo.c_iflag &= ~IXOFF;
    arq_novo.c_iflag &= ~IXON;
    arq_novo.c_cc[VMIN]  = TAM_DADOS+(sizeof(int)*3);
    arq_novo.c_cc[VTIME] = 0;

    tcsetattr(fd, TCSANOW, &arq_novo);
}

int escrever_porta(char *buffer)
    {
	int fd;
        fd=abrir_porta();
        configure_port(fd);
	tcflush(fd, TCOFLUSH);
	ssize_t saida;
        saida = write(fd,buffer,TAM_DADOS+(sizeof(int)*3));
       return saida;
    }

int ler_porta(char *buffer)
{
        int fd;
        fd=abrir_porta();
        configure_port(fd);
	ssize_t entrada;
	fcntl(fd, F_SETFL, FNDELAY);
	entrada = read(fd,buffer,TAM_DADOS+(sizeof(int)*3));
	tcflush(fd, TCIFLUSH);
	return entrada;
 }

void fechar_porta()
{
        int fd;
   	close(fd);
}
#endif

Arquivo serial.h

Arquivo fonte que contem os cabeçalhos e os protótipos das funções usadas no serial.c

#ifndef SERIAL_H
#define SERIAL_H
#include <stdio.h>

extern
int abrir_porta();

extern
void configure_port(int);

extern
int escrever_porta(char*);

extern
int ler_porta(char *);

extern
void fechar_porta();

#endif

Arquivo definicoes.c

Arquivo fonte responsável pela montagem dos pacotes de dados, manipulação do arquivo,  calculo do CRC e ACK.

#ifndef DEFINICOES_C
#define DEFINICOES_C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "declaracoes.h"
#include "serial.h"
#include "base64.h"

char *abre_arq(char *nome_arq)
{
    unsigned char caracter;
    FILE *entrada;
    FILE *saida;
    int aux;

    entrada = fopen (nome_arq, "r+b");
     	if(!entrada)
          printf("Não foi possível abrir arquivo:%s ",nome_arq);

    saida = fopen("base64.64","w+b");
     	if(!saida)
          printf("Não foi possível criar dependências");

    encode(entrada,saida);

    fclose(saida);
    fclose(entrada);

    saida = fopen ("base64.64", "r+b");
     	if(!saida)
            printf("Não foi possível criar dependências");

   	int posicao_inicial = ftell(saida);
        fseek(saida, 0, SEEK_END);
    	int tamanho = ftell(saida);
        fseek(saida, posicao_inicial, SEEK_SET);

      char *ptr = aloca_mem(tamanho);

    if (saida == NULL)
    	printf("Não foi possível criar dependências");
    else
        {
            int aux=0;
             while(!feof(saida))
                {
                    caracter = getc(saida);
                        if(!feof(saida))
                            {
		 		ptr[aux] = caracter;
			        aux++;
                            }
               }
        }

       fclose(saida);
       remove("base64.64");

       return ptr;
}

pacote* monta_pacote(int indice,char *ptr_arq,int tipo,char *nome_arq)
{
    int aux=0;
    pacote *p = (pacote*) malloc (sizeof(pacote));

    if (tipo == 1)
    {
		p->tipo = 1;
		p->tamanho = strlen(ptr_arq);
		strcpy(p->dados,nome_arq);
          	return p;

     }
    else if (tipo == 2)
    {
		p->tipo=2;
                for(aux=0;aux < TAM_DADOS ;aux++,indice++)
                {
                    p->dados[aux]=ptr_arq[indice];

                }
                p->tamanho = sizeof(p->dados);;
   		return p;
    }
    else if (tipo == 3)
    {
        p->tipo=3;
        p->dados[0] = 'a';
        p->tamanho = sizeof(p->dados);
        return p;
    }
}

char *aloca_mem(int tamanho)
{
    char * ptr = (char*)malloc (tamanho * sizeof(char));
    if(!ptr)
    	printf("Não foi possível alocar memória");

    else return ptr;
}

short int CRC( char vetor[TAM_DADOS+(sizeof(int)*2)], int tam)
{

    short int crc = 0xffff;// 65535
    int j, i;
    for (j = 0; j < tam; j++)
    {

        crc = crc ^ vetor[j];
        for (i = 0; i < 8; i++)
        {
           crc = crc >> 1;
           if ((crc & 1) == 1)
             crc = crc ^ 0xA001;//40961
        }
    }
    return crc;
}
void pacote_to_string(pacote *p,char *buffer)
{
     int crc=0;

     memcpy(buffer, &p->tipo, sizeof(int));
     buffer += sizeof(int);
     memcpy(buffer, &p->tamanho, sizeof(int));
     buffer += sizeof(int);
     memcpy(buffer, &p->dados, TAM_DADOS);
     buffer += TAM_DADOS;
     p->crc = CRC(buffer, 108);
     memcpy(buffer, &p->crc, sizeof(short int));

        printf("\nmandou");
	printf("\ntipo: %d",p->tipo);
}

pacote *  string_to_pacote(char *buffer)
{

     pacote *p = (pacote*) malloc (sizeof(pacote));

     memcpy(&p->tipo, buffer, sizeof(int));
     buffer += sizeof(int);
     memcpy(&p->tamanho, buffer, sizeof(int));
     buffer += sizeof(int);
     memcpy(&p->dados, buffer, TAM_DADOS);
     buffer += TAM_DADOS;
     memcpy(&p->crc, buffer, sizeof(short int));

	printf("\n leu");
	printf("\ntipo: %d",p->tipo);
	return p;
 }

int testa_crc(pacote *p,char *buffer)
{
	short int crc=0;
	buffer += 108;
	memcpy(&crc, buffer, sizeof(short int));
	if(crc == p->crc)
		return 0;
	else
		return -1;
}

int teste_ACK(pacote *p)
{
	if (p->tipo == 3 && p->dados[0] == 'a')
		return 0;
	else
	        return -1;
}

int escreve_arquivo(char *ptr_arq,char *nome_arq,int tamanho)
{
    FILE *saida;
    FILE * arquivo;
    int aux;
        saida = fopen("base64.64","w+b");
            if(!saida)
            {
                printf("Não foi possível criar arquivo");
                return -1;
            }

            fprintf(saida,"%s",ptr_arq);
	fclose(saida);

        saida = fopen("base64.64","r+b");
            if(!saida)
            {
                printf("Não foi possível criar arquivo");
                return -1;
            }

	arquivo = fopen(nome_arq,"w+b");
            if(!saida)
            {
                printf("Não foi possível criar arquivo");
                return -1;
            }

	decode(saida,arquivo);

fclose(saida);
fclose(arquivo);
remove("base64.64");
return 0;
}

#endif

Arquivo declaracoes.h

Arquivo fonte que contem os cabeçalhos e protótipos das funções usadas no definicoes.c

#ifndef DECLARCOES_H
#define DECLARACOES_H
#define bzero(b,len) (memset((b), '\0', (len)), (void) 0)
#define TAM_DADOS 100
#include <stdio.h>

typedef struct pacote
{
	unsigned int tipo;
	unsigned int tamanho;
	short int crc;
	char dados[TAM_DADOS];
}pacote;

extern
short int calcula_CRC( char*, int);

extern
char* aloca_mem(int);

extern
char *abre_arq(char*);

extern
int escreve_arquivo(char *,char *,int);

extern
void pacote_to_string(pacote*,char*);

extern
pacote * string_to_pacote(char*);

extern
pacote * monta_pacote(int,char *,int,char*);

extern
int testa_crc(pacote*,char*);

extern
int teste_ACK(pacote *);
#endif

Arquivo base64.c

Arquivo fonte responsável pela codificação e descodificação na base 64, base padrão para codificação de caracteres para envio pela porta serial. Essa base também é utilizada pelos emails.

#ifndef BASE64_C
#define BASE64_C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "base64.h"
#define B64_DEF_LINE_SIZE   72
#define B64_MIN_LINE_SIZE    4
#define THIS_OPT(ac, av) (ac > 1 ? av[1][0] == '-' ? av[1][1] : 0 : 0)

static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";

void encodeblock( unsigned char in[3], unsigned char out[4], int len )
{
    out[0] = cb64[ in[0] >> 2 ];
    out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ];
    out[2] = (unsigned char) (len > 1 ? cb64[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '=');
    out[3] = (unsigned char) (len > 2 ? cb64[ in[2] & 0x3f ] : '=');
}

void encode( FILE *infile, FILE *outfile )
{
    unsigned char in[3], out[4];
    int i, len, blocksout = 0;

    while( !feof( infile ) ) {
        len = 0;
        for( i = 0; i < 3; i++ ) {
            in[i] = (unsigned char) getc( infile );
            if( !feof( infile ) ) {
                len++;
            }
            else {
                in[i] = 0;
            }
        }
        if( len ) {
            encodeblock( in, out, len );
            for( i = 0; i < 4; i++ ) {
                putc( out[i], outfile );
            }
            blocksout++;
        }
        if( blocksout >= (100/4) || feof( infile ) ) {
            if( blocksout ) {
                fprintf( outfile, "\r\n" );
            }
            blocksout = 0;
        }
    }
}

void decodeblock( unsigned char in[4], unsigned char out[3] )
{
    out[ 0 ] = (unsigned char ) (in[0] << 2 | in[1] >> 4);
    out[ 1 ] = (unsigned char ) (in[1] << 4 | in[2] >> 2);
    out[ 2 ] = (unsigned char ) (((in[2] << 6) & 0xc0) | in[3]);
}

void decode( FILE *infile, FILE *outfile )
{
    unsigned char in[4], out[3], v;
    int i, len;

    while( !feof( infile ) ) {
        for( len = 0, i = 0; i < 4 && !feof( infile ); i++ ) {
            v = 0;
            while( !feof( infile ) && v == 0 ) {
                v = (unsigned char) getc( infile );
                v = (unsigned char) ((v < 43 || v > 122) ? 0 : cd64[ v - 43 ]);
                if( v ) {
                    v = (unsigned char) ((v == '$') ? 0 : v - 61);
                }
            }
            if( !feof( infile ) ) {
                len++;
                if( v ) {
                    in[ i ] = (unsigned char) (v - 1);
                }
            }
            else {
                in[i] = 0;
            }
        }
        if( len ) {
            decodeblock( in, out );
            for( i = 0; i < len - 1; i++ ) {
                putc( out[i], outfile );
            }
        }
    }
}

int b64( int opt, char *infilename, char *outfilename )
{
    FILE *infile;
    int retcode = 0;

    if( !infilename )
        infile = stdin;
    else
        infile = fopen( infilename, "rb" );

        FILE *outfile;
        if( !outfilename )
            outfile = stdout;
        else
            outfile = fopen( outfilename, "wb" );

            if( opt == 'e' ) {
                encode( infile, outfile);
            }
            else {
                decode( infile, outfile );
            }

            if( outfile != stdout ) {
                if( fclose( outfile ) != 0 ) {
                    retcode = 1;
                }
            }

        if( infile != stdin ) {
            fclose( infile );
        }
    return( retcode );
}
#endif

Arquivo base64.h

Arquivo fonte que contem os cabeçalhos e os prototipos das funções usadas no arquivo base64.c

#ifndef BASE64_H
#define BASE64_H
#include <stdio.h>

extern void encode( FILE *, FILE *);
extern void decode( FILE *, FILE *);

#endif

Caso tenha alguma dúvida a respeito do código fonte ou do seu funcionamento entre em contato. Para fazer o download do código fonte CLICK AQUI. Para fazer download do documento de Requisitos desse protocolo CLICK AQUI, esse documento foi feito como pré requisito para a aprovação da Disciplina de Engenharia de Software I na qual eu reaproveitei o software da disciplina de Redes de Computadores I.

por ferpinheiro Postado em Redes

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alteração )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alteração )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alteração )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alteração )

Conectando a %s