PHPのお勉強!

PHP TOP

socket_recv

(PHP 4 >= 4.1.0, PHP 5, PHP 7, PHP 8)

socket_recv接続したソケットからデータを受信する

説明

socket_recv(
    Socket $socket,
    ?string &$data,
    int $length,
    int $flags
): int|false

socket_recv() 関数は、 socket から length バイトのデータを受信して data に格納します。 socket_recv() を使うと、 接続したソケットからデータを収集することができます。 さらに、フラグを指定して関数の挙動を変更することもできます。

data は参照渡しなので、 引数リストには変数で渡さなければなりません。 socket から socket_recv() で読み込んだデータが data に入ります。

パラメータ

socket

socket は、事前に socket_create() で作成した Socket クラスのインスタンスでなければなりません。

data

受信したデータが、 data で指定した変数に格納されます。 エラーが発生したり接続がリセットされたりデータが存在しなかったりした場合は、 data には null が設定されます。

length

最大 length バイトまでをリモートホストから取得します。

flags

flags の値は、次のフラグを論理 OR (|) 演算子で組み合わせたものとなります。

flags のとりうる値
フラグ 説明
MSG_OOB out-of-band を処理します。
MSG_PEEK 受信キューの先頭からデータを取得し、受信したデータをキューから削除しません。
MSG_WAITALL 最低 length バイト受信するまでブロックします。 しかし、シグナルを受け取ったりリモートホストが接続を切断したりした場合は、 この関数が返すデータがそれより少なくなる可能性があります。
MSG_DONTWAIT このフラグを設定すると、正常にブロックされている状態でも関数が結果を返します。

戻り値

socket_recv() は、受信したバイト数を返します。 エラーが発生した場合は false を返します。 実際のエラーコードを取得するには socket_last_error() をコールします。 このエラーコードを socket_strerror() に渡すと、エラーに関する説明テキストを取得することができます。

変更履歴

バージョン 説明
8.0.0 socket は、Socket クラスのインスタンスになりました。 これより前のバージョンでは、リソース型でした。

例1 socket_recv() の例

この例は、 の最初の例を書き換えて socket_recv() を使う形式にしたものです。

<?php
error_reporting
(E_ALL);

echo
"<h2>TCP/IP Connection</h2>\n";

/* WWW サービスのポートを取得します */
$service_port = getservbyname('www', 'tcp');

/* 対象となるホストの IP アドレスを取得します */
$address = gethostbyname('www.example.com');

/* TCP/IP ソケットを作成します */
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (
$socket === false) {
echo
"socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
} else {
echo
"OK.\n";
}

echo
"Attempting to connect to '$address' on port '$service_port'...";
$result = socket_connect($socket, $address, $service_port);
if (
$result === false) {
echo
"socket_connect() failed.\nReason: ($result) " . socket_strerror(socket_last_error($socket)) . "\n";
} else {
echo
"OK.\n";
}

$in = "HEAD / HTTP/1.1\r\n";
$in .= "Host: www.example.com\r\n";
$in .= "Connection: Close\r\n\r\n";
$out = '';

echo
"Sending HTTP HEAD request...";
socket_write($socket, $in, strlen($in));
echo
"OK.\n";

echo
"Reading response:\n\n";
$buf = 'This is my buffer.';
if (
false !== ($bytes = socket_recv($socket, $buf, 2048, MSG_WAITALL))) {
echo
"Read $bytes bytes from socket_recv(). Closing socket...";
} else {
echo
"socket_recv() failed; reason: " . socket_strerror(socket_last_error($socket)) . "\n";
}
socket_close($socket);

echo
$buf . "\n";
echo
"OK.\n\n";
?>

上の例の出力は、 たとえば以下のようになります。

<h2>TCP/IP Connection</h2>
OK.
Attempting to connect to '208.77.188.166' on port '80'...OK.
Sending HTTP HEAD request...OK.
Reading response:

Read 123 bytes from socket_recv(). Closing socket...HTTP/1.1 200 OK
Date: Mon, 14 Sep 2009 08:56:36 GMT
Server: Apache/2.2.3 (Red Hat)
Last-Modified: Tue, 15 Nov 2005 13:24:10 GMT
ETag: "b80f4-1b6-80bfd280"
Accept-Ranges: bytes
Content-Length: 438
Connection: close
Content-Type: text/html; charset=UTF-8

OK.

add a note

User Contributed Notes 13 notes

up
31
dgk at tcde dot ru
20 years ago
I've used socket_select and socket_recv with a while loop and found myself in trouble when remote side closed connection. The code below produced infinite loop and socket_select returned immediately (which lead to high cpu time consumption).

<?

socket_set_nonblock($my_socket);
$streams = array($my_socket/*, ... */);

