PHP Doku:: Liest höchstens die angegebene Anzahl Bytes von einem Socket - function.socket-read.html

Verlauf / Chronik / History: (14) anzeigen

Sie sind hier:
Doku-StartseitePHP-HandbuchFunktionsreferenzSonstige DiensteSocketsSocket-Funktionensocket_read

Ein Service von Reinhard Neidl - Webprogrammierung.

Socket-Funktionen

<<socket_listen

socket_recv>>

socket_read

(PHP 4 >= 4.1.0, PHP 5)

socket_readLiest höchstens die angegebene Anzahl Bytes von einem Socket

Beschreibung

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

Die Funktion socket_read() liest von dem Socket socket, der entweder mit socket_create() oder mit socket_accept() erzeugt wurde.

Parameter-Liste

socket

Ein gültiger Socket-Deskriptor, der von socket_create() oder socket_accept() erzeugt wurde.

length

Die Anzahl Bytes, die höchstens gelesen wird, wird in dem Parameter length angegeben. Anderenfalls können Sie mit \r, \n, oder \0 den Lesevorgang beenden (dies ist abhängig von dem Parameter type, siehe weiter unten).

type

Der optionale Parameter type ist eine vordefinierte Konstante:

  • PHP_BINARY_READ (Standardwert) - benutzt die Systemfunktion recv(). Das ist sicher zum Lesen von Binärdaten.
  • PHP_NORMAL_READ - Lesevorgänge werden bei \n oder \r beendet.

Rückgabewerte

socket_read() gibt bei Erfolg die Daten als String zurück oder FALSE, falls ein Fehler aufgetreten ist (oder auch, wenn der entfernte Host die Verbindung abgebrochen hat). Der Fehlercode kann mit socket_last_error() abgefragt werden. Dieser Fehlercode kann an die Funktion socket_strerror() übergeben werden, um eine textuelle Beschreibung des Fehlers zu erhalten.

Hinweis:

socket_read() gibt einen leeren String ("") zurück, wenn es keine Daten mehr zu lesen gibt.

Changelog

Version Beschreibung
4.1.0 Der Vorgabewert für type wurde von PHP_NORMAL_READ auf PHP_BINARY_READ gesetzt.

Siehe auch


20 BenutzerBeiträge:
- Beiträge aktualisieren...
hamishcool3 at yahoo dot co dot uk
26.10.2010 0:13
A couple of things:

The maximum amount that can be read by socket_read is, I think, 133693415 bytes, from my experimentation.

Also, here is a quick function if anyone wants to read indefinitely until a certain character is sent:

<?php
function socket_read_normal($socket, $end=array("\r", "\n")){
    if(
is_array($end)){
        foreach(
$end as $k=>$v){
           
$end[$k]=$v{0};
        }
       
$string='';
        while(
TRUE){
           
$char=socket_read($socket,1);
           
$string.=$char;
            foreach(
$end as $k=>$v){
                if(
$char==$v){
                    return
$string;
                }
            }
        }
    }else{
       
$endr=str_split($end);
       
$try=count($endr);
       
$string='';
        while(
TRUE){
           
$ver=0;
            foreach(
$endr as $k=>$v){
               
$char=socket_read($socket,1);
               
$string.=$char;
                if(
$char==$v){
                   
$ver++;
                }else{
                    break;
                }
                if(
$ver==$try){
                    return
$string;
                }
            }
        }
    }
}
?>

You can have either an array of single characters (multi-char values are automatically cut down) or one string. Multiple strings are difficult to do, because you would need to decide which string the current character should 'belong' to (if that makes any sense?) and allow for multiple strings being looked at simultaneously. To put it bluntly, I can't be bothered :)
philthathril at NOSPAM dot gmail dot com
19.10.2009 19:49
I discovered that socket_read() was not always grabbing the length that I requested. If I requested 1024 bytes, it may return any number smaller than that. Well, due to the nature of network traffic, there's no way to determine how much data will be read from the socket each time.

To read data of a certain length, this little method works well.

<?php
public function readSocketForDataLength ($socket, $len)
{
   
$offset = 0;
   
$socketData = '';
   
    while (
$offset < $len) {
        if ((
$data = @socket_read ($socket, $len-$offset, $this->readType)) === false) {
           
$this->error();
            return
false;
        }
       
       
$dataLen = strlen ($data);
       
$offset += $dataLen;
       
$socketData .= $data;
       
        if (
$dataLen == 0) { break; }
    }

    return
$socketData;
}
?>

