The remote_socket argument, in its end (well... after the port), can also contain a "/" followed by a unique identifier. This is especially useful if you want to create multiple persistent connections to the same transport://host:port combo.
Example:
<?php
$socket = stream_socket_client('tcp://mysql.example.com:3306/root', $errorno, $errorstr, $timeout, STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT);
?>
Note that while (p)fsockopen() follows a similar scheme, it doesn't have this particular feature.
stream_socket_client
(PHP 5)
stream_socket_client — Abrir una conexión de socket de dominio de Internet o Unix
Descripción
$remote_socket
[, int &$errno
[, string &$errstr
[, float $timeout = ini_get("default_socket_timeout")
[, int $flags = STREAM_CLIENT_CONNECT
[, resource $context
]]]]] )
Inicia una conexión de flujo o datagrama al destino especificado
por remote_socket. El tipo de socket creado
se determina por el transporte especificado usando el formato de URL estándar:
transporte://objetivo. Para sockets de Dominio de Internet
(AF_INET) como TCP y UDP, la porción del objetivo
del parámetro remote_socket debería consistir en
un nombre de host o dirección IP seguida de dos puntos y un número de puerto. Para sockets
de dominio de Unix, la porción de target apuntará
al archivo de socket del sistema de archivos.
Nota:
El flujo será abierto por defecto en modo de bloqueo. Se puede cambiar al modo de no bloqueo usando stream_set_blocking().
Parámetros
-
remote_socket -
Dirección del socket al que se va a conectar.
-
errno -
Será establecido al número de error a nivel de sistema si la conexión falla.
-
errstr -
Será establecido al mensaje de error a nivel de sistema si la conexión falla.
-
timeout -
Número de segundos hasta que la llamda al sistema de connect() debería esperar.
Nota: Este parámetro sólo se aplica cuando no se hace un intento de conexión asíncrona.
Nota:
Para establecer un tiempo de espera para lectura/escritura de información sobre el socket, use la función stream_set_timeout(), ya que
timeoutsólo se aplica mientras se hace una conexión al socket. -
flags -
Campo de máscara de bits que puede ser establecido a cualquier combinación de banderas de conexión. Actualmente la selección de banderas de conexión está limitada a
STREAM_CLIENT_CONNECT(por defecto),STREAM_CLIENT_ASYNC_CONNECTySTREAM_CLIENT_PERSISTENT. -
context -
Un recurso de contexto válido creado con stream_context_create().
Valores devueltos
Si se tuvo éxito, un recurso de flujo es devuelto, el cuál se puede
usar junto con las demás funciones de archivo (tales como
fgets(), fgetss(),
fwrite(), fclose(), y
feof()), FALSE si falló.
Errores/Excepciones
Si falló, los argumentos errno y
errstr serán rellenados con el error
a nivel de sistema actual que ocurrió en la llamda a nivel de
sistema de connect(). Si el valor devuelto en
errno es 0 y la
función devolvió FALSE, es una indicación de que el error
ocurrió antes de la llamada a connect(). Esto es
lo más probable debido a un problema de inicialización del socket. Observe que
los argumentos errno y
errstr siempre serán pasados por
referencia.
Ejemplos
Ejemplo #1 Ejemplo de stream_socket_client()
<?php
$fp = stream_socket_client("tcp://www.example.com:80", $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
fwrite($fp, "GET / HTTP/1.0\r\nHost: www.example.com\r\nAccept: */*\r\n\r\n");
while (!feof($fp)) {
echo fgets($fp, 1024);
}
fclose($fp);
}
?>
Ejemplo #2 Usar una conexión UDP
Recuperar el día y la hora desde el servicio "daytime" de UDP (puerto 13) en localhost.
<?php
$fp = stream_socket_client("udp://127.0.0.1:13", $errno, $errstr);
if (!$fp) {
echo "ERROR: $errno - $errstr<br />\n";
} else {
fwrite($fp, "\n");
echo fread($fp, 26);
fclose($fp);
}
?>
Notas
Los sockects UDP a veces parecerán estar abiertos sin un error, incluso si el host remoto es inalcanzable. El error sólo aparecerá cuando se lea o escriba información desde/en el socket. La razón de esto es que UDP es un protocolo no orientado a conexión, lo que significa que el sistema operativo no intenta establecer un enlace para el socket hasta que se necesite enviar o recibir informaicón.
Nota: Cuando se especifique una dirección numérica IPv6 (por ej., fe80::1), la IP debe ser encerrada entre corchetes — por ejemplo, tcp://[fe80::1]:80.
Nota:
Dependiendo del entorno, pueden no estar disponibles el dominio Unix o el tiempo de espera opcional de la conexión. Una lista de transportes disponibles se puede recuperar unsando stream_get_transports(). Véase Lista de Transportes de Sockets Soportados para una lista de transportes incluidos.
Ver también
- stream_socket_server() - Crear un socket de servidor de dominio de Internet o de Unix
- stream_set_blocking() - Establecer el modo bloqueo/no-bloqueo en un flujo
- stream_set_timeout() - Establecer un perido de tiempo de espera en un flujo
- stream_select() - Ejecuta el equivalente de la llamada al sistema select() sobre las matrices de flujos dadas con un tiempo de espera especificado por tv_sec y tv_usec
- fgets() - Obtiene una línea desde el puntero a un fichero
- fgetss() - Obtiene un línea desde un puntero a un archivo y elimina las etiquetas HTML
- fwrite() - Escritura de un archivo en modo binario seguro
- fclose() - Cierra un puntero a un archivo abierto
- feof() - Comprueba si el puntero a un archivo está al final del archivo
- Funciones de cURL
stream_socket_client is much easier and faster to use to direct sockets, because you can use directly fwrite / fget / fclose functions, but I find hard to find how to connect to a UNIX domain socket. The URL to use is "udg:///path/to/socket".
For example, to log to the log socket (like syslog), you can use:
<?php
$socket = stream_socket_client('udg:///dev/log',
$errorno,
$errorstr,
$timeout);
fwrite($socket, ...);
?>
# Some may find it useful to know that your caCert
# must be in pem format, and that PHP seems to like
# your key, cert, and cacert pem's to be concatenated
# in a single file (I suffered various "unknown chain"
# errors, otherwise)
#
# So, (linux users), concat your components as follows:
# (where current working dir is dir where
# cert components are stored)
#
# cat key.pem >certchain.pem
# cat cert.pem >>certchain.pem
# cat cacert.pem >>certchain.pem
#
# Then, the php....
##################################
<?php
$host = 'host.domain.tld';
$port = 1234;
$timeout = 10;
$cert = '/path/to/your/certchain/certchain.pem';
$context = stream_context_create(array('ssl'=>array('local_cert'=> $cert,
)));
if ($fp = stream_socket_client('ssl://'.$host.':'.$port, $errno, $errstr, 30,
STREAM_CLIENT_CONNECT, $context)) {
fwrite($fp, "\n");
echo fread($fp,8192);
fclose($fp);
} else {
echo "ERROR: $errno - $errstr<br />\n";
}
?>
For those wanting to use stream_socket_client() to connect to a local UNIX socket who can't find documentation on how to do it, here's a (rough) example:
<?php
$sock = stream_socket_client('unix:///full/path/to/my/socket.sock', $errno, $errstr);
fwrite($sock, 'SOME COMMAND'."\r\n");
echo fread($sock, 4096)."\n";
fclose($sock);
?>
I came here since fsockopen() does not support any SSL certificate checking in PHP5.
while curl is nice, I use stream_socket_client() to make XML-RPC POST requests via HTTPS and since I have not found any PHP code around that does this, I'll attach an example that also includes HTTP-Digest Auth (eg. trac's WikiRPCInterface2):
<?php
#################################################
# $host: hostname ; eg 'example.org'
# $path: request' eg '/index.php?id=123'
# $data_to_send : data to POST after the HTTP header.
#
# if $opts is an empty array() a standard HTTP to port 80 request is performed.
#
# set auth['type']='basic' to use plain-text auth,
# digest-auth will be handled automatically if $auth['username'] is set and a 401
# status is encountered. - use auth['type']='nodigest' to override.
#
##
function httpPost($host, $path, $data_to_send,
$opts=array('cert'=>"", 'headers'=>0, 'transport' =>'ssl', 'port'=>443),
$auth=array('username'=>"", 'password'=>"", 'type'=>"")
) {
$transport=''; $port=80;
if (!empty($opts['transport'])) $transport=$opts['transport'];
if (!empty($opts['port'])) $port=$opts['port'];
$remote=$transport.'://'.$host.':'.$port;
$context = stream_context_create();
$result = stream_context_set_option($context, 'ssl', 'verify_host', true);
if (!empty($opts['cert'])) {
$result = stream_context_set_option($context, 'ssl', 'cafile', $opts['cert']);
$result = stream_context_set_option($context, 'ssl', 'verify_peer', true);
} else {
$result = stream_context_set_option($context, 'ssl', 'allow_self_signed', true);
}
$fp = stream_socket_client($remote, $err, $errstr, 60, STREAM_CLIENT_CONNECT, $context);
if (!$fp) {
trigger_error('httpPost error: '.$errstr);
return NULL;
}
$req='';
$req.="POST $path HTTP/1.1\r\n";
$req.="Host: $host\r\n";
if ($auth['type']=='basic' && !empty($auth['username'])) {
$req.="Authorization: Basic ";
$req.=base64_encode($auth['username'].':'.$auth['password'])."\r\n";
}
elseif ($auth['type']=='digest' && !empty($auth['username'])) {
$req.='Authorization: Digest ';
foreach ($auth as $k => $v) {
if (empty($k) || empty($v)) continue;
if ($k=='password') continue;
$req.=$k.'="'.$v.'", ';
}
$req.="\r\n";
}
$req.="Content-type: text/xml\r\n";
$req.='Content-length: '. strlen($data_to_send) ."\r\n";
$req.="Connection: close\r\n\r\n";
fputs($fp, $req);
fputs($fp, $data_to_send);
while(!feof($fp)) { $res .= fgets($fp, 128); }
fclose($fp);
if ($auth['type']!='nodigest'
&& !empty($auth['username'])
&& $auth['type']!='digest' # prev. digest AUTH failed.
&& preg_match("/^HTTP\/[0-9\.]* 401 /", $res)) {
if (1 == preg_match("/WWW-Authenticate: Digest ([^\n\r]*)\r\n/Us", $res, $matches)) {
foreach (split(",", $matches[1]) as $i) {
$ii=split("=",trim($i),2);
if (!empty($ii[1]) && !empty($ii[0])) {
$auth[$ii[0]]=preg_replace("/^\"/",'', preg_replace("/\"$/",'', $ii[1]));
}
}
$auth['type']='digest';
$auth['uri']='https://'.$host.$path;
$auth['cnonce']=randomNonce();
$auth['nc']=1;
$a1=md5($auth['username'].':'.$auth['realm'].':'.$auth['password']);
$a2=md5('POST'.':'.$auth['uri']);
$auth['response']=md5($a1.':'
.$auth['nonce'].':'.$auth['nc'].':'
.$auth['cnonce'].':'.$auth['qop'].':'.$a2);
return httpPost($host, $path, $data_to_send, $opts, $auth);
}
}
if (1 != preg_match("/^HTTP\/[0-9\.]* ([0-9]{3}) ([^\r\n]*)/", $res, $matches)) {
trigger_error('httpPost: invalid HTTP reply.');
return NULL;
}
if ($matches[1] != '200') {
trigger_error('httpPost: HTTP error: '.$matches[1].' '.$matches[2]);
return NULL;
}
if (!$opts['headers']) {
$res=preg_replace("/^.*\r\n\r\n/Us",'',$res);
}
return $res;
}
?>
the httpPost() function was almost too long for a user-comment.. I've cut some newlines but to be complete the code snippet misses:
<?php
function randomNonce($len=0) {
$chars = "ABCDEFGHIJKMNOPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz023456789";
$i=0; $rv='';
if ($len < 1) $len= (6+rand()%10);
while ($i++ < $len) {
$rv.=$chars[rand() % strlen($chars)];
}
return $rv;
}
?>
