Você está na página 1de 15

UFMG – Universidade Federal de Minas Gerais

REDES – Trabalho Prático 1 – MINI SERVIDOR

Aluno: Rafhael Batista Soares

Decisões de Implementação
Antes de mais nada, para podermos construir programas baseados em
sockets, devemos incluir todos os headers que contêm as declarações e
códigos relativos às funções que iremos usar. São eles:

sys/types.h -> definição de tipos de dados


sys/socket.h -> funções e constantes relativas a sockets
netinet/in.h -> para manipulação de estruturas, dados e endereços da famàlia
internet
arpa/inet.h -> definições para funções destinadas a operações com sockets

Iremos usar nossa primeira função: socket(). Esta função é responsável por
criar um socket e retornar uma identificação para ele, a qual denominamos
descritor de socket.

A declaração da variável "socket";


Um socket é identificado por um descritor de sockets retornado pela função
socket(). Este descritor, na verdade, é um número inteiro

socket = socket(AF_INET,SOCK_STREAM,0);

Esta linha tenta criar um socket voltado à familia internet e que utilizará o
protocolo TCP. Nem sempre um socket poderá ser criado com êxito, e o uso
de um socket inválido causaria vários erros em um programa. Por esta razão,
é fundamental que se verifique a validade de um socket recém-criado antes
de usá-lo. Para fazê-lo, podemos utilizar o valor de retorno da função
socket().

Após criarmos um socket, devemos definir o seu estilo de funcionamento:


modo servidor ou modo cliente.

No primeiro modo, fazemos com que o socket fique em modo de espera, isto
é, aguardando conexões. Para tanto, devemos especificar uma porta que
será utilizada e associá-la ao socket. Esta tarefa é realizada através de uma
estrutura denominada sockaddr_in e das funções bind() e listen().
A seguir preenchemos a estrutura sockaddr_in (addr) com os parâmetros
necessários para definir o socket como atuante no modo servidor:

A função bind() é responsível por associar um endereço e porta locais a um


socket.

Por fim, utilizando a função listen(), coloca-se, de fato, o socket em modo de


espera definindo um máximo de 1 conexão pendente que deve ser
aguardada.

Após configurado, podemos fazer com que o socket aceite conexões que
possam vir a surgir

Sempre quando terminamos de usar um socket, devemos fechá-lo:

close(socket);

Iremos construir um próprio programa cliente que se conecte ao nosso


programa servidor. Utilizamos a função connect() para fazer com que um
socket se conecte a um host remoto. Veja:

A função inet_addr() obtém um IP no formato string e o converte para o seu


respectivo valor numérico na forma de Network Byte Ordem - que é o tipo
requerido pela estrutura sockaddr. A função inet_addr() pode trabalhar
apenas sob endereços IP.

Maiores detalhes

int bind(int socket, const struct sockaddr * addr, socklen_t tamanho_addr);

socket:
descritor do socket;

addr:
ponteiro para uma estrutura sockaddr (ou sockaddr_in);

tamaho_addr:
tamanho, em bytes, da estrutura sockaddr.

Como vimos no código, a função bind() retornarí -1 se falhar. Se tiver êxito, o


valor retornado será ZERO.

int listen(int socket, int backlog);


socket:
descritor do socket;

backlog:
define o número míximo de conexões pendentes;

Assim como a função bind(), se a função listen() falhar, esta retorna -1 e


retorna zero, em caso de êxito.

int accept(int socket, struct sockaddr *addr_opcional, socklen_t *


tamanho_addr_opcional);

socket:
descritor do socket;

addr_opcional:
é um parâmetro opcional que pode ser passado como ZERO. É um ponteiro
para uma estrutura sockaddr que irí retornar informações (endereço e porta)
do computador remoto que requisitou a conexão.

tamanho_addr_opcional:
quando o parâmetro addr_opcional é passado como ZERO, este parâmetro
também se torna opcional. Do contrírio, é um ponteiro para uma variível do
tipo socklen_t (inteiro sem sinal - unsigned long) contendo o tamanho da
estrutura sockaddr.

Caso ocorra algum erro ao aceitar a conexão pendente, accept() retorna -1.
Do contrírio, retorna o descritor para o novo socket.

int send(int socket, const void * dados, int tamanho , int flags);

socket:
descritor do socket;

dados:
dados a serem enviados. Pode-se passar uma string ou variível como valor;

tamanho:
número de bytes a serem enviados;

flags:
valores opcionais normalmente passados como ZERO.

A função send(), como a maioria das funções relacionadas a sockets,


também retorna -1 se algum erro ocorrer ao tentar enviar os dados. Se a
função for bem sucedida, ela retornarí o número de bytes enviados.
int recv(int socket, void * buffer, int tamanho , int flags);

