Introducción a los Sockets BSD en PHP.

¿Qué es un socket?

Un socket es un punto de comunicación entre dos máquinas.

Modelo Cliente-Servidor

Cliente-servidor
Tipicamente los sockets BSD consisten en dos niveles de procesos de aplicación separados; un proceso (el cliente) realiza una conexión mientras que el segundo proceso (servidor) las acepta, a esto se le conoce como Modelo Cliente – Servidor.

Tipos de Sockets

  • Sockets STREAM

    Este tipo de socket permite trabajar realizando/esperando conexiones a puertos TCP.

  • DATAGRAM

    Lo mismo que el anterior pero trabajando con paquetes UDP

  • RAW

    O mejor conocidos como de bajo nivel, permiten trabajar con las cabeceras IP, TCP, UDP, construir nuestros propios packetes etc.. Por desgracia no veremos su implementación.

Implementación en PHP.

PHP no sólo nos ofrece la función “fsockopen” para tratar con sockets, también nos permite trabajar con Sockets BSD, del cuál he visto muy poca información.

Instalación en Linux

Por fortuna para los usuarios de Linux los sockets BSD ya vienen habilitados, y si no, tendremos que recompilar nuestro php con la opción “—socket”.

Instalación en Windows

Para los usuarios de Windows PHP no trae la extensión habilitada.
Para habilitar los sockets bsd seguir los siguientes 4 pasos:

  1. Ir a la carpeta donde está instalado php y abrir el fichero “php.ini”.
  2. Buscar la línea ;extension=php_sockets.dll y remover el punto y coma que tiene al principio.
  3. Buscar la línea que contiene el directorio de extensiones php “extension_dir” y verificar que en la ruta declarada se encuentre la librería “php_sockets.dll”.
  4. Salvar los cambios en el php.ini.

Ya que tenemos habilitados los sockets en PHP sea en Windows o Linux nos dedicaremos ahora a su implementación! (Lo que tanto hemos esperado muchachos!)

Ejemplo de Cliente.

Me pondría a explicar cada una de las funciones con sus prototipos para que todos pudieramos entender pero sería muy aburrido y pienso que es más fácil comprender/aprender con un buen código por lo tanto aquí les dejo un ejemplo de cómo conectar con algoritmatica

 $sock = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
// Creamos un socket con socket_create, le pasamos como primer parámetro la constante del tipo de dominio a utilizar, luego el tipo de socket (SOCK_STREAM, SOCK_DGRAM, SOCK_RAW) y por ultimo el protocolo
$result = socket_connect($sock,gethostbyname(“www.algoritmatica.com”),80);
// Pasamos como primer parámetro el descriptor del socket creado, usamos gethostbyname para resolver el nombre de dominio (host) y por último indicamos que queremos conectar al puerto 80
// Creamos la petición HTTP
$header .= “HEAD / HTTP/1.0\r\n”;
$header .= “Host: www.algoritmatica.com”;
$header .= “Connection:Close”;
socket_write($sock,$header,strlen($header));
// Pasamos como primer parámetro el descriptor de socket, la petición http y su tamaño en bytes usando la función strlen.
while($respuesta = socket_read($sock,2048))
// Esperamos la respuesta  y los datos.
{
echo $respuesta;
}
socket_close($sock);
// Cerramos socket y conexión.

Ejemplo de servidor

Este ejemplo está extraído de la referencia de PHP :) (que todos deberíamos tener a la hora de programar… agradezco el código!)

set_time_limit(0);
ob_implicit_flush();

$address = '192.168.1.3';
$port = 10000;

if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0) {
  echo "socket_create() failed: reason: " . socket_strerror($sock) . "\n";
}

if (($ret = socket_bind($sock, $address, $port)) < 0) {
  echo "socket_bind() failed: reason: " . socket_strerror($ret) . "\n";
}

