Escola Superior de Tecnologia de Setúbal

Propaganda
Escola Superior de Tecnologia de Setúbal
Sistemas Embebidos
Trabalho de Laboratório nº2
1. Objectivos
Pretende-se trocar mensagens entre dois sistemas com SO Linux com o recurso a sockets.
2. Características
•
Os programas devem ser feitos em linguagem C/C++ e em ambiente Linux.
•
A comunicação entre os programas deve ser efectuada com o recurso a Sockets Berkley.
•
O programa deve suportar pelo menos quatro tipos de mensagens:
•
•
•
Echo Request e Echo Reply → Quando um programa recebe uma mensagem de Echo
Request responde com um echo reply.
•
Time Stamp Request e Time Stamp Reply → Permite saber a hora do outro sistema.
O programa envia um Time Stamp Request com a sua hora actual. Quando esta mensagem
é recebida, é enviado um Time Stamp Reply com a hora recebida no Time Stamp
Request e a hora actual do sistema.
As mensagens devem ser terminadas com um campo que contenha um CRC16 gerado por um
polinómio irredutível de ordem 16 (por exemplo, CRC-16-CCITT, CRC-16-IBM, CRC-16-T10-DIF, ...).
•
O campo CRC na emissão deve ser gerado a partir da mensagem a ser transmitida.
•
Na recepção, a integridade da mensagem deve ser verificada através do seu CRC.
O programa deve ser baseado no princípio cliente/servidor, isto é, um programa (o servidor) espera os
pedidos do outro programa (o cliente).
3. Cálculo de CRCs
Cálculo e verificação de um CRC a partir do polinómio x2 + 1.
Exemplo de um algoritmo em pseudo-código para o cálculo de um CRC16.
/*****************************************************************************
*
*
* Function: crc16
*
* Description: Calculate the 16 bit CRC generated by a 16 degree polinomyal *
*
*
* Parameters:
*
*
buf -> Message in which the CRC will be calculated.
*
*
Must be terminated with 2 zeros.
*
*
dim -> Number of message octets, including the 2 ending zeros.
*
1
*
polynomial -> The 16 bit degree polynomial
*
*
e.g., x^16 + x^12 + x^5 + 1
*
*
(1) 0001 0000 0010 0001 = 0x11021 or 0x01021
*
*
*
* Return:
*
* The function returns the 16 bit CRC.
*
* The message ending zeros are also substituted by the CRC value.
*
*
*
*****************************************************************************/
function crc16(byte array buf, int dim, int polynomial) {
reminder := 0;
for i from 0 to dim-1 {
aux = buf[i];
for j from 0 to 7 {
reminder = reminder leftshift 1;
if aux and 0x80
reminder = reminder or 0x01;
if reminder and 0x10000
reminder = reminder xor polynomial;
aux = aux leftshift 1;
}
}
reminder = reminder and 0xffff;
buf[dim - 2] = reminder rightshift 8;
buf[dim - 1] = reminder;
return reminder;
}
4. Sockets
A função socket() cria um novo socket. O socket criado é representado por um inteiro. A função
socket() retorna -1 em caso de erro.
int socket(int domain, int type, int protocol);
•
•
•
domain, especifica a família de protocolos do socket, por exemplo:
•
PF_INET para IPv4;
•
PF_INET6 para IPv6.
type, especifica o tipo de socket, por exemplo:
•
SOCK_STREAM, serviço fiável e orientado à ligação (stream socket);
•
SOCK_DGRAM, serviço não fiável (datagram sockets).
Protocol, especifica o protocolo de transporte.
•
Os mais comuns são: IPPROTO_TCP e IPPROTO_UDP.
Do lado do servidor é necessário ligar o socket a um endereço. A função bind() permite associar um
endereço a um socket.
int bind(int sockfd, struct sockaddr *socket_addr, socklen_t addrlen);
•
sockfd, descritor do socket ao qual se pretende atribuir o endereço.
•
socket_addr *, ponteiro para uma estrutura do tipo sockaddr onde é indicado o nº do
porto e o endereço do socket.
•
Dependendo do tipo de endereço, é utilizado um tipo de estrutura diferente, no final é
feito um cast para um ponteiro do tipo sockaddr.
// IPv4 AF_INET sockets:
struct sockaddr_in {
short
sin_family;
unsigned short
sin_port;
struct in_addr
sin_addr;
char
sin_zero[8];
};
2
//
//
//
//
e.g. AF_INET, AF_INET6
e.g. htons(3490)
see struct in_addr, below
not used, must be zero
struct in_addr {
unsigned long s_addr;
};
•
// load with inet_pton()
addrlen, especifica a dimensão da estrutura sockaddr.
A função recvfrom() permite a recepção de uma mensagem a partir de um socket. Se não existirem
mensagens no socket, o processo é bloqueado até que chegue uma mensagem, a menos que o socket seja não
bloqueante (flag = MSG_DONTWAIT).
ssize_t recvfrom( int sockfd,
void *buf,
size_t len,
int flags,
struct sockaddr *src_addr,
socklen_t *addrlen);
//
//
//
//
//
//
Socket descriptor
Buffer to place the message
Length of the buffer area
Or of several options
Address of the message sender
Length of the sender's address
4.1. Criação de um socket UDP do lado do servidor
•
O servidor recebe mensagens através de um socket que utiliza UDP como protocolo de suporte à
comunicação.
•
Se o sistema tiver vários endereços IP, o servidor aceita mensagens de qualquer um dos seus endereços
IP.
•
É utilizado o porto 7654.
•
Para compilar e executar o programa efectuar:
gcc server.c -o server
chmod u+x server
./server
#include
#include
#include
#include
#include
#include
#include
#include
<stdio.h>
<errno.h>
<string.h>
<sys/socket.h>
<sys/types.h>
<netinet/in.h>
<unistd.h>
<stdlib.h>
int main(void) {
struct sockaddr_in ip4addr;
char buffer[1024];
ssize_t recsize;
socklen_t fromlen;
int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
memset(&ip4addr, 0, sizeof ip4addr); //
ip4addr.sin_family = AF_INET;
//
ip4addr.sin_addr.s_addr = INADDR_ANY;//
//
ip4addr.sin_port = htons(7654);
//
Init struct with zero.
IPv4 Socket
If server has several IP addresses,
accept connects from any address.
Convert memory order into network order.
if ( bind(sock,(struct sockaddr *)&ip4addr, sizeof(ip4addr)) == -1 ) {
// ^--- cast IPv4 address pointer to sockaddr pointer type.
printf( "Error: bind failure...\n" );
close(sock);
exit(0);
}
while(1) {
recsize = recvfrom( sock,
(void *)buffer,
1024,
0,
(struct sockaddr *)&ip4addr,
&fromlen);
if ( recsize < 0 ) {
printf( "Error: recvfrom failure...\n" );
close(sock);
exit(0);
}
3
printf( "recsize: %d", (int)recsize );
sleep(1);
printf(", datagram: %.*s\n", (int)recsize, buffer);
} // while
} // main
4.2. Criação de um socket UDP do lado do cliente
•
O cliente envia mensagens através de um socket que utiliza UDP como protocolo de suporte à
comunicação.
•
O cliente envia a mensagem Hello world! para o endereço 10.41.0.3, porto 7654. É necessário
alterar o código e colocar o endereço da máquina onde irá ser executado o servidor.
•
Para compilar e executar o programa efectuar:
gcc client.c -o client
chmod u+x client
./client
#include
#include
#include
#include
#include
#include
#include
#include
#include
<stdlib.h>
<stdio.h>
<errno.h>
<string.h>
<sys/socket.h>
<sys/types.h>
<netinet/in.h>
<unistd.h>
<arpa/inet.h>
int main(int argc, char *argv[]) {
int sock;
struct sockaddr_in sa;
int bytes_sent;
char buffer[200];
strcpy( buffer, "Hello world!" );
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if ( sock == -1 ) { /* if socket failed to initialize, exit */
printf("Error Creating Socket");
exit(EXIT_FAILURE);
}
memset(&sa, 0, sizeof sa);
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr("10.41.0.3");
sa.sin_port = htons(7654);
bytes_sent = sendto( sock,
buffer,
strlen(buffer),
0,
(struct sockaddr*)&sa,
sizeof sa);
if (bytes_sent < 0) {
printf("Error sending packet: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
close(sock); /* close the socket */
return 0;
}
4.3. Teste
O programa pode ser testado executando:
•
O servidor e o cliente na mesma máquina.
•
O servidor numa máquina e o cliente numa outra.
•
O servidor num PC e o cliente na placa Olimex ou vice-versa.
4
5. Relatório
O relatório deve ser objectivo e centrar-se no conteúdo do trabalho e nos algoritmos utilizados.
Juntamente com o relatório, mas em formato electrónico, devem ser enviadas as listagens dos programas assim
como os executáveis.
Alguns recomendações sobre o estilo de programação:
•
O código deve ser facilmente perceptível, isto implica:
•
Quando se olha para o código e este tem um aspecto confuso, é porque o é. Deve-se neste caso,
alterar o código ou a listagem de modo a tornar tudo mais perceptível.
•
Uma boa indentação, ou seja, o código dos ciclos e das condições deve ser imediatamente
visível. Existem algumas recomendações para uma boa indentação, mas na prática é uma questão
de estilo pessoal.
•
Deve-se ter a noção de que o código é para ser visto por mais pessoas (neste caso os
professores).
•
Os comentários nunca são demais.
•
Os nomes das funções e das variáveis começam por letras minúsculas.
•
Os nomes das constantes são escritas totalmente em letras maiúsculas.
•
Os nomes das funções, variáveis e constantes devem ser claros e demonstrar claramente a sua função.
•
Na listagem as funções devem ter em comentário um cabeçalho que indique claramente a sua função, o
valor retornado e a lista dos parâmetros.
/*********************************************************************
* function: stringMaxIndex
*
*
*
* Description: Return the index of the max value of a string
*
*
*
* Prototype: unsigned int stringMaxIndex( char *string );
*
*
*
* Parameters: char *string -> Pointer to the string
*
* Return: unsigned int -> The index of the max value of the string *
*
pointed by string
*
*********************************************************************/
•
O programa indent permite formatar automaticamente um programa em C. Este programa pode ser
instalado através do gestor de aplicações do Ubuntu ou através da linha de comandos.
•
O comando sudo apt-get install indent, instala a aplicação indent.
•
O comando sudo apt-get install indent-doc, instala a documentação da aplicação
indent1.
•
•
Esta documentação pode ser consultada com um
ficheiro /usr/share/doc/indent-doc/indent.html.
browser
abrindo
Para se obter uma indentação normalmente utilizada em Linux, executar o seguinte comando:
indent -kr -i8 -ts8 -sob -l80 -ss -bs -psl file
1 A documentação das aplicações é instalada na directoria /usr/share/doc.
5
o
Download