Happy coding!
p dot hannay at ecu dot edu dot au
30.04.2009 6:30
Here is a quick example of how to use socket_read to receive data until the connection is terminated.

I've found this useful when I need to manually retrieve replies to XML-RPC queries.

<?php
$reply
= "";
do {
    
$recv = "";
    
$recv = socket_read($socket, '1400');
     if(
$recv != "") {
        
$reply .= $recv;
     }
} while(
$recv != "");

echo(
$reply);
?>
t33th4n at gmail dot com
23.02.2009 21:53
I don't know if is it stated anywhere with this clearance, but here is the source code for detecting the connection abort/closure for sockets testing with socket_read function:

<?php
$buf
= @socket_read($routes[$i][$connectionid]['tunnelsrc'], $buffer_size);
if (
$buf === '')
{
   
$routes[$i][$connectionid]['disconnected']='Conenction abort at source side';
}
?>

($buf === '') is the key :)

I was making an ecrypted tunnel script with mcrypt and was annoying that i could not detect the connection abort from any side.
tech [{at}] swatcash [{dot}] com
2.07.2008 1:23
Messed up the end of my previous one. Corrected version here:

a simple work around to non-blocking working with normal read is like so:

$read = array($socket);
$write  = NULL;
$except = NULL;
while(1) {
    $num_changed_sockets = socket_select($read, $write, $except, 0, 1);
    if ( $num_changed_sockets > '0' ) {
        socket_read($socket,10000,PHP_NORMAL_READ);
    }
}
Anonymous
15.12.2007 20:11
On non-blocking connections it may not return full length requested.
nad0r1 at hush dot ai
20.11.2007 16:59
Another way to bypass the annoying thing with telnet, that send each character as a string ,is to check if the response is "\r\n", that is the string that telnet sends when the user presses enter.

Here is an example:
<?php
error_reporting
(E_ALL);

/* Allow the script to hang around waiting for connections. */
set_time_limit(0);

/* Turn on implicit output flushing so we see what we're getting
 * as it comes in. */
ob_implicit_flush();

$address = '127.0.0.1';
$port = 100;

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