if (($ret = socket_listen($sock, 5)) < 0) {
  echo "socket_listen() failed: reason: " . socket_strerror($ret) . "\n";
}

do {
  if (($msgsock = socket_accept($sock)) < 0) {
    echo "socket_accept() failed: reason: " . socket_strerror($msgsock) . "\n";
    break;
  }
  /* Send instructions. */
  $msg = "\nWelcome to the PHP Test Server. \n" .
  "To quit, type 'quit'. To shut down the server type 'shutdown'.\n";
  socket_write($msgsock, $msg, strlen($msg));

  do {
    if (false === ($buf = socket_read($msgsock, 2048, PHP_NORMAL_READ))) {
      echo "socket_read() failed: reason: " . socket_strerror($ret) . "\n";
      break 2;
    }
    if (!$buf = trim($buf)) {
      continue;
    }
    if ($buf == 'quit') {
      break;
    }
    if ($buf == 'shutdown') {
      socket_close($msgsock);
      break 2;
    }
    $talkback = "PHP: You said '$buf'.\n";
    socket_write($msgsock, $talkback, strlen($talkback));
    echo "$buf\n";
  } while (true);
  socket_close($msgsock);

} while (true);

socket_close($sock);

Fin

He dado una pequeña introducción a los sockets, el modelo cliente-servidor y sockets en PHP, sería interesante que para una próxima entrada escriba sobre sockets pero en C y su implementación en windows (winsockets).

¿Algún comentario o sugerencia? cualquier cosa será bienvenida! no dejes de comentar!
gracias.

Para mayor información:
http://www.php.net/manual/es/ref.sockets.php

hola probe tu codigo pero me lanza los siguientes errores cuando corro el servidor, utlizo wamp5

socket_create() fallo: razon: Se ha proporcionado un argumento no válido.
Warning: socket_bind() [function.socket-bind]: unable to bind address [0]: Sólo se permite un uso de cada dirección de socket (protocolo/dirección de red/puerto) in C:\wamp\www\yenny\servidor1.php on line 14

Warning: socket_listen() [function.socket-listen]: unable to listen on socket [0]: Se ha proporcionado un argumento no válido. in C:\wamp\www\yenny\servidor1.php on line 19

Warning: socket_accept() [function.socket-accept]: unable to accept incoming connection [0]: Se ha proporcionado un argumento no válido. in C:\wamp\www\yenny\servidor1.php on line 25

Warning: socket_write() expects parameter 1 to be resource, boolean given in C:\wamp\www\yenny\servidor1.php on line 32

Warning: socket_read() expects parameter 1 to be resource, boolean given in C:\wamp\www\yenny\servidor1.php on line 35
Amir Canto dice:

Estimada Yenny Colmenarez, primero que nada es un gusto que haya mujeres entrando al blog (Que por cierto he notado con son muchas :) )

Bueno, segundo, el código no es mío, lo he tomado de la referencia de PHP.net, sin embargo has probado quitar los caracteres raros que salieron en el código?
<0 <-

Por que yo estoy probando el código y todo funciona perfecto

Mmm puedes cambair los argumentos de socket create así

socket_create(AF_INET, SOCK_STREAM,tcp)

y checa si no te funciona si no vuelve a probar con

socket_create(AF_INET,SOCK_STREAM,SOL_TCP)

y si NADA! entonces avisanos por aquí para que nos pongamos a checarlo bien
yo ahora estoy en el trabajo y te contesto así de rápido..

de todas formas estamos en contacto
y muchas gracias por avisar =)

Eduardo: Definitivamente, hace falta el foro para estas cuestiones =).

Un saludo y gracias por comentar.

hola como estas probe con las dos observaciones que me diste y tampoco funciona..

que podra ser??

Eduardo dice:

Hola Yenny,
Acabo de arreglar el código (ya que salían caracteres raros en lugar del signo “menor que”).

Los errores que describes, son raros, en primer lugar porque ni siquiera te crea el socket.