$lastAccess = time();
while (socket_select($streams, $write = NULL, $except = NULL, SLEEP_TIME_SECONDS, SLEEP_TIME_MILLISECONDS) !== FALSE) {
if (in_array($my_socket, $streams)) {
while (@socket_recv($my_socket, $data, 8192, 0)) {
echo $data;
}
$lastAccess = time();
} else {
if (time()-$lastAccess > LAST_ACCESS_TIMEOUT) {
break;
}
}
// ...
$streams = array($my_socket/*, ... */);
}

?>

The solution was simple, but quite hard to find because socket_recv is not documented. socket_recv returns FALSE if there is no data and 0 if the socket is widowed (disconnected by remote side). So I had just to check return value of socket_recv. The problem now sounds stupid, but I've spend some time to find it out.
I hope this will save some of somebody's hair ;)
up
11
ss-130 at yandex dot ru
11 years ago
<?php
$er
= error_reporting(0);
$bytes = socket_recv($socket,$buffer,1,MSG_WAITALL);
error_reporting($er);

// MEGA BUG HERE
// this statuses are wrong and swapped, closed socket must be with "FALSE"
// but in fact he swap the values:
// http://php.net/manual/en/function.socket-recv.php
//
if($bytes===false){ // no data available, socket not closed
echo 'WS_READ_ERR1: '.socket_strerror(socket_last_error($socket)).PHP_EOL;
// print when no data available:
// WS_READ_ERR1: Resource temporarily unavailable
continue;
}else if(
$bytes===0){ // socket closed
echo 'WS_READ_ERR2: '.socket_strerror(socket_last_error($socket)).PHP_EOL;
// print when socket closed:
// WS_READ_ERR2: Success
$process->close();
}

?>
up
7
m_lajos at hotmail dot com
10 years ago
Workaround for the missing MSG_DONTWAIT flag according to the bug report page:

<?php if(!defined('MSG_DONTWAIT')) define('MSG_DONTWAIT', 0x40); ?>
up
2
rathamahata at rathamahata dot net
19 years ago
It looks like that mysterious flags are just the recv(2) flags passed to your OS syscall and nothing more...

