Ferramentas de Utilizador

Ferramentas de Site


dev_geral:c:sockets_de_berkeley

Sockets de Berkeley

Neste artigo vamos fazer uma breve introdução aos Sockets de Berkeley em linguagem C para ambientes UNIX e Linux. Basicamente os Sockets de Berkeley são um API, isto é, um conjunto de bibliotecas de funções para a programação sobre protocolos de comunicação.

Modelo Cliente – Servidor

O modelo Cliente – Servidor é composto por um conjunto de dois programas em execução que comunicam entre si. O servidor está sempre à espera de pedidos efectuados pelos clientes mas desconhece a sua localização. O cliente tem de conhecer obrigatoriamente a localização do servidor (IP) para poder ligar-se e comunicar com ele. O código do cliente e do servidor não têm de ser necessariamente diferentes, facilmente se desenvolve uma aplicação que tanto pode funcionar como cliente como para servidor. A grande diferença está no seu comportamento, ou seja, o servidor inicia a execução e aguarda uma ligação, o cliente inicia a execução e a ligação ao servidor.

Para dar um exemplo prático de como funcionam os sockets, vamos fazer um simples par de aplicações em que o cliente envia uma palavra com letras minúsculas para o servidor. O servidor trata de converter as letras em maiúsculas devolvendo a palavra ao cliente. Vamos utilizar o modo TCP do protocolo TCP/IP e o conjunto de aplicações são fechadas quando o cliente digita a palavra “exit”.

Cliente

O cliente tem o seguinte código. De seguida vamos analisá-lo passo a passo para melhor compreensão do mesmo.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <unistd.h>
 
int main() {
 	struct sockaddr_in target;
	int sock = socket(AF_INET, SOCK_STREAM, 0);
	char palavra[50], palavra2[50];
	int ad1 = sizeof(target);
 
	bzero((char *)&target, ad1);
 
	target.sin_family = AF_INET;
	target.sin_addr.s_addr = inet_addr("127.0.0.1");
	target.sin_port = htons(8450);
 
	if(connect(sock, (struct sockaddr *)&target, ad1) == -1)
	{
		close(sock);
		puts("Conexao falhada!");
		exit(0);
	}
 
	do {
		scanf("%s", palavra);
		write(sock, palavra, 50);
		if(strcmp(palavra, "exit") != 0){
			read(sock, palavra2, 50);
			printf("%sn", palavra2);
		}
	}while(strcmp(palavra, "exit") != 0);
	close(sock);
	return 0;
}

Bibliotecas necessárias

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <unistd.h>

Definir e inicializar a estrutura de destino (servidor) e as variáveis

// definição das estruturas do cliente e do servidor
struct sockaddr_in me, from;
// criação dos sockets.o “sock” é declarado identicamente ao cliente
int newSock, sock = socket(AF_INET, SOCK_STREAM, 0);
// declaração das variáveis
int tam = 0, i = 0;
int ad1 = sizeof(me);
char palavra[50], palavra2[50];
 
// inicializa a estrutura do cliente
bzero((char *)&target, ad1);

Definir as propriedades do servidor ao qual nos vamos ligar

// indica a família do protocolo
target.sin_family = AF_INET;
// especifica o endereço (IP) do servidor
target.sin_addr.s_addr = inet_addr("127.0.0.1");
// porta que o programa vai usar
target.sin_port = htons(8450);

Estabelecer a ligação ao servidor

// efectua a ligação ao servidor. Se falhar (por exemplo o servidor estar em baixo) o programa termina
if(connect(sock, (struct sockaddr *)&target, ad1) == -1)
{
	close(sock);
	puts("Conexao falhou!");
	exit(0);
}

Ler dados do teclado

Vamos usar um método de leitura de daods muito simples. Lê uma palavra do teclado enquanto esta for diferente de “exit”. Em seguida envia-a para o servidor e recebe-a com letras maiúsculas