Prueba este código y dime si te marca error:

<?PHP
set_time_limit(0);
ob_implicit_flush();

$address = '127.0.0.1';
$port = 85;
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

if (($sock) < 0) {
  echo "socket_create() failed: reason: " . socket_strerror($sock) . "\n";
}

socket_close($sock);
?>

si todo va bien… no debería mostrarte nada.

Si te marca error, será mandinga, o el wamp, aunque no creo … ummmm

Ahora, con respecto al error del BIND, lo que te está diciendo es “Ya tienes un puerto abierto por otro programa, por lo que no puedo abrir ese puerto!”

Si el puerto ya está en uso, no lo podrás abrir de nuevo.

$address = '127.0.0.1';
$port = 85;

Prueba poner otro puerto, por ejemplo el 86, 87, 88, si con esos no te funciona entonces no sé, pero la IP original que puso Amir es de su LAN.

Todos los demás errores se derivan de estos dos primeros, una vez que arregles esas dos funciones todo lo demás debería funcionarte.

Si sigue sin funcionar vuelve a postear le daremos una revisada mas profunda.

Nos avisas ;) gracias.

Este comentario te ha servido? y colabora con el blog
Responder
Mar dice:

Hola a todos, este blog me parece buenísimo, pues es de gran ayuda para todos los que iniciamos en esto de los socket con php.

He probado el mismo ejemplo y le he hecho algunas modificaciones como esta:

if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
    echo "socket_create() fall&oacute;: motivo: " . socket_strerror($sock) . "\n";}

if (($ret= socket_bind($sock, $direccion, $puerto))=== false) {
    echo "socket_bind() fall&oacute;: motivo: " . socket_strerror($ret) . "\n";
}

if (($ret = socket_listen($sock, 5)) === FALSE) {
    echo "socket_listen() fall&oacute;: motivo: " . socket_strerror($ret) . "\n";
}