ext/sockets/sockets.c:PHP_FUNCTION(socket_recv)
...
if ((retval = recv(php_sock->bsd_socket, recv_buf, len, flags)) < 1) {
efree(recv_buf);
...

for linux you can type `man 2 recv' and you will see complete description of thouse flags.

Sergey S. Kosrtyliov <rathamahata@rathamahata.net>
http://www.rathamahata.net/
up
1
bastiaan at [no-spam] megabass dot nl
20 years ago
in case you want to empty/unset $buffer, but failing to do so, try using 0 as flag.
PHP_NORMAL_READ and PHP_BINARY_READ respectively hold 1 and 2 as value.
up
1
davide dot renzi at gmail dot com
12 years ago
In PHP version 5.* there is a bug: MSG_DONTWAIT flag is not defined (see https://bugs.php.net/bug.php?id=48326)
up
2
lexkrstn at gmail dot com
6 years ago
It seems like the flags are just passed to the underlying recv() function of your OS, hence there no MSG_DONTWAIT flag on Windows and you should not define it yourself in that case, it just won't work.
up
-1
Anonymous
3 years ago
<?php

namespace Safe;

use
Safe\Exceptions\SocketsException;

/**
* After the socket socket has been created
* using socket_create, bound to a name with
* socket_bind, and told to listen for connections
* with socket_listen, this function will accept
* incoming connections on that socket. Once a successful connection
* is made, a new socket resource is returned, which may be used
* for communication. If there are multiple connections queued on
* the socket, the first will be used. If there are no pending
* connections, socket_accept will block until
* a connection becomes present. If socket
* has been made non-blocking using
* socket_set_blocking or
* socket_set_nonblock, FALSE will be returned.
*
* The socket resource returned by
* socket_accept may not be used to accept new
* connections. The original listening socket
* socket, however, remains open and may be
* reused.
*
* @param resource $socket A valid socket resource created with socket_create.
* @return resource Returns a new socket resource on success. The actual
* error code can be retrieved by calling
* socket_last_error. This error code may be passed to
* socket_strerror to get a textual explanation of the
* error.
* @throws SocketsException
*
*/
function socket_accept($socket)
{
error_clear_last();
$result = \socket_accept($socket);
if (
$result === false) {
throw
SocketsException::createFromPhpError();
}
return
$result;
}

/**
* Create a Socket resource, and bind it to the provided AddrInfo resource. The return
* value of this function may be used with socket_listen.
*
* @param resource $addr Resource created from socket_addrinfo_lookup.
* @return resource Returns a Socket resource on success.
* @throws SocketsException
*
*/
function socket_addrinfo_bind($addr)
{
error_clear_last();
$result = \socket_addrinfo_bind($addr);
if (
$result === null) {
throw
SocketsException::createFromPhpError();
}
return
$result;
}
up
0
Anonymous
19 years ago
I'm glad that Bastion left the above post about the mysterious int flag. He just helped to fix a problem that I've spent six hours on. Here's my code:

for($ct=1; $ct<=$numrecs; $ct++) {
$rec = "";
$nr=socket_recv($fp,$rec,77,0);
print "Rec # $ct -->";
print "$rec";
print "<br>";
}

The code is pretty simple, it just loops through all my records and prints them out. All records are 77 bytes and all end with a period. The first 36 records print perfectly then at 37 things go bad. The records start to get offset. The last few characters of the 37th record end up printing on the 38th record. The data on the sending side was perfect, so I knew that the problem was with socked_recv.

After reading the above post I tried changing the int flag. Changing the flag to 2 worked:
$nr=socket_recv($fp,$rec,77,2);

Now everything lines up perfectly. I had always left int flag as 0 since it's undocumented.

Martin K.
up
-2
engine at [NO SPAM] illusiononly dot com
20 years ago
To read from socket both on linux and windows OS having flash as a client I use function bellow. $length is the size of a chunk, not the max length to read. It will continue reading until EOL char occures or client disconnects (or in case of error), so it works for bigger packets as well.

function read($descriptor, $length = 1024) {
$this->method = "read";
if(!$client){
echo("No valid socket descriptor !\n");
return false;
}
$read ='';
while(($flag=socket_recv($descriptor, $buf, $length,0))>0){
$asc=ord(substr($buf, -1));
if ($asc==0) {
$read.=substr($buf,0,-1);
break;
}else{
$read.=$buf;
}
}
if ($flag<0){
//error
return false;
}elseif ($flag==0){
//Client disconnected
return false;
}else{
return $read;
}

}
up
-2
Anonymous
19 years ago
My last post was incorrect. The int flag set to 2 apparently reset the file position pointer so what I was reading was the first record repeatedly.

My workaroud ended up being the following:

for($ct=1; $ct<=$numrecs; $ct++) {
$rec = "";
$nr=socket_recv($fp,$rec,76,0);

//grab the extra bytes.
$terminator = "";
while ($terminator != ".") {
$nr=socket_recv($fp,$terminator,1,0);
}

$custarray[]=substr($rec,0,76);
}

Martin K.
up
-3
cottton at i-stats dot net
10 years ago
socket_recv()
returns FALSE if client returned no data
returns 0 (zero) if client disconnected

also (asuming case socket_select() "gave" us a "changed" socket):
if
socket_recv() returned FALSE
and no bytes were received
then
client "crashed" (call it disconnected).

else if
socket_recv() returned 0 (zero)
and no bytes were received
then
client "normaly" disconnected.

Im pretty sure -- 99.99%.
Example:
<?php
function receive($socket)
{
// !
// on all following cases we assume that
// socket_select() returned the current socket as "changed"
// !

$timeout = 3; // set your timeout

/* important */
$socket_recv_return_values['no_data_received'] = false;
$socket_recv_return_values['client_disconnected'] = 0;

$start = time();
$received_data = null;
$received_bytes = null;
socket_set_nonblock($socket);
socket_clear_error();
while(
(
$t_out=((time()-$start) >= $timeout)) === false
and ($read=@socket_recv($socket, $buf, 4096, 0)) >= 1
){
$received_data = (isset($received_data)) ? $received_data . $buf : $buf;
$received_bytes = (isset($received_bytes)) ? $received_bytes + $read : $read;
}
$last_error = socket_last_error($socket);
socket_set_block($socket);

if(
$t_out === true){
throw new
Exception(
'timeout after ' . ((!$received_bytes) ? 0 : $received_bytes) . ' bytes',
0 // your eCode here
);
}
elseif(
$last_error !== false and $last_error !== 0){
throw new
Exception(
socket_strerror($last_error),
$last_error
);
}
else{
if(
$read === $socket_recv_return_values['no_data_received']){
// client returned NO DATA
// but we were in a loop and could have got some data before:
if($received_bytes < 1){
// client is connected but sent NO DATA ?
// no:
// in this case the client must be "crashed" because -
// it is not possible to "send no data" (zero bytes)
// socket_select() now returns this socket as "changed" "forever"
throw new Exception(
'client crashed',
0 // your eCode here
);
}else{
// client returned DATA
return $received_data;
}
}
elseif(
$read === $socket_recv_return_values['client_disconnected']){
// client disconnected
if($received_bytes < 1){
// client disconnected before/without sending any bytes
throw new Exception(
'client disconnected',
0 // your eCode here
);
}
else{
// *this value* ^= $socket_recv_return_values['client_disconnected']
//
// client disconnected AFTER sending data (we were in a loop!)
// socket_select() will return this socket "forever" as "changed" and -
// socket_recv() will return *this value* "forever".
// we will be "back" again "very soon" to see:
// socket_recv() returns *this value* AND no bytes received
// which results in disconnect-exception above
return $received_data;
}
}
}
}
?>