Sockets em PHP parte 1

Iniciado por branco, 19 de Junho , 2008, 05:24:36 PM

tópico anterior - próximo tópico

0 Membros e 1 Visitante estão vendo este tópico.

branco

Sockets em PHP

Introdução
Seguindo a lógica do artigo Introdução a Sockets, agora mostrarei a vocês como utilizá-los em PHP.
Se for o seu caso, tire da cabeça a idéia de que PHP é utilizado apenas na criação de sites.

Como utilizar
Como eu disse no artigo de introdução, o processo para fazer um pedido é o seguinte:
1 - Cria o Socket
2 - Conecta o Socket no destino
3 - Faz o pedido

Segue abaixo a função utilizada para criar o Socket:

resource socket_create  ( int $domain  , int $type  , int $protocol  )

O resource é o valor que a função retorna.

O parâmetro int $domain
Ele pode ter três valores, que são:
AF_INET, AF_INET6, e AF_UNIX

AF_INET é a família de protocolo que utiliza TCP e UDP para transferência de dados, IPv4.
AF_INET6 só tem a diferença de ser IPv6.
AF_UNIX é utilizado para comunicações locais.

O parâmetro type
Seleciona o tipo de comunicação que vai ser utilizado, eles podem ser:

SOCK_STREAM,SOCK_DGRAM,SOCK_SEQPACKET,SOCK_RAW,SOCK_RDM.

Nesse artigo, utilizaremos apenas SOCK_STREAM, em que o TCP se basea.

O parâmetro protocol
Ele configura o protocolo que será utilizado dentro da família que foi especificada no parâmetro domain.
As opções são:
icmp,udp,tcp

Nesse artigo abordaremos apenas TCP.
Você pode utilizar as constantes SOL_TCP e SOL_UDP para passar o protocolo, ou deve utilizar a função getprotobyname() para pegar o correspondente numérico de um protocolo, exemplo : getprotobyname('tcp').

Agora que já conhecemos o básico, podemos criar um Socket:

$sk = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);

O próximo passo é utilizar o Socket criado para se conectar em um destino, segue o protótipo da função que será utilizada com esse objetivo:

bool socket_connect  ( resource $socket  , string $address  [, int $port  ] )

bool é o valor retornado pela função, ou seja, true ou false.
O primeiro parâmetro, resource $socket, é o socket que criamos.
no caso, é o $sk.

O parâmetro string $address, é o destino onde nos conectaremos.
O parâmetro int $port é utilizado apenas quando estamos trabalhando com a família AF_INET, e especifica a qual porta nos conectaremos no destino.

Então, segue a conexão com o host remoto:

$err = socket_connect($sk, 'www.darkers.com.br', 80);

Depois que nos conectarmos, o próximo passo é fazer o pedido de uma página qualquer.
Como estamos trabalhando com o Socket puro, deveremos escrever inclusive a requisição da página.
O pedido pode ser esse:

$pedido = "GET / HTTP/1.1 \r\n";
$pedido .= "Host: www.darkers.com.br \r\n\r\n";

GET / pede para o Darkers nos mandar a página inicial.
HTTP/1.1 diz qual versão do protocolo http estamos utilizando.
\r\n faz com que haja uma nova linha, ela faz parte da especificação RFC 2616.
Host: www.darkers.com.br diz ao servidor para qual host estamos enviando a requisição.
\r\n\r\n é devido à especificação, todo request deve terminar com duas novas linhas.

E agora, temos que mandar alguém enviar esse pedido, e quem vai fazer isso é a função:

int socket_write  ( resource $socket  , string $buffer  [, int $length  ] )

Como podem observar, a função retorna int, que significa a quantidade de bytes que foram escritos com sucesso no socket que foi passado pelo parâmetro resource $socket. Se um erro acontecer, a função retorna false.

O parâmetro string $buffer espeficica a string que será escrita, e int length que é opnicional, envia o tamanho do buffer.
Sendo assim, segue a utilização em conjunto com nossas criações anteriores:

$err = socket_write($sk,$pedido, strlen($pedido));

Pronto! Fizemos o pedido =]
O que eu recomendo agora, é que você faça o download de um sniffer e utilize-o para analisar a resposta do servidor.
Ele é útil não somente para ver a resposta do servidor, mas também para ver se você está mandando a requisição corretamente.

Espero que esteja tudo certo ai com o seu socket, antes de continuarmos.
Faça um echo $err para verificar quantos bytes foram escritos.
Caso tenha acontecido algum erro, entre em contato.

Agora, vamos falar do recebimento das respostas do socket.
A função utilizada é:

   string socket_read  ( resource $socket  , int $length  [, int $type  ] )

A string retornada é a mesma que foi lida.
O parâmetro resource $socket, é o socket onde escreveremos, no caso, oque criamos, que foi $sk.
int $length especifica a quantidade de bytes a ser lida.
Você também pode especificar o parâmetro type para ser PHP_NORMAL_READ, assim a leitura vai parar quando encontrar o total de bytes especificado em length, ou um caractere de nova linha (\n ou \r).

Caso aconteça algum erro, o valor retornado por essa função será false.
No nosso exemplo podemos utilizar:

$dados = socket_read($sk,4000,PHP_NORMAL_READ);

E fazer um echo para mostrar os dados:

echo $dados;

Pronto! é só isso :)
Mas, fazendo da maneira acima, você vai ler apenas até quando a função encontrar uma nova linha (\n ou \r).
Então, torna-se necessário a utilização do while, por exemplo:

while($dados = socket_read($sk,4000,PHP_NORMAL_READ)) {
   echo $dados;
}

Agora você vai lê todos os dados que estiverem no buffer, ou seja, todos os dados que o Darkers te respondeu.
Quando executar esse script verá a página do Darkers no seu browser.

Depois de terminar a utilização do socket, deve-se finalizá-lo:

socket_close($sk);

Pronto!

O código completo fica assim:

$sk = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
$err = socket_connect($sk, 'www.darkers.com.br', 80);
$pedido = "GET / HTTP/1.1 \r\n";
$pedido .= "Host: www.darkers.com.br \r\n\r\n";
$err = socket_write($sk,$pedido, strlen($pedido));
while($dados = socket_read($sk,4000,PHP_NORMAL_READ)) {
   echo $dados;
}
socket_close($sk);
?>

Repare bem que pegamos tudo que foi retornado, inclusive os headers.

Até a parte 2.
Olha o trem... Quem vai ficar, quem vai partir? Quem vai chorar, quem vai sorrir?

DarkGenesis

Aguardando a parte II.
Tks branco.