do {
    if (($mens_sock = socket_accept($sock)) === false) {
        echo "socket_accept() fall&oacute;: motivo " . socket_strerror($mens_sock) . "\n";
       break;

tengo el mismo error que Yenny Colmenarez, exepto por la ultima linea que me aparece:

Warning: socket_bind() [function.socket-bind]: unable to bind address [0]: Sólo se permite un uso de cada dirección de socket (protocolo/dirección de red/puerto) in C:\xampp\htdocs\xampp\prueba\server01.php on line 19
socket_bind() falló: motivo: La operación se ha completado correctamente. 

Warning: socket_listen() [function.socket-listen]: unable to listen on socket [0]: Se ha proporcionado un argumento no válido. in C:\xampp\htdocs\xampp\prueba\server01.php on line 23
socket_listen() falló: motivo: La operación se ha completado correctamente. 

Warning: socket_accept() [function.socket-accept]: unable to accept incoming connection [0]: Se ha proporcionado un argumento no válido. in C:\xampp\htdocs\xampp\prueba\server01.php on line 28
socket_accept() falló: motivo La operación se ha completado correctamente.

Ya probe el ejemplo que nos pone Eduardo y al parecer todo va bien, de hecho mi problema no parece ser el socket_create, sino las otras funciones como el socket_bind, socket_listen, socket_acept y otras funciones similares, pues he probado varios ejemplos y el mensaje siempre es el mismo. Utilizo el xampp versión 2.5 y php versión 5.

Por favor necesito de la ayuda de todos uds. Gracias por anticipado y sigan creando más blogs como éste.

Eduardo dice:

Hola Mar,

el error del bind está en los parametros que le estas pasando:

socket_bind($sock, $direccion, $puerto))

$direccion y $puerto, deben ser una direccion IP que pueda ser abierta (por ejemplo, la tuya local!) y el puerto es lo que mas problemas puede dar.

¿Que puerto debes abrir? Hay que estar atento con esto, porque yo la primera vez que ejecuté el código, le puse el puerto 80 y me marcó error (porque ese puerto ya lo tengo abierto).
Puse el puerto del ejemplo de Amir, el 10000, y también me marcó error (ni idea pero supongo que debo tener otro programa escuchando en ese puerto), puse los puertos 81,82,83,84… esos si me dejó abrirlos.

Los demás errores, del listen y accept, tienen que ver con el bind… una funcion falla y fallan todas las demás.

Espero haberte ayudado, Salu2.

Este comentario te ha servido? y colabora con el blog
Responder
Mar dice:

Saludos Eduardo.

Gracias por responder a mi pregunta.En cuanto a lo del puerto tienes razon.El puerto 80 esta abierto, use otros como el 82,83,85, y funcionó por un segundo.La primera que llamo a telnet me envia:
Welcome to the PHP Test Server.
To quit, type ‘quit’. To shut down the server type ‘shutdown’

Y cuando pensé que todo iba muy bien(cosa de segundos), recibo en siguiente mensaje:

Se ha perdido la conexión con el host.

¿¿Alguien tiene idea de lo que pueda estar pasando?? Espero respuestas u otros ejemplos. Toda información me va caer muy bien. Saludos a todos los que visitan este blog. Hasta luego.

Angelica dice:

Hola , jeje entre a tu blog y me ha percido bueno, aunque con esto de los sockets tengo problemas para comprenderlo muy bien, mi pregunta va dirigida a que si se puede hacer un ping con ese tipo de funciones, gracias

Eduardo dice:

Hola Angelica,
Efectivamente, puedes realizar un ping. Aunque con el solo hecho de “conectarte” a un servidor, podrias verificar si esa conexión es válida o no para saber si el servidor existe.

Salu2.

Este comentario te ha servido? y colabora con el blog
Responder
Mar dice:

Hola de nuevo. Me gustaria saber como enviar datos(ya sea imagenes, datos,informacion confidencial, etc) de una pc a otra a través de internet, ya he realizado pruebas en una lan y funciona perfectamente, pero a través de internet no se como hacerlo. Es necesario abrir los puertos de módem o como?

Angelica dice:

Hola muchas gracias por tu respuesta, realmente lo que necesito es saber si mis clientes estan conectados (prendidos) para pdoer enviar archivos a ellos, los archivos los envio por ftp, si con el ping se que estan conectados, como puedo verificar los puertos, con lo mismo de los sockets?, perdon por las molestias gracias.

Eduardo dice:

@Angelica,
Tendrías que tener un programa corriendo en las computadoras de tus clientes. Ese programa debería abrir un puerto X (previamente configurando el router/firewall para que deje abrirlo) y luego si, con los sockets BSD podrías verificar si ese host con ese puerto abierto existe.

Este comentario te ha servido? y colabora con el blog
Responder
Humberto dice:

Hola Amir.

Buscando información sobre Sockets, encontre este blog, el cual me pareció muy interesante.

Yo tengo la siguiente inquietud:

Me pidieron en el trabajo generar un XML con PHP y enviarlo a una aplicación JAVA vía socket. Y para ser sincero, no tengo idea de como hacer el envío.

Si pudieras orientarme, lo agradeceria mucho.

Saludos cordiales.

Amir dice:

@Humberto:

Es simple, pon tu aplicación Java a escuchar en determinado puerto esperando por la comunicación

y ahora con PHP simplemente crea un socket, (socket_create) y usa socket_connect al host y puerto donde está corriendo tu app java y con socket_write envia el XML generado, sea manualmente o con las funciones para manipular XML’s

un saludo.
Ya luego tu app java parsea los datos recividos..

Este comentario te ha servido? y colabora con el blog
Responder

Buen dia, que tema tan interesante. los felicito

de antemano gracias por su atencion y/o ayuda tengo la siguiente duda

por consola hago un telnet 200.74.146.84 8600 y se coencta.
luego escribo la M y eneter
y me aparece un emnsaje

Deseo hacer esto por socket escribi el sigueitne codigo

$conexion = fsockopen (“200.74.146.84″, 8600);

if ($conexion) {
echo “Conexion realiaza con éxito”;

fwrite($conexion, “MARCHIV270223.9999.23OJO.1.98663305.escobar\n”);
echo fread($conexion, 1000).”-”;

fclose ($conexion);

se coencta bien pero a la hora de escribir o leer algo se demora 120 segundos y no muestra nada,

otra cosa esto solo funciona desde el pc dodne trabjo pues para hacer esto existe un vpn entre dos redes

sera que me podrian colaborar o ilustrar como podria hacer esto

mil gracias

Una pregunta importante…
solo he programado software offline, y estoy aprendiento programacion web, comprendo el codigo pero tengo una pregunta basica
donde meto el archivo de cliente y donde meto el del servidor??

estoy usando wamp como servidor local

Roberto dice:

Hola… estoy teniendo problemas con socket_create ya que me esta devolviendo “Warning: socket_create() Unable to create socket [1]: Operation not permitted” . No he podido encontrar en ningun lado una rta que indique a que se debe y como solucionarlo.
He intentado con fsockopen pero me devuelve “fsockopen() [function.fsockopen]: unable to connect to”…
Si alguien lidio ya con esto y sabe como solucionarlo, les agradecer mucho.
Saludos!

Pedro dice:

en que parte del servidor tengo que escribir el codigo server para poder abrirlo desde un telnet???

genius551v dice:

Hola,

Mucho gusto, magnifico foro, felicitaciones.

Necesito algo de ayuda con esto:

Tengo la necesidad de enviarle un estado a una aplicacion cliente (web) pero sin necesidad de que sea echa por una peticion del cliente, me explico:

1. Pongamos como ejemplo que necesito hacer una aplicacion (web) para monitorear el estado de una maquina.

2. la aplicacion digamos que es una pagina que me muestra un aviso con fondo verde si el estado de la maquina es activo.

——————
|ESTADO: ACTIVA|
——————

3. supongamos que la aplicacion se conecta a un servidor web Apache con PHP, lo mas normal. Y la maquina tiene alguna interfaz conectada por algun puerto al servidor y a traves de esta envia su estado al servidor. (esto lo hacen los electronicos, no recuerdo como se llama ¿¿¿PL???)

4. si la maquina se detiene, envia al servidor el cambio de estado, pasa de ACTIVA a DETENIDA (o 1 a 0)

5. cuando se da ese evento, el servidor (y PHP) deberia de enviarle es estado a la aplicacion cliente. (el cliente NO lo ha solicitado)

6. el cliente actualizaria su pantalla para mostrar

———————
|ESTADO: DETENIDA|
———————

PREGUNTA: Puedo pretender utilizar sockets para hacer esto???

Espero cordialmente que alguien pueda ayudarme o guiarme sobre que y por donde puedo buscar.

Cualquier colaboracion o idea de como hacerlo sera bienvenida.

Gracias a todos!!!

me urge… :oops:

NOTA: aclaro que necesito que el evento de enviarle los datos al cliente debe ser determinado por el servidor, solo y cuando el evento se realiza en la maquina por ejemplo. No me sirve que sea un SCRIPT del lado del cliente que este ejecutando una tarea de consultar el estado en el servidor cada cierto tiempo.

Es decir, el servidor debe saber a que cliente le envia la informacion.

Esteban dice:

Realmente muy bien explicado, codigo probado, y realmente funciona.

Ahora, tengo una duda, puede ejecutarse el servidor en un directorio personal de apache? es decir, en la universidad esta permitido el uso de sockets, pero la pregunta es, cuando se hace el socket_bind, se puede pasar como parametro el URL personal “host/~usuario/servidor.php” ?