socket:
descritor do socket;

buffer:
buffer no qual serão armazenados os dados recebidos. Geralmente
passamos arrays de chars como parâmetros;

tamanho:
número míximo de bytes que podem ser recebidos;

flags:
valores opcionais normalmente passados como ZERO.

A função retorna o número de bytes recebidos em situação normal. Por outro


lado, caso ocorra algum erro, o valor retornado é -1.

int connect(int socket, const struct sockaddr * addr, socklen_t tamanho_addr);

socket:
descritor do socket;

addr:
ponteiro para uma estrutura sockaddr (ou sockaddr_in);

tamaho_addr:
tamanho, em bytes, da estrutura sockaddr.

Uma eventual falha na conexão farí com que a função connect() retorne o
valor -1. Caso a conexão seja feita com sucesso, ZERO é retornado.

Código fonte
Servidor

/*

UFMG - UNIVERSIDADE FEDERAL DE MINAS GERAIS


---------------------------------------------------------
| |
| REDES - TRABALHO PRATICO 1 - MINI SERVIDOR |
| RAFHAEL BATISTA SOARES |
| |
---------------------------------------------------------
|
|
| COMPILAR : $ gcc -o servidor redetp1_servidor.c |

|
|
---------------------------------------------------------
|
|
| EXECUTAR : $ ./servidor <numero_da_porta> |
|
|
---------------------------------------------------------

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//definição de tipos de dados


#include <sys/types.h>

//funções e constantes relativas a sockets


#include <sys/socket.h>

//definições para funções destinadas a operações com sockets


#include <arpa/inet.h>

//manipulação de estruturas, dados e endereços da famàlia


internet
#include <netinet/in.h>

#include <errno.h>
#include <unistd.h>

int main(int argc, char *argv[])


{
/* Declaracao da variavel utilizada como identificadora do
socket */
int sockfd, new_sockfd;

int bytes;
socklen_t length;//para accept() e bind()

char buffer[32], quit;

/* Declaracao de uma estrutura sockaddr_in responsavel por


fornecer ao socket as informações sobre a familia,
endereço e porta
que devem ser utilizados para a comunicacao */
struct sockaddr_in server;
struct sockaddr_in client;

sockfd = socket(AF_INET, SOCK_STREAM, 0);


if(sockfd < 0)
{
perror("SERVIDOR ENCERROU");
exit(1);
}

/* A seguir preenchemos a estrutura sockaddr_in (addr) com


os parâmetros
necessírios para definir o socket como atuante no modo
servidor: */
if(argc == 2)
{
server.sin_family = AF_INET;

/* Porta local na qual o socket ira aguardar conexões */


server.sin_port = htons(atoi(argv[1]));

/* Endereco ZERO -> utiliza endereço local padrao */


server.sin_addr.s_addr = INADDR_ANY;
memset(&(server.sin_zero), 0x00, 8);
}
else
{
printf("\n\t USANDO: %s <num_porta> \n\n", argv[0]);
close(sockfd);
exit(1);
}

/*
A função bind() e responsavel por associar
um endereço e porta locais a um socket.
*/
length = sizeof(struct sockaddr);
if(bind(sockfd, (struct sockaddr *)&server, length) < 0)
{
perror("SERVIDOR ENCERROU");
close(sockfd);
exit(1);
}

/*
A função listen(), coloca-se,
de fato, o socket em modo de espera definindo um míximo
de 1 conexão pendente que deve ser aguardada.

O SERVIDOR ACEITA APENAS 2 CONEXOES

*/
if(listen(sockfd, 2) < 0)
{
perror("SERVIDOR OCULPADO !!!! ");
close(sockfd);
exit(1);
}
printf("\nESPERANDO CONEXAO NA PORTA %s \n\n", argv[1]);

/*
Após configurado, podemos fazer com
que o socket aceite conexoss que possam vir a surgir
*/
new_sockfd = accept(sockfd, (struct sockaddr *)&client,
&length);
if(new_sockfd < 0)
{
perror("SERVIDOR ENCERROU");
close(sockfd);
exit(1);
}

printf("\n ESPERANDO CONEXAO DE %s\n\n",


inet_ntoa(client.sin_addr));

