No artigo anterior, exploramos os file handles ee como eles são empregados na manipulação de
arquivos. Neste artigo, continuaremos a discussão sobre handles, focando agora nos socket handles. Contudo, o
conceito fundamental permanece o mesmo: um Socket Handle é um identificador utilizado pelo sistema operacional para
distinguir uma conexão específica. Um canal de comunicação gerenciado pelo sistema operacional é denominado Socket; já o
identificador que um processo emprega para referenciar esse canal é conhecido como socket handle. Usar um socket
handle é similar a usar um file handle. Podemos solicitar ao SO a abertura de um socket, e ele nos retornará um socket
handle que podemos usar para enviar e receber dados, e devemos fechá-lo uma vez que tenhamos terminado de usá-lo. Agora,
ao invés de interagir com um arquivo, estaremos interagindo com processos que podem estar localizados tanto localmente
quanto remotamente, de maneira transparente.
Criando Socket
O socket possui um communication domain que limita o tipo de comunicação que pode ser realizada e também determina o
formato do endereço do socket.
name
descrição
PF_UNIX
comunicação local (Unix)
PF_INET
comunicação via internet (IPv4)
PF_INET6
comunicação via internet (IPv6)
PF_UNIX utiliza como endereço o nome do file system da máquina, limitando a comunicação a processos executados na
mesma máquina. Por outro lado PF_INET e PF_INET6 são usados para comunicação via internet, onde o endereço é um
par de host e port. Isso possibilita a comunicação entre processos executados em quaisquer duas máquinas conectadas
à internet.
O Communication type do socket, por sua vez, indica se a comunicação é confiável (sem perda de pacotes ou duplicação
de dados) e também determina como os dados são enviados e recebidos (stream de bytes ou sequência de pacotes). O
Communication type restringe o protocolo utilizado para a transmissão de dados.
Type
Confiavel
Representacao dos dados
SOCK_STREAM
sim
Stream de bytes
SOCK_DGRAM
não
Pacotes
SOCK_RAW
não
Pacotes
SOCK_SEQPACKET
sim
Sequencia de pacotes
A função socket, localizada no módulo Unix, possui a seguinte assinatura:
Então, com base no que vimos até agora, precisamos apenas entender o terceiro parâmetro da função socket. Esse
parâmetro é o protocolo que será utilizado para a comunicação, representado por um inteiro. O valor 0 indica a seleção
do protocolo padrão para um dado communication domain e type (ex: UDP para SOCK_DGRAM). Os números desses protocolos
podem ser encontrados no arquivo /etc/protocols ou na tabela protocols do NIS (Network Information Service).
Com isso, podemos criar um socket para comunicação via internet (IPv4) utilizando o protocolo TCP da seguinte maneira:
O próximo passo é conectar o socket a um endereço. Para isso, utilizamos a função connect:
Conectando a um endereço
Agora, fomos apresentados a um novo tipo, sockaddr. Esse tipo é utilizado para representar um endereço de socket.
ADDR_UNIXf é um endereço de socket local, onde f é o nome do arquivo no sistema de arquivos. ADDR_INET(a,p) é um endereço de socket de internet, onde a é o endereço IP e p é a porta.
A função host realiza um DNS lookup no nome passado como parâmetro e retorna o endereço IP associado ao domínio.
Recebemos 1 endereço IP associado ao domínio ocaml.org. É sabido que por convenção a porta 80 é utilizada para o HTTP.
Uma forma equivalente de realizar a mesma ação em OCaml seria:
Logo, podemos criar nosso sockaddr da seguinte maneira:
Agora, usando tudo o que vimos e adicionando algumas funções novas, que vou explicar logo em seguida, temos:
A função print_server_response recebe um file_descriptor e imprime o conteúdo recebido do servidor. Para isso, ela
cria um buffer de 4096 bytes e lê o conteúdo do file_descriptor para o buffer, imprimindo o conteúdo do buffer até que
não haja mais nada para ler.
A função make_friend cria um socket e tenta conectar a um endereço passado como parâmetro. Caso a conexão seja
bem-sucedida, ela envia a mensagem "Olá Mundo! \n" para o servidor e imprime a resposta recebida. Por fim, ela fecha o
socket.
Gostaria de pontuar o uso da função read:
A função read lê até len bytes do file_descrfd e armazena no buffer buf a partir da posição pos. Ela retorna
o número de bytes lidos. Caso a função retorne 0, significa que não há mais dados para serem lidos. Essa é exatamente a
lógica que aplicamos na função print_server_response.
Para realizar uma chamada à função make_friend, basta passar o endereço do servidor como parâmetro:
Bem, ao menos fizemos a conexão com o servidor. Encontramos alguém lá fora e enviamos uma mensagem; eles nos
responderam. O que a resposta significa é que eles não entenderam nossa mensagem. A resposta começa com "HTTP/1.1", e
esse é o tópico do nosso próximo artigo.
Conclusão
Neste artigo, exploramos a criação de sockets e a conexão a um endereço. Vimos como criar um socket para comunicação
via internet (IPv4) utilizando o protocolo TCP e como conectar a um endereço. Além disso, vimos como criar um
sockaddr e como realizar um DNS lookup para obter o endereço IP associado a um domínio.