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 — Bir Genel Ağ veya Unix alan soketi bağlantısı açar
Açıklama
$uzak_soket
[, int &$hatanum
[, string &$ileti
[, float $süre = ini_get("default_socket_timeout")
[, int $seçenekler = STREAM_CLIENT_CONNECT
[, resource $bağlam
]]]]] )
uzak_soket ile belirtilen hedefe bir akım veya
verikatarı bağlantısı açar. Oluşturulacak soketin türü standart URL
biçemi kullanılarak belirtilir: şema://hedef.
TCP ve UDP gibi Genel Ağ Alanı soketleri (AF_INET)
için, uzak_soket'in hedef
parçası bir konak ismi veya IP adresinden sonra gelen bir ikinokta imi
ve bir port numarasından oluşur. Unix alan soketleri için
hedef parçası ise dosya sistemi üstündeki bir soket
dosyasını göstermelidir.
Bilginize:
Akım öntanımlı olarak engellenebilen türde açılır. stream_set_blocking() işlevini kullanarak engellenmeyen kipe geçebilirsiniz.
Değiştirgeler
-
uzak_soket -
Bağlanılacak soketin adresi.
-
hatanum -
Bağlantı başarısız olursa sistem seviyesindeki hatanın numarası bu değiştirgeye atanır.
-
ileti -
Bağlantı başarısız olursa sistem seviyesindeki hatanın açıklaması bu değiştirgeye atanır.
-
süre -
connect() sistem çağrısının zaman aşımına uğrayacağı saniye sayısı.
Bilginize: Bu değiştirge sadece eşzamansız bağlantı yapılmaya çalışılmıyorsa uygulanır.
Bilginize:
Soket üzerinde veri okumak veya yazmak için zaman aşımı, sokete bağlantı yapılırken uygulanacak şekilde sadece stream_set_timeout() tarafından belirlenir.
-
seçenekler -
Bağlantı seçeneklerinin birleşimi olarak bir bit maskesi. Geçerli bağlantı seçenekleri:
STREAM_CLIENT_CONNECT(öntanımlı),STREAM_CLIENT_ASYNC_CONNECTveSTREAM_CLIENT_PERSISTENT. -
bağlam -
stream_context_create() ile oluşturulmuş geçerli bir bağlam özkaynağı.
Dönen Değerler
Başarısızlık durumunda FALSE, aksi takdirde
fgets(), fgetss(),
fwrite(), fclose() ve
feof() gibi diğer dosya işlevleri ile kullanılmak üzere
bir akım özkaynağı döner.
Hatalar/İstisnalar
Seçimlik hatanum ve ileti
değiştirgeleri belirtilmişse, bunlara socket(),
bind() ve listen() sistem
çağrılarında oluşan sistem seviyesindeki hatanın bilgisi atanır. Eğer
hatanum değiştirgesinde 0
döndüğünde işlev FALSE döndürmüşse hata, bind()
çağrısından önce oluşmuş demektir. Bu sorunla çoğunlukla soketin
ilklendirilmesi sırasında karşılaşılır. hatanum
ve ileti değiştirgelerinin daima gönderimli
aktarıldıklarına dikkat ediniz.
Örnekler
Örnek 1 - stream_socket_client() örneği
<?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);
}
?>
Örnek 2 - UDP bağlantı kullanımı
localhost üzerindeki "daytime" (port 13) UDP hizmetinden tarih ve saati öğrenmek.
<?php
$fp = stream_socket_client("udp://127.0.0.1:13", $errno, $errstr);
if (!$fp) {
echo "HATA: $errno - $errstr<br />\n";
} else {
fwrite($fp, "\n");
echo fread($fp, 26);
fclose($fp);
}
?>
Notlar
UDP soketler bazen, uzak konağa erişilemediği halde hata vermeksizin açılmış gibi görünür. Hata sadece bağlantıya veri okuyup yazmaya çalıştığında ortaya çıkar. Bunun sebebi UDP'nin bir bağlantısız protokol olmasıdır. Bağlantısız protokollerde işletim sistemi, asıl veri alınıncaya veya gönderilinceye kadar sokete bir bağlantı oluşturmaya çalışmaz.
Bilginize: Bir sayısal IPv6 adresi (fe80::1 gibi) belirtirken port numarasıda belirtmek isterseniz IP adresini köşeli ayraçlar arasına almalısınız. Örnek: tcp://[fe80::1]:80.
Bilginize:
Platforma bağlı olarak, Unix alan soketleri mevcut olmayabilir. Kullanılabilecek aktarım çeşitlerinin listesi stream_get_transports() işlevi ile alınabilir. Yerleşik aktarım türlerinin tam listesini Desteklenen Soket Aktarımlarının Listesi sayfasında bulabilirsiniz.
Ayrıca Bakınız
- stream_socket_server() - Bir Genel Ağ veya Unix alan sunucusu soketi oluşturur
- stream_set_blocking() - Akımın engelleme kipini ayarlar
- stream_set_timeout() - Akımın zaman aşımı süresini ayarlar
- stream_select() - Belirtilen akım dizisi üzerinde belirtilen zaman aşımı ile select() sistem çağrısının eşdeğeri olarak çalışır
- fgets() - Dosya tanıtıcısından bir satır döndürür
- fgetss() - Dosya tanıtıcısından bir satırı HTML etiketlerinden arındırarak döndürür
- fwrite() - Dosyaya ikil kipte yazar
- fclose() - Açık bir dosya tanıtıcısını kapatır
- feof() - Bir dosya tanıtıcısı üzerinde konum dosya sonunda mı diye bakar
- cURL İşlevleri
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, ...);
?>
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;
}
?>
# 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";
}
?>
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;
}
?>
