🐧 Check Wiki For Lab Guides 🐧
Este repositório contém:
- Projetos - made with Filipe
- Documentação - https://ca-moes.github.io/RCOM/html/
- Slides
Média TPC | Lab1 | Lab2 | Frequência | Exame | Final |
---|---|---|---|---|---|
16.0 | 19.0 | 18.0 | 18.0 | ? | 15 |
Redes de Computadores
🐧 Check Wiki For Lab Guides 🐧
Este repositório contém:
Média TPC | Lab1 | Lab2 | Frequência | Exame | Final |
---|---|---|---|---|---|
16.0 | 19.0 | 18.0 | 18.0 | ? | 15 |
perror("read failed")
antes de switch quando res==-1 só em reading e writing cycleProblema: Quando BCC2 era 0x7e o Emissor não recebia uma trama de confirmação, mas a seguir reenviava e recebia bem, fechando bem o programa e enviando bem o ficheiro
Solução:
unsigned char finalBuffer[currentLenght +6]; /trama I completa/
passou para
unsigned char finalBuffer[currentLenght + 4 + buf2Size]; /trama I completa/
Caso apenas o read esteja aberto, podemos instalar um handler e um alarm no inicio para que caso não se estabeleça nenhuma ligação em X segundos, llopen retorna -1.
Criar 2 programas reader.c
writer.c
que no futuro serão apenas um chamado app.c
e que receberá um argumento na execução.
Estes programas têm de usar a função llopen()
e conseguir estabelecer uma ligação, ainda sem enviar dados.
Não esquecer de usar as funções de log para mostrar na consola os passos do programa.
Estamos a passar um buffer com MAX_SIZE ao llwrite mas esse buffer pode precisar de stuffing e não estamos a ter isso em conta, assim vai ultrapassar o MAX_SIZE e dar merda no frame buffer da linkLayer (é este o problema do frame e não o padding).
Temos de arranjar maneira de gerir o tamanho depois de o frame ter levado com byte stuffing.
Aqui o lenght é realmente igual a MAX_SIZE:
int llwrite(int fd, char *buffer, int lenght){
int currentLenght = lenght;
unsigned char buf1[4] = {FLAG, A_ER, C_I(linkLayer.sequenceNumber), BCC(A_ER, C_I(linkLayer.sequenceNumber))};
unsigned char *dataBuffer = (unsigned char *)malloc(lenght);
if (lenght > MAX_SIZE){
log_error("llwrite() - Message size greater than MAX_SIZE");
return -1;
}
faz-se byte stuffing
e aqui o finalBuffer já pode ter um tamanho bem maior dependendo dos stuffings q levou:
unsigned char finalBuffer[currentLenght + 6]; /*trama I completa*/
fillFinalBuffer(finalBuffer, buf1, buf2, dataBuffer, currentLenght);
stateMachineSetUp(C_RR(linkLayer.sequenceNumber^0x01), A_ER, Start, Write);
writeCycle(writeR, fd, finalBuffer, sizeof(finalBuffer));
Por isso é que dá merda no pinguim.gif e nos ficheiros de texto não, porque precisa de muitos mais stuffings que o ficheiro de texto e origina mais leaks de memória.
A minha sugestão seria ter um MAX_SIZE -> que seria o tamanho máximo sem stuffing.
e depois ter outro MAX_SIZE_AFTER_STUFFING -> que seria o tamanho máximo possível do frame após o stuffing ou seja MAX_SIZE * 2.
Não sei se é a melhor solução mas penso que resolve
A fazer na sala:
ToDo:
I : [F, A, C, BCC1, D1, D2, ..., Dn, BCC2, F]
SET = [FLAG,A,C,BCC,FLAG]
UA = [FLAG,A,C,BCC,FLAG]
Na application layer quando se recebe um numero de sequência que não é suposto, deve fazer-se lseek() para ir à posição certa do ficheiro escrever esse conjunto de dados.
De acordo com #17 e #18 estes ciclos são muito usados ao longo do pla e podem ser extraidos Para deixar o código mais limpo.
Também estive a pensar em usar algo como o que fizemos para LPOO relativo aos estados do PLA:
llopen()
llwrite()
llread()
llclose()
) -> Que é como temos agora, já que estamos a usar o mesmo enum para ambas as máquina de estados.Objetivo : Ter só uma máquina de estados personalizável que processe todo o tipo de tramas
Para ficar bem organizado seria criado um ficheiro só para conteúdo relativo á maquina de estados : state_machine.c
No inicio de cada função da pla dar setup á máquina de estados modificando a struct.
Ter uma struct global do estado da máquina de estados com
No inicio de cada função do pla ficaria algo do gênero:
state_machine.mode = Supervision;
state_machine.control = C_UA;
state_machine.adress = A_ER;
e chama-se dentro dos ciclos de leitura a máquina com:
stateMachine(byte, null, null); // para outros casos
// OU
stateMachine(byte, &buf, &size); // para o caso de Read
Na função de state machine teremos as variaveis estáticas (que poderão ser globais) e um switch:
stateMachine(unsigned char byte, unsigned char **buf, int* bufsize){
static unsigned char checkBuffer[2];
static int frameIndex, wrongC;
switch(state){ // Start, FLAG_RCV ...
case Start:
ProcessStart(Byte);
break;
}
Cada Case apontará para um função que terá dentro um switch
ou if..else
para cada state_machine.mode
:
ProcessStart(unsigned char byte){
switch(state_machine.mode)
case supervision:
break;
case read:
break;
}
Possível que dê merdinha ao passar o pointer pointer da buffer da máquina de estados do read. Mas deve se arranjar solução
Objetivo : Ter duas funções, cada uma representa um dos issues (#17 e #18) para serem usadas nas funções do pla.
Daqui é possível criar duas funções, uma para cada issue e passar todos os argumentos necessários, se possivel dentro de uma struct se os argumentos forem comuns para simplificar.
Pôr o makefile a funcionar. É com isto que se compila de acordo com o relatório
Com tamanhos menores fica mais rápido em alguns trabalhos o.O
MAX_SIZE pelo menos 5
Na leitura, caso haja um erro nos bytes de cabeçalho é cancelada a leitura e mandado um REJ ou RR (caso sequence number esteja trocado).
Não tenha a certeza ao que acontece ao resto dos bytes que estão na trama. Se eles ficarem no buffer para ler então será preciso que a read leia tudo e no fim é que mande o RR ou REJ para limpar o buffer de leitura
Nós fazemos o BCC2 e depois damos stuffing, já que o BCC é sem dar stuff, secalhar conseguimos juntar estes 2 loops para fazer o bcc2 e o stuffing ao mesmo tempo.
/*building trama I*/
unsigned char BCC2 = buffer[0];
for (int i = 1; i<lenght; i++){
BCC2 = BCC2 ^ buffer[i];
}
// Byte Stuffing (data buffer)
for (int i = 0, k=0; i<lenght; i++, k++){
if (buffer[i] == 0x7E || buffer[i] == 0x7D){
currentLenght++;
dataBuffer = (unsigned char *) realloc(dataBuffer, currentLenght);
dataBuffer[k+1] = buffer[i] ^ 0x20;
dataBuffer[k] = 0x7D;
k++;
}
else{
dataBuffer[k] = buffer[i];
}
}
O programa terá 2 passos:
Para criar a Trama I (passo 1) terá de recolher os seguintes valores:
Após ter estes valores guardados podemos começar o stuffing :
For byte in buffer_dados:
if byte == 0x7E
byte = 0x7D 0x5E
if byte == 0x7D
byte = 0x7D 0x5D
criando assim o array de Dados, espeta-se isso no meio da trama e tá pronto para mandar
unsigned int timeout; /Alarm Timeout: x s/
unsigned int numTransmissions; /Number of tries in case of failure/
strcpy(linkLayer.port,port);
linkLayer.baudRate = BAUDRATE;
linkLayer.sequenceNumber = 0x00;
linkLayer.timeout = 3;
linkLayer.numTransmissions = 3;
O transmitter manda um frame (113) ao qual o o receiver responde enviando um RR, esse RR não chega ao Transmitter apesar de ser enviado pelo Receiver (bytes sent: 5).
O que faz com que o Transmitter reenvie o mesmo frame (que agora é o 114) mas acontece que o programa verifica que o Control Byte não é igual ao C_I(sequenceNumber_expected) sendo um frame duplicado, envia um RR e passa ao próximo.
A variável que contem o enum state é global mas ao passar para a máquina de estados está a ser passada por referência &state
.
Ou pomos um enum em cada função e passamos por ref ou pomos o enum global e não se passa sequer como argumento da máquina de estados, já que esta tem acesso global.
Ao enviar tramas I aconteceu que mandava 2 vezes a flag de final.
Problema: BCC2 estava a ficar com o valor 7e e o BCC2 não estava a levar stuffing
Solução: Dar stuffing ao BCC2
--
Poderá o mesmo acontecer a BCC1?
#define A_ER 0b00000011 ///< (0x03) Campo de Endereço (A) de commandos do Emissor, resposta do Receptor
#define A_RE 0b00000001 ///< (0x01) Campo de Endereço (A) de commandos do Receptor, resposta do Emissor
#define C_SET 0b00000011 ///< (0x03) Campo de Controlo - SET (set up)
#define C_DISC 0b00001011 ///< (0x0B) Campo de Controlo - DISC (disconnect)
#define C_UA 0b00000111 ///< (0x07) Campo de Controlo - UA (Unnumbered Acknowledgement)
#define C_RR(r) ((0b00000101) ^ (r) << (7)) ///< (0x05 OU 0x85) Campo de Controlo - RR (receiver ready / positive ACK))
#define C_REJ(r) ((0b00000001) ^ (r) << (7)) ///< (0x01 OU 0x81) Campo de Controlo - REJ (reject / negative ACK))
#define C_I(r) ((0b01000000) & (r) << (6)) ///< (0x00 0x40) Campo de Controlo - Tramas I
A_ER ^ C_SET = 0x00
A_ER ^ C_DISC = 0x08
A_ER ^ C_UA = 0x04
A_ER ^ C_RR 1 = 0x86
A_ER ^ C_RR 0 = 0x06
A_ER ^ C_REJ 1 = 0x82
A_ER ^ C_REJ 0 = 0x02
A_ER ^ C_I 1 = 0x43
A_ER ^ C_I 0 = 0x03
A_RE ^ C_SET = 0x02
A_RE ^ C_DISC = 0x0a
A_RE ^ C_UA = 0x06
A_RE ^ C_RR 1 = 0x84
A_RE ^ C_RR 0 = 0x04
A_RE ^ C_REJ 1 = 0x80
A_RE ^ C_REJ 0 = 0x00
A_RE ^ C_I 1 = 0x41
A_RE ^ C_I 0 = 0x01
Resposta: não
Connection established.
Content: 0x7e
Content: 0x03
Content: 0x40
Content: 0x43
cut
Content: 0x7e
Content: 0x03
Content: 0x40
Content: 0x43
Content: 0x04
Content: 0x68
Content: 0x65
Content: 0x6c
Content: 0x7d
Content: 0x5e
Content: 0x6f
Content: 0x70
Byte 0x04 está a ser inserido entre BCC1 e Dados
Em llwrite() caso a state machine retorne -1 tem de enviar uma nova trama
Parece que após sair do signal handler o read continua blockeado
Se/quando corrigirmos isto podemos deixar estar a pasta Aula 2 como está e criar uma nova pasta para fazer o resto da primeira prática laboratorial
Algumas mensagens de erro estão erradas (nome da função de onde são chamadas errado).
Temos de verificar 1 a 1 os returns -1 a ver se têm um log_error direito
Basicamente tentei enviar o pinguim.gif, e ele não consegue fechar tudo direito.
Fiz debug e o que está a acontecer é:
Por alguma razão a variavel frame unsigned char frame[MAX_SIZE+6]; /*Trama*/
na struct linkLayer está a ficar sem espaço e está a afetar a variável unsigned int status; /*TRANSMITTER | RECEIVER*/
fazendo com que ela fique com lixo.
variavel status da struct linklayer:
"STATUSS" antes de correr a máquina de estados e "status2" depois de correr a maquina de estados:
o que faz com que o receiver chegue ao fim e não corra o llclose :-)
Isto acontece ao preencher o frame na maquina de estados do llread:
if (state_machine.type == Read) linkLayer.frame[frameIndex] = byte;
E acontece logo com o primeiro frame.
a 7 bytes do fim da trama I, ou seja se adicionarmos 7 ao tamanho do frame buffer corre tudo muito bem - temos de descobrir pq é q ele está a usar mais 7bytes do que é suposto
O pior é que só acontece com este ficheiro, experimentei com um ficheiro de texto com bué caracteres e funcionou bem. lets see
Situações em que o programa fica bloqueado em vez de terminar:
gethostbyname
, já que o DNS não tem o endereço IP do que se quer aceder. Não há uma forma fácil de tratar disto sem misturar forks e processos filhos, por isso ficou um print por cima da chamada gethostbyname
e por baixo para saber se está bloqueado nessa secção.stateMachineSetUp(C_UA, A_RE, Start, Supervision);
alarm(5); /* waits a limited time for UA response from Transmitter */
/* parse UA*/
if (readingCycle(closeDISC, fd, NULL, NULL, NULL) < 0)
return -1;
alarm(0);
return fd;
Receiver_DISC_UA na receção do UA
Aproveitar de LAIG e SOPE. Podemos acrescentar umas funções que mandem diferentes mensagens:
pld.c e pld.h terão as funções llopen() llwrite() llread() llclose()
pld_spec.c e pld_spec.h (pld specification) terão o resto das funções
Ao ler nos pc's em RCOM a leitura pela parte do emissor está mal. O Emissor lê lixo enquanto que no socat funcionar bem
Solução : Trocar os buffers de char[]
para unsigned char[]
o writenoncanonical.c
e noncanonical.c
passarão a ser a função llopen(int porta, flag TRANSMITTER | RECEIVER)
num novo ficheiro pld.c
Protocolo de Ligação de Dados.
Ao dar sucesso vai devolver o fd
que arranja no inicio fd = open(argv[1], O_RDWR | O_NOCTTY );
Not sure se o que manda como primeiro argumento - int porta
é suposto ser o /dev/ttySx
DISC (disconnect) 00001011 - 0x08
UA (unnumbered acknowledgment) - 00000111 - 0x07
Modificar stateMachine_SET_UA
para receber um enum {SET, UA, DISC}
em vez de int type
e com isso verifica dentro da state Machine qual é o C que é suposto receber.
A struct que está nos slides:
struct applicationLayer {
int filedescriptor; /*Descritor correspondente à porta série*/
int status /*TRANSMITTER | RECEIVER*/
}
Daria jeito aqui porque o llclose()
é diferente para o receiver e transmitter e pelos slides int llclose(int fd)
não recebe como argumento o type
como o int llopen(int porta, TRANSMITTER | RECEIVER)
Tendo a struct como global esta poderia ser acedida pelo llclose()
para saber como fechar.
Com isto llclose fica assim:
do...while
com while
dentro para mandar DISC e esperar DISC. Ter alarm para caso receba mal ou não receba mande outra vezllopen()
manda UA e retorna)unsigned char replyBuf[UA_SIZE] = {FLAG, A_ER, C_UA, BCC(A_ER, C_UA), FLAG};
res = write(fd,replyBuf,UA_SIZE); //+1 para enviar o \0
if (res == -1) {
log_error("receiver_UA() - Failed writing UA to buffer.");
return -1;
}
return 0;
Done
do...while
com while
dentro para mandar DISC e esperar UA. Ter alarm para caso receba mal ou não receba mande outra vezhttps://www.valgrind.org/docs/manual/quick-start.html
valgrind --version
sudo apt get valgrind
Compile your program with -g
valgrind --leak-check=yes myprog arg1 arg2
Do lado do Transmissor terá de ser possível passar como argumento o nome do ficheiro a transferir, numa primeira fase o ficheiro estará no diretório do código source, mais tarde podemos aceitar paths absolutos.
Do lado do Recetor podemos também numa fase avançada passar por argumento o caminho onde ficará o ficheiro.
Após a leitura dos argumentos este será o flow do programa:
Ambas as partes executarão llopen()
para estabelecer a ligação. Caso o Emissor não consiga fazer llopen()
a função retorna -1 e o programa pode fechar. Do lado do Recetor fica aberto ou fecha após x segundos [refer to #19].
Assim que se estabelecer uma ligação o Emissor cria o Pacote de Controlo para dar informação sobre o inicio da transferência de dados:
e pode dar um llwrite(Cp);
(Cp = Control Packet).
Do lado do Recetor teremos um llread(Cp)
que assim que leia com sucesso dará parse de toda a informação.
IMPORTANTE dar parse da informação relativa ao tamanho em bytes do ficheiro para conseguir controlar a leitura.
Aqui o Emissor pode começar a mandar Pacotes de Dados:
Constrói o pacote, mandando para llwrite()
o buffer e o tamanho em bytes. Como llwrite()
pode dar erro e devolver -1 este terá de estar envolvido num do...while
para fazer várias tentativas de envio. Caso não consiga enviar um pacote de dados ao fim de x tentativas algo aconteceu á ligação, com isto o programa apresenta uma mensagem de erro e dá return
.
Do lado do Recetor teremos um ciclo while(1)
que estará constantemente a ler e levará break
em certas situações. Este ciclo servirá para ler todos os pacotes até receber o último pacote: Um Pacote de Controlo para sinalizar o fim da transferência.
Dentro do ciclo teremos um llread(buffer)
para receber os Pacotes de Dados e uma função para dar parse da informação recebida. No meio da informação teremos o tamanho em bytes do campo de dados e podemos ir subtraindo este valor ao tamanho total do ficheiro para sabermos quando perto do fim da transferência estamos.
Após mandar todos os Pacotes de Dados, o Emissor cria o pacote de Controlo para finalizar a transmissão e manda via llwrite(Cp)
. Após isto pode iniciar a sequência de fecho do pla com llclose()
.
Do lado do Recetor vamos receber no ciclo de leitura dos Pacotes de Dados o Pacote de Controlo de fecho, saindo assim do ciclo e iniciando também a sequência de fecho do pla com llclose()
.
Da forma como a Link Layer está feita não haverá erros na transmissão da informação dos dados já que o recetor verifica isso e caso haja erros (BCC errado) pede um reenvio. Mesmo assim é possivel que llwrite()
retorne -1, neste caso o Emissor terá de tentar mais vezes com llwrite()
e verificar se o problema persiste, em caso positivo fecha a aplicação com mensagem de erro.
Temos a possibilidade de trocar no programa writenoncanonical.c
o buffer de unsigned char
para uma struct
com os valores de cada Byte. Em termos de organização de dados seria preferivel mas surgem daqui umas questões a considerar, nomeadamente padding:
Se cada elemento da struct fosse um unsigned char
não haveria problemas. Não seria necessário padding e sizeof
seria n_elementos * 1 (tamanho em bytes)
Mas como a trama de Informação terá um campo de dados que será variável terá de ser um unsigned char[]
dentro da struct, que com o padding iria aumentar o tamanho da struct apresentado pelo sizeof
Isto não foi ainda testado. Pode ocorrer de que o array é divido também em camadas. Por enquanto não tenho a certeza
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.