quit = 'N';
while(quit != 'S')
{
bytes = send(new_sockfd, "SERVIDOR FUNCIONANDO!!!", 12,
0);
//ocorreu um erro = -1
if(bytes < 0)
{
perror("SERVIDOR ENCERROU");
close(new_sockfd);
close(sockfd);
exit(1);
}

bytes = recv(new_sockfd, buffer, 32, 0);


//NOTE:ocorreu um erro = -1 ou cliente fechou conex„o = 0
if(bytes <= 0)
{
perror("SERVIDOR ENCERROU");
close(new_sockfd);
close(sockfd);
exit(1);
}

buffer[bytes] = 0x00;//0x00 o mesmo que '\0'


quit = buffer[0];
printf("\n RECEBIDO ..... %s\n\n", buffer);
}

/*
Sempre quando terminamos de usar um socket,
devemos fechí-lo:
*/
close(new_sockfd);
close(sockfd);
return 0;
}

Cliente

/*

UFMG - UNIVERSIDADE FEDERAL DE MINAS GERAIS


-------------------------------------------------------------
--
|
|
| REDES - TRABALHO PRATICO 1 - MINI SERVIDOR
|
| RAFHAEL BATISTA SOARES
|
|
|
-------------------------------------------------------------
-
|
|
| COMPILAR : $ gcc -o cliente redetp1_cliente.c
|
|
|
-------------------------------------------------------------
-
|
|
| EXECUTAR : $ ./cliente <ip_do_servidor> <num_da_porta>
|
|
|
-------------------------------------------------------------
-

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//definição de tipos de dados


#include <sys/types.h>

//funções e constantes relativas a sockets


#include <sys/socket.h>

//definições para funções destinadas a operações com sockets


#include <arpa/inet.h>
//manipulação de estruturas, dados e endereços da famàlia
internet
#include <netinet/in.h>

#include <errno.h> /*perror()*/


#include <unistd.h> /*close()*/

int main(int argc, char *argv[])


{

/* Declaracao da variavel utilizada


como identificadora do socket */
int sockfd;
int bytes;
socklen_t length;
char recv_buffer[32], send_buffer[32], quit;

/* Declaracao de uma estrutura sockaddr_in responsavel por


fornecer ao socket as informações sobre a familia,
endereço e porta
que devem ser utilizados para a comunicacao */
struct sockaddr_in server;

sockfd = socket(AF_INET, SOCK_STREAM, 0);


if(sockfd < 0)
{
perror("CLIENTE FALHOU");
exit(1);
}

/* A seguir preenchemos a estrutura sockaddr_in (addr) com


os parâmetros
necessírios para definir o socket como atuante no modo
cliente: */
if(argc == 3)
{
server.sin_family = AF_INET;
//NOTE:meio sem garantia, mas espero que voce nao erre ao
passar o numero da porta.
/* Porta local na qual o socket ira aguardar conexões */
server.sin_port = htons(atoi(argv[2]));
server.sin_addr.s_addr = inet_addr(argv[1]);
memset(&(server.sin_zero), 0x00, 8);
}
else
{
printf("\n\t FORMATO CORRETO: %s <ip_do_host> <num_porta>
\n\n", argv[0]);
close(sockfd);
exit(1);
}

//conectando-se ao servidor
length = sizeof(struct sockaddr);
if(connect(sockfd, (struct sockaddr *)&server, length) <
0)
{
perror("CLIENTE FALHOU");
close(sockfd);
exit(1);
}

quit = 'N';
while(quit != 'S')
{
bytes = recv(sockfd, recv_buffer, 32, 0);
/*supondo que nao ocorreu um erro acima
ponha o caractere '\0'
*/
recv_buffer[bytes] = 0x00;

/* servidor fechou a conexao ou ocorreu um erro


ocorreu um erro = -1, fechou conexao = 0
*/
if(bytes <= 0)
{
perror("CLIENTE FALHOU");
close(sockfd);
exit(1);
}

printf("\nENVIE UMA REQUISICAO PARA O SERVIDOR:\n");


/*
lendo da entrada padrao
*/
fgets(send_buffer, 32, stdin);
/*
se no inicio da string estiver o 'S'
entao esse é o ultimo loop
*/
quit = send_buffer[0];

/*
enviando string ao servidor.
strlen(send_buffer) pode ser menor que 32
*/
bytes = send(sockfd, send_buffer, strlen(send_buffer),
0);
if(bytes < 0)
{
perror("CLIENTE FALHOU");
close(sockfd);
exit(1);
}
/*
so agora podemos ver a mensagem recebida do servidor
*/
printf("\n RECEBEU : %s\n\n", recv_buffer);
}

/* Para encerrar a conexao */


close(sockfd);

return 0;
}

Copilando e Utilizando o Aplicativo


Copilar

executando servidor

executando cliente
servidor aceitou conexão

enviando requisições para servidor

cliente encerrando conexão


servidor encerrando conexão

servidor encerrando com falha


mais exemplo de conexão

mais um exemplo de envio de mensagem

Você também pode gostar