if (
socket_bind($sock, $address, $port) === false) {
    echo
"socket_bind() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}
else
  echo
'Socket ' . $address . ':' . $port . " has been opened\n";

if (
socket_listen($sock, 5) === false) {
    echo
"socket_listen() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}
else
   echo
"Listening for new clients..\n";

  
$client_id = 0;
do {
    if ((
$msgsock = socket_accept($sock)) === false) {
        echo
"socket_accept() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
        break;
    }
    else {
       
$client_id += 1;
      echo
"Client #" .$client_id .": Connect\n";
    }
     
   
/* 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));
$cur_buf = '';
    do {
        if (
false === ($buf = socket_read($msgsock, 2048))) {
            echo
"socket_read() failed: reason: " . socket_strerror(socket_last_error($msgsock)) . "\n";
            break
2;
        }
        if (
$buf == "\r\n") {
        if (
$cur_buf == 'quit') {
            echo
'Client #' .$client_id .': Disconnect' . "\n";
            break;
        }
        if (
$cur_buf == 'shutdown') {
           
socket_close($msgsock);
            break
2;
        }
       
//else {
      
$talkback = "Unknown command: " . str_replace("\r\n", '\r\n', $cur_buf) ."\n";
      
socket_write($msgsock, $talkback, strlen($talkback));
      
// }
       
echo 'Client #' .$client_id .': ' . $cur_buf . "\n";
       
$cur_buf = '';
        }
        else
$cur_buf .= $buf;
    } while (
true);
   
socket_close($msgsock);
} while (
true);

socket_close($sock);
?>
jgbustos at gmail dot com
17.07.2007 14:47
PHP on win32 developers, please look at this bug report before using the PHP_NORMAL_READ option:

http://bugs.php.net/bug.php?id=21197

In a nutshell, using PHP_NORMAL_READ will make your calls to socket_read() return an empty buffer every time.
ein at anti-logic dot com
28.06.2007 20:32
the proper way to detect a closed connection is to check socket_last_error.

Connection reset by peer is 104 (either use socket_strerror or don't suppress errors for the time being to find these out), sooo.

while($buffer=@socket_read($sock,512,PHP_NORMAL_READ)){
    echo $buffer;
}
if(socket_last_error($sock) == 104) {
    echo "Connection closed";
}
nuitari-php at nuitari dot net
14.06.2007 8:08
PHP_NORMAL_READ - reading stops at \n or \r.

This seems to be meant literally.
If there is a \r, then it will stop reading, even if there is a \n right after it. You have to call the read again just to get rid of the \n.
florin[at nospam]flachi[nospam dot]net
27.02.2007 1:22
Hello,

Here is a working solution for using socket_read in nonblocking mode for PHP_NORMAL_MODE read type, as it has some problems in standard PHP.

The function socket_normal_read will read from an unlimited number of sockets in PHP_NORMAL_MODE read type.

If you plan to use this function for an application that uses a lot of sockets that are always opening and closing, you might want to improve it to delete the respective records from $sockets and $queues static variables when you close a socket that you will not use anymore.

<?php

    define
("LINE_END", "\n");

    function
socket_normal_read ($socket, $length) {

        static
$sockets = array ();
        static
$queues = array ();
        static
$sock_num = 0;

        for (
$i = 0;  isset ($sockets[$i]) && $socket != $sockets[$i]; $i++);

        if (!isset (
$sockets[$i])) {
           
$sockets [$sock_num] = $socket;
           
$queues [$sock_num++] = "";
        }

       
$recv = socket_read ($socket, $length, PHP_BINARY_READ);
        if (
$recv === "") {
            if (
strpos ($queues[$i], LINE_END) === false)
                return
false;
        }
        else if (
$recv !== false) {
           
$queues[$i] .= $recv;
        }

       
$pos = strpos ($queues[$i], LINE_END);
        if (
$pos === false)
            return
"";
       
$ret = substr ($queues[$i], 0, $pos);
       
$queues[$i] = substr ($queues[$i], $pos+2);

        return
$ret;
    }

   
$sock1 = socket_create (AF_INET, SOCK_STREAM, SOL_TCP);
   
$sock2 = socket_create (AF_INET, SOCK_STREAM, SOL_TCP);
   
$sock3 = socket_create (AF_INET, SOCK_STREAM, SOL_TCP);
   
$sock4 = socket_create (AF_INET, SOCK_STREAM, SOL_TCP);
   
socket_connect ($sock1, "127.0.0.1", 4551);
   
socket_connect ($sock2, "127.0.0.1", 4552);
   
socket_connect ($sock3, "127.0.0.1", 4553);
   
socket_connect ($sock4, "127.0.0.1", 4554);
   
socket_set_nonblock ($sock1);
   
socket_set_nonblock ($sock2);
   
socket_set_nonblock ($sock3);
   
socket_set_nonblock ($sock4);
    while (
1) {
       
usleep (1000000);
       
$x = socket_normal_read ($sock1, 4096);
        if (
$x !== false && strlen ($x))
            echo
"1: $x\n";
       
$x = socket_normal_read ($sock2, 4096);
        if (
$x !== false && strlen ($x))
            echo
"2: $x\n";
       
$x = socket_normal_read ($sock3, 4096);
        if (
$x !== false && strlen ($x))
            echo
"3: $x\n";
       
$x = socket_normal_read ($sock4, 4096);
        if (
$x !== false && strlen ($x))
            echo
"4: $x\n";
    }

?>

To test this simple application, just use netcat (nc -vv -l 127.0.0.1 455x) to open 4 listening sockets. After you run this script, write some lines in each of those netcat sessions.

I hope this will help people that tried to use test-based socket connections in PHP using the sockets library.

I wait for feedback at the address specified.

Thank you.
dotpointer
5.02.2007 0:27
PHP 5.2.0 / Win32 / Apache 1.3 - It seems like...

PHP_BINARY_READ - works, but returns '', not FALSE...
- is blocking, until data received or connection closed
- does pass-through \r\n etc.
- returns data on data, '' on connection closed
- you can detect closed connection by checking for '' (not FALSE as stated i manual)

PHP_NORMAL_READ - not working so good...
- is non-blocking
- does not pass-through \r\n etc.
- returns false on no-data, false on connection closed :(
- (no way here to detect a closed connection...?)
- (is this a bug? http://bugs.php.net/bug.php?id=21880 )
- (is this a bug? http://bugs.php.net/bug.php?id=21197 )
- (could not get data from this option at all in fact...)

PHP_BINARY_READ seems to be the "right way to go"
for now. Both checking for '' and false to detect closed
connection is probably smart, as this "bug"(?) may
be fixed...
Niels laukens
21.08.2006 9:34
This paragraph is confusing:

socket_read() returns the data as a string on success, or FALSE on error (including if the remote host has closed the connection). The error code can be retrieved with socket_last_error(). This code may be passed to socket_strerror() to get a textual representation of the error.
Note: socket_read() returns a zero length string ("") when there is no more data to read.

My tests (on PHP 5.1.4) show that when you socket_read() on a shutdown-socket, it returns FALSE when using PHP_NORMAL_READ, but returns "" when reading in PHP_BINARY_READ.
sbasurto at yahoo dot com
11.08.2006 20:28
Interesting use of sockets:
<?php
//Use sockets and xml coool!!!
//Check this out
/*
//============================================================
//First you create a xsl template like the following called test.xsl:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="iso-8859-1"/>
<xsl:template match="/">
<table>
<tr><td><font color='red'><xsl:value-of select="test/one"/></font></td></tr>
<tr><td><font color='blue'><xsl:value-of select="test/two"/></font></td></tr>
</table>
</xsl:template>
</xsl:stylesheet>
//============================================================
*/
//============================================================
//Second: You create a php script called getResponse.php with the following content:

echo chr(60).chr(63)."xml version".chr(61)."\"1.0\" encoding".chr(61)."\"ISO".chr(45)."8859".chr(45)."1\" standalone".chr(61)."\"yes\" ".chr(63).chr(62)."\n";
...
connect to a database postgres...
$sql = "select first, second from my_table;";
...
execute the query and asign to $result array...

$xml_string = "<test>\n";
while(
$result){ 
 
$xml_string .= "<one>".$result[0]['first']."</one>\n";
 
$xml_string .= "<two>".$result[0]['second']."</two>\n";
}
$xml_string .= "</test>\n";

header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Content-Type:text/xml");
echo
$xml_string;
//============================================================

//==================S O C K E T S===============================
//Finally: Create a php script called master.php with the following content.
$server = "192.168.0.1"; //my server
$sk = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($sk, $server, 80);
$request = "GET /getResponse.php HTTP/1.0"."\r\n";
$request .= "Host:192.168.0.1 \r\n\r\n";
socket_write($sk, $request);

$doc = new DOMDocument();
$xsl = new XSLTProcessor();
$doc->load("./test.xsl");
$xsl->importStyleSheet($doc);
   
$doc->loadXML(strstr(socket_read($sk,12000),"<?xml"));
echo
$xsl->transformToXML($doc);
//==================S O C K E T S================================

"The output is the result of the query one line red and one blue".

//The most interesting thing here, is that you can create all the
//pages of your site with one xsl template and xml, but with
//the advantage of using the power of PHP.
//cool isn't it?

Regards bazz.
?>
Ronin-php at onabout dot net
2.05.2006 21:06
Just a helper for those trying to use sockets to transfer large ammounts of info.

I was pulling my hair out forever trying to figure out why different strings sent by different socket_writes were getting concatenated by socket_read.

If you have a problem with this, try a sleep(), the delay allows the server to see the difference (it is able to do one before the next arrives)
Bill Kuker
18.03.2005 16:31
Just a note that on my system the length seems to have an undocumented upper bound of 65536. I was being lazy and not read()ing in a while loop until I pointed it at real data ;)
michi at tr51 dot org
26.05.2004 21:48
if you'd like to make a "socket_read" on a linux-system connected with a flash-client (v. 6.0 r81) you have to send a string to the connected port:

<?php

  
...  //initialising communication

   
$string = "ready to get/send data\0";
   
socket_write($socket, $string);

   
//now you can read from...
   
$line = trim(socket_read($socket, MAXLINE));

    ... 
// do some stuff, finaly close connection
?>
magicking89 at hotmail dot com
31.08.2003 1:01
if you want to use a non block socket you must to use socket_last_error

if(!socket_last_error($sc)){
   if($buffer=socket_read($sc,512,PHP_NORMAL_READ)){
      echo $buffer;
   }
}

if you use it your script wont take all your memory
schst at php-tools dot de
5.07.2003 3:19
You may download a generic server class at http://www.php-tools.de
This class will accept the sockets read data from it and hands it to a callback function. Furthermore there are methods for connection handling included.

24.09.2002 20:48
Windows telnet sends/recieves one character at a time. Try adding PHP_NORMAL_READ to the end of socket_read, that might help.



PHP Powered Diese Seite bei php.net
The PHP manual text and comments are covered by the Creative Commons Attribution 3.0 License © the PHP Documentation Group - Impressum - mail("TO:Reinhard Neidl",...)