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.
Curtir isso:
Curtir Carregando...