do {
	scanf("%s", palavra);
	// envia para o servidor os dados contidos na variável “palavra”
	write(sock, palavra, 50);
	if(strcmp(palavra, "exit") != 0){
		// recebe do servidor os dados e guarda-os na variável “palavra2”
		read(sock, palavra2, 50);
		printf("%sn", palavra2);
	}
}while(strcmp(palavra, "exit") != 0);

Fechar o socket

close(sock);

Servidor

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <ctype.h>
 
int main() {
  	struct sockaddr_in me, from;
  	int newSock, sock = socket(AF_INET, SOCK_STREAM, 0);
	int tam = 0, i = 0;
	int ad1 = sizeof(me);
  	char palavra[50], palavra2[50];
 
  	bzero((char *)&me, ad1);
 
  	me.sin_family = AF_INET;
  	me.sin_addr.s_addr = htonl(INADDR_ANY);
  	me.sin_port = htons(8450);
 
  	if(bind(sock, (struct sockaddr *)&me, ad1) == -1)
	{
		close(sock);
		puts("Porta Ocupada!"); 
		exit(0);
	}
 
	listen(sock, 5);
   	newSock = accept(sock, (struct sockaddr *)&from, (void *)&ad1);
	close(sock);
 
	for(;;){
		read(newSock, palavra, 50);
		if(strcmp(palavra, "exit") != 0){
			tam = strlen(palavra);
			for(i=0;i<tam;i++){
				if((palavra[i]>='a') && (palavra[i]<='z')){
					palavra2[i] = toupper(palavra[i]);
				}
			}
			palavra2[i] = '0';
			write(newSock, palavra2, 50);
		}
		else{
			close(newSock);
			exit(0);
		}
	}
	return 0;
}

Segue a análise do código passo a passo

Definir a estrutura do cliente, do servidor e as variáveis

// definição das estruturas do cliente e do servidor
struct sockaddr_in me, from;
// criação dos sockets.o “sock” é declarado identicamente ao cliente
int newSock, sock = socket(AF_INET, SOCK_STREAM, 0);
// declaração das variáveis
int tam = 0, i = 0;
int ad1 = sizeof(me);
char palavra[50], palavra2[50];
 
// inicializa a estrutura do servidor
bzero((char *)&me, ad1);

Definir as propriedades do servidor

// indica a família do protocolo
me.sin_family = AF_INET;
// fica associado a todos os endereços IP do host local
me.sin_addr.s_addr = htonl(INADDR_ANY);
// porta em que o servidor vai estar à escuta
me.sin_port = htons(8450);

Alocar a porta

Se estiver ocupada é fechado o servidor

// se a porta estiver ocupada o servidor não pode correr e é terminado
if(bind(sock, (struct sockaddr *)&me, ad1) == -1)
{
	close(sock);
	puts("Porta Ocupada!"); 
	exit(0);
}

Esperar por pedidos

É criado um novo socket para tratar apenas deste pedido enquanto que o outro socket continua à espera de pedidos:

GeSHi (c):
// coloca o socket à escuta. Podem ser mantidos em espera 5 pedidos de ligação
listen(sock, 5);
// gera um novo socket específico para essa ligação
newSock = accept(sock, (struct sockaddr *)&from, (void *)&ad1);
// fechar o socket antigo
close(sock);

Ciclo infinito que trata dos pedidos do cliente

for(;;){
	// recebe os dados do cliente e guarda-os na variável “palavra”
	read(newSock, palavra, 50);
	if(strcmp(palavra, "exit") != 0){
		tam = strlen(palavra);
		for(i=0;i<tam;i++){
			if((palavra[i]>='a') && (palavra[i]<='z')){
				palavra2[i] = toupper(palavra[i]);
			}
		}
		palavra2[i] = '0';
		// envia os dados que estão na variável “palavra2” para o cliente
		write(newSock, palavra2, 50);
	}
	else{
		close(newSock);
		exit(0);
	}
}
dev_geral/c/sockets_de_berkeley.txt · Esta página foi modificada pela última vez em: 2018/05/14 21:37 (Edição externa)