Los sockets se abren con fsockopen(). Esta función está disponible tanto en PHP3 como en PHP4. Usa el siguiente formato:
int fsockopen (string hostname, int port [, int errno [, string errstr [, double timeout]]])
En el caso de un dominio de Internet, abrirá un socket TCP conectándose al hostname en el puerto port. hostname puede ser en este caso o bien un nombre de dominio real o bien una dirección IP. Para conexiones UDP , se necesita indicar explícitamente el protocolo: udp://hostname. En sistemas Unix, hostname se usará como ruta del socket y el puerto se pondrá a 0. El timeout opcional se puede emplear para definir un timeout en segundos para la llamada del sistema.
Más información sobre fsockopen() en: http://www.php.net/manual/es/function.fsockopen.php
Para acceder a un servidor de News Usenet, se requiere usar un protocolo especfico, el NNTP, Network News Transfer Protocol.
Este protocolo está muy bien definido en el RFC977 (Request For Comment numero 977), que se encuentra en http://www.w3.org/Protocols/rfc977/rfc977.html
Este documento describe con precisión como conectarse y hablar con un servidor NNTP, usando los diferentes comandos disponibles para tal efecto.
Para conectar con un servidor NNTP necesitamos saber su hostname o dirección IP, y el puerto en el que el servidor está corriendo. Deberiamos incluir el timeout, de forma que una llamada erronea, no nos cuelgue la aplicación.
<?php
$cfgServer = "your.news.host";
$cfgPort = 119;
$cfgTimeOut = 10;
// open a socket
if(!$cfgTimeOut)
// without timeout
$usenet_handle = fsockopen($cfgServer, $cfgPort);
else
// with timeout
$usenet_handle = fsockopen($cfgServer, $cfgPort, &$errno, &$errstr, $cfgTimeOut);
if(!$usenet_handle) {
echo "Connexion failed\n";
exit();
}
else {
echo "Connected\n";
$tmp = fgets($usenet_handle, 1024);
}
?>
Ya estamos conectados al servidor, y podemos hablar con él a través del socket abierto. Supongamos que queremos bajar 10 artículos de algún grupo de noticias (news). RFC977 indica que lo primero que hay que hacer es selecionar el grupo de noticias con el comando GROUP:
GROUP ggg
El parámetro ggg es el nombre del grupo de noticias que seleccionamos (por ejemplo: es.comp.os.linux.programacion). Un listado de los grupos de noticias disponibles, se puede conseguir con el comando LIST. La respuesta del servidor nos indicará el número del primer y último artículo del grupo, y una estimación de los artículos disponibles.
Ejemplo:
chrome:~$ telnet my.news.host 119 Trying aa.bb.cc.dd... Connected to my.news.host. Escape character is '^]'. 200 my.news.host InterNetNews NNRP server INN 2.2.2 13-Dec-1999 ready (posting ok). GROUP es.comp.os.linux.programacion 211 232 222996 223235 es.comp.os.linux.programacion quit 205 .Tras recibir el comando "GROUP es.comp.os.linux.programacion", el servidor de News contesta "211 232 222996 223235 es.comp.os.linux.programacion". 211 es un código definido en el RFC (dice básicamente que el comando se ha ejecutado sin problemas - en el RFC hay más detalles). También ha contestado que hay 232 artículos, indexados desde el 222996 hasta el 223235. Estos números se hacen llamar número de artículo. Cada artículo tiene uno único. Si contamos: 222996 + 232 no da 232235. Los siete que faltan se borrarían del servidor de alguna forma. Es posible que se hayan cancelado por el propio autor (sí, esto se puede hacer!) o bien se han borrado tras una denuncia de abuso, por ejemplo.
Hay que prestar atención al hecho de que el servidor puede requerir autenticación antes de seleccionar el grupo de news, dependiendo de si es público o privado. Incluso puede dejar libre acceso a la lectura de artículos, pero necesitar autorización para publicar nuevos artículos.
<?php
//$cfgUser = "xxxxxx";
//$cfgPasswd = "yyyyyy";
$cfgNewsGroup = "alt.php";
// identification required on private server
if($cfgUser) {
fputs($usenet_handle, "AUTHINFO USER ".$cfgUser."\n");
$tmp = fgets($usenet_handle, 1024);
fputs($usenet_handle, "AUTHINFO PASS ".$cfgPasswd."\n");
$tmp = fgets($usenet_handle, 1024);
// check error
if($tmp != "281 Ok\r\n") {
echo "502 Authentication error\n";
exit();
}
}
// select newsgroup
fputs($usenet_handle, "GROUP ".$cfgNewsGroup."\n");
$tmp = fgets($usenet_handle, 1024);
if($tmp == "480 Authentication required for command\r\n") {
echo "$tmp\n";
exit();
}
$info = split(" ", $tmp);
$first = $info[2];
$last = $info[3];
print "First : $first\n";
print "Last : $last\n";
?>
Ahora que sabemos cuál es el número del último artículo disponible, es fácil bajarse los 10 últimos artículos. RFC977 dice que el comando ARTICLE puede ser usado con el número de artículo, o bien, con el ID del Mensaje.
No es lo mismo una cosa que otra. El primero es un índice que utiliza el servidor de news al que nos conectamos, y el segundo es un índice universal que representa al artículo en todos los servidores NNTP. El ID del mensaje es un número que encontramos en la cabecera del mensaje.
<?php
$cfgLimit = 10;
// upload last articles
$boucle=$last-$cfgLimit;
while ($boucle <= $last) {
set_time_limit(0);
fputs($usenet_handle, "ARTICLE $boucle\n");
$article="";
$tmp = fgets($usenet_handle, 4096);
if(substr($tmp,0,3) != "220") {
echo "+----------------------+\n";
echo "Error on article $boucle\n";
echo "+----------------------+\n";
}
else {
while($tmp!=".\r\n") {
$tmp = fgets($usenet_handle, 4096);
$article = $article.$tmp;
}
echo "+----------------------+\n";
echo "Article $boucle\n";
echo "+----------------------+\n";
echo "$article\n";
}
$boucle++;
}
?>
Así, habremos bajado los 10 últimos artículos de este grupo de noticias en este servidor. Es posible descargar sólo la cabecera de los artículos, usando el comando HEAD, o sólo el contenido, usando el comando BODY.
Para finalizar la sesión con el servidor NNTP, simplemente cerramos el socket usando fclose(), igual que si cerraramos un fichero.
<?php
// close connexion
fclose($usenet_handle);
?>