PHP Conference Japan 2024

socket_read

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

socket_read从套接字读取最多 length 字节

描述

socket_read(Socket $socket, int $length, int $mode = PHP_BINARY_READ): string|false

函数 socket_read() 从由 socket_create()socket_accept() 函数创建的 Socket 实例 socket 中读取。

参数

socket

使用 socket_create()socket_accept() 创建的 Socket 实例。

length

读取的字节最大数量由 length 参数指定。否则,您可以使用 \r\n\0 来结束读取(取决于 mode 参数,请参见下文)。

mode

可选的 mode 参数是一个命名常量

返回值

socket_read() 在成功时返回数据作为字符串,或在错误时返回 false(包括远程主机已关闭连接的情况)。可以使用 socket_last_error() 获取错误代码。此代码可以传递给 socket_strerror() 以获取错误的文本表示形式。

注意:

当没有更多数据可读时,socket_read() 返回一个零长度字符串("")。

变更日志

版本 描述
8.0.0 socket 现在是一个 Socket 实例;之前,它是一个 resource

参见

添加注释

用户贡献的注释 21 条注释

31
cottton at i-stats dot net
10 年前
引用
"注意
socket_read() 在没有更多数据可读时返回零长度字符串("")。

这是错误的!

在 while 循环中
(例如,接收少量字节 - 仅够进行一次调用,但您使用循环以确保已接收所有数据)
如果您使用
<? socket_set_block($socket); ?>
您将获得
循环中的第一次调用:数据
循环中的第二次调用:如果不再有数据或“另一端”发生任何事情,则永远阻塞。

所以当然您想使用
<? socket_set_nonblock($socket); ?>
您将获得
循环中的第一次调用:数据
循环中的第二次调用:socket_read() 返回 FALSE(布尔值),而 socket_last_error() 给您一个 SOCKET_EWOULDBLOCK (http://de1.php.net/manual/de/sockets.constants.php)

我从未从 socket_read 中获得过空字符串。
我一个星期左右一直在“处理”这个问题(错误?)。

您最好使用 socket_recv() 代替。
(祝你好运)
3
匿名
22 年前
Windows telnet 每次发送/接收一个字符。尝试在 socket_read 的末尾添加 PHP_NORMAL_READ,这可能会有所帮助。
6
nuitari-php at nuitari dot net
17 年前
PHP_NORMAL_READ - 读取在 \n 或 \r 处停止。

这似乎是字面意思。
如果有 \r,那么它将停止读取,即使紧随其后的是 \n。您必须再次调用读取才能去除 \n。
4
dotpointer
17 年前
PHP 5.2.0 / Win32 / Apache 1.3 - 似乎...

PHP_BINARY_READ - 工作正常,但返回 '',而不是 FALSE...
- 正在阻塞,直到收到数据或连接关闭
- 传递 \r\n 等。
- 在有数据时返回数据,在连接关闭时返回 ''
- 您可以通过检查 ''(而不是手册中提到的 FALSE)来检测连接关闭

PHP_NORMAL_READ - 工作效果不佳...
- 非阻塞
- 不传递 \r\n 等。
- 在无数据时返回 false,在连接关闭时返回 false :(
- (这里无法检测连接关闭...?)
- (这是个错误吗?http://bugs.php.net/bug.php?id=21880 )
- (这是个错误吗?http://bugs.php.net/bug.php?id=21197 )
- (实际上根本无法从此选项获取数据...)

PHP_BINARY_READ 似乎是“正确的方法”
现在。检查 '' 和 false 以检测关闭
连接可能很明智,因为这个“错误”(?)可能会
被修复...
2
d dot bergloev at gmail dot com
4 年前
在大多数情况下,二进制读取是读取数据的正确方法,而“普通读取”是一种奇怪且懒惰的 PHP 内置模式,主要适用于终端数据。

如果您想使用二进制读取跟踪关闭的连接,正确的方法不是像某些人建议的那样从二进制切换到“普通”。正确的方法是创建一些测试场景,并查看 PHP 如何处理特定情况。

以下是在使用非阻塞套接字时快速测试显示的内容。

<?php
$input
= socket_read($socket, 1024);

// 在大多数情况下,错误会产生一个空字符串而不是 FALSE
if ($input === FALSE || strcmp($input, '') == 0) {
$code = socket_last_error($socket);

// 你必须清除错误,否则它在下次读取时不会改变
socket_clear_error($socket);

if (
$code == SOCKET_EAGAIN) {
// 从非阻塞套接字中没有可读内容,稍后再试...

} else {
// 连接很可能已关闭,尤其是在 $code 为 '0' 时
}

} else {
// 处理数据
}
?>

还有更多错误需要考虑,但这可以帮助你入门。
3
michi at tr51 dot org
20 年前
如果你想在连接到 Flash 客户端(v. 6.0 r81)的 Linux 系统上执行“socket_read”,则必须向连接的端口发送一个字符串。

<?php

... // 初始化通信

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

// 现在你可以读取了...
$line = trim(socket_read($socket, MAXLINE));

...
// 做一些事情,最后关闭连接
?>
1
berniev
7 年前
从文档或注释中看不出来...
PHP_NORMAL_MODE 与 PHP_BINARY 的区别在于,前者无论 socket_set_nonblock 如何都会阻塞,后者则会尊重阻塞和非阻塞。
1
Anonymous
16 年前
在非阻塞连接上,它可能不会返回请求的完整长度。
0
dhaubert dot ti at gmail dot com
9 年前
一种等待套接字消息响应或获取第一个传入消息的方法。

请注意,如果服务器离线,你会有一个套接字资源,但在尝试 socket_read() 时,它会给出警告消息,这会导致你的硬盘驱动器因日志记录而快速填满。

上面的示例尝试最多读取 3 次消息,每次读取之间睡眠 3 秒。
<?php
function waitResponse($response = "") {
$status = "";
$tries = 3;
$counter = 0;
while (
$status == $response) {
$status = socket_read($socket, 1024);
if(!
$status){
if(
$counter >= $tries){
break;
}else{
$counter++;
sleep(3);
}
}
}
return
$response;
}
0
eng.mrkto.com
13 年前
看起来在阻塞模式下的 socket_* 函数中,没有办法检查套接字中是否还有超过 $length 字节的数据可用(例如 stream_get_meta_data()['unread_bytes'])。
所以你需要选择你喜欢的最大 $length(例如 133693415:)或使用非阻塞模式(对于真正的大数据接收)。
0
t33th4n at gmail dot com
15 年前
我不知道是否在任何地方都明确说明了这一点,但这是用于检测使用 socket_read 函数测试套接字的连接中断/关闭的源代码。

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

($buf === '')是关键 :)

我正在使用 mcrypt 制作一个加密隧道脚本,并且很恼火,因为我无法从任何一方检测到连接中断。
0
tech [{at}] swatcash [{dot}] com
16 年前
搞砸了我之前那个的结尾。此处为更正版本。

一个简单的解决方法,可以在非阻塞模式下使用正常读取,如下所示。

$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);
}
}
0
nad0r1 at hush dot ai
17 年前
另一种解决使用 telnet 时遇到的恼人问题的办法是,telnet 将每个字符都作为字符串发送,可以通过检查响应是否为“\r\n”来解决,这是用户按下 Enter 键时 telnet 发送的字符串。

这是一个示例。
<?php
error_reporting
(E_ALL);

/* 允许脚本保持运行,等待连接。 */
set_time_limit(0);

/* 打开隐式输出刷新,以便我们看到接收到的内容
* 并在其出现时显示。 */
ob_implicit_flush();

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

if ((
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
echo
"socket_create() 失败:原因: " . socket_strerror(socket_last_error()) . "\n";
}

if (
socket_bind($sock, $address, $port) === false) {
echo
"socket_bind() 失败:原因: " . socket_strerror(socket_last_error($sock)) . "\n";
}
else
echo
'套接字 ' . $address . ':' . $port . " 已打开\n";

if (
socket_listen($sock, 5) === false) {
echo
"socket_listen() 失败:原因: " . socket_strerror(socket_last_error($sock)) . "\n";
}
else
echo
"正在监听新的客户端..\n";

$client_id = 0;
do {
if ((
$msgsock = socket_accept($sock)) === false) {
echo
"socket_accept() 失败:原因: " . socket_strerror(socket_last_error($sock)) . "\n";
break;
}
else {
$client_id += 1;
echo
"客户端 #" .$client_id .": 连接\n";
}

/* 发送说明。 */
$msg = "\n欢迎使用 PHP 测试服务器。 \n" .
"要退出,请输入 'quit'。 要关闭服务器,请输入 'shutdown'。\n";
socket_write($msgsock, $msg, strlen($msg));
$cur_buf = '';
do {
if (
false === ($buf = socket_read($msgsock, 2048))) {
echo
"socket_read() 失败:原因: " . socket_strerror(socket_last_error($msgsock)) . "\n";
break
2;
}
if (
$buf == "\r\n") {
if (
$cur_buf == 'quit') {
echo
'客户端 #' .$client_id .': 断开连接' . "\n";
break;
}
if (
$cur_buf == 'shutdown') {
socket_close($msgsock);
break
2;
}
//else {
$talkback = "未知命令: " . str_replace("\r\n", '\r\n', $cur_buf) ."\n";
socket_write($msgsock, $talkback, strlen($talkback));
// }
echo '客户端 #' .$client_id .': ' . $cur_buf . "\n";
$cur_buf = '';
}
else
$cur_buf .= $buf;
} while (
true);
socket_close($msgsock);
} while (
true);

socket_close($sock);
?>
0
Niels laukens
18 年前
这段话令人困惑

socket_read() 在成功时将数据作为字符串返回,在错误时返回 FALSE(包括远程主机已关闭连接的情况)。可以使用 socket_last_error() 获取错误代码。此代码可以传递给 socket_strerror() 以获取错误的文本表示形式。
注意:当没有更多数据要读取时,socket_read() 会返回一个零长度字符串("")。

我的测试(在 PHP 5.1.4 上)表明,当您在关闭的套接字上使用 socket_read() 时,它在使用 PHP_NORMAL_READ 时会返回 FALSE,但在使用 PHP_BINARY_READ 读取时会返回 ""。
0
schst at php-tools dot de
21 年前
您可以在 http://www.php-tools.de 下载一个通用的服务器类
此类将接受套接字读取的数据并将其传递给回调函数。此外,还包含连接处理方法。
-1
Arvy
6 年前
在非阻塞套接字上,如果未接收数据,即使客户端断开连接,该函数也将返回 ""。

如果要检查客户端是否断开连接,请使用 ===。

$ret=socket_read($socket);

$ret=="" : 已连接但未接收数据或客户端已断开连接
$ret==="" : 客户端已断开连接
-1
sahel dot nuri at outlook dot com
7 年前
这个小而重要的信息可以节省您的时间。
如果您想识别客户端在没有任何消息的情况下断开连接,则必须选择正确的标志。
因为,如果您使用标志 PHP_BINARY_READ
<?php
function read($sock){
while(
$buf = @socket_read($sock, 1024 [, PHP_BINARY_READ ]))
if(
$buf = trim($buf))
break;

return
$buf;
}
?>
并且用户断开连接,该函数将返回一个空字符串。
但如果您使用标志 PHP_NORMAL_READ
<?php
function read($sock){
while(
$buf = @socket_read($sock, 1024, PHP_NORMAL_READ))
if(
$buf = trim($buf))
break;

return
$buf;
}
?>
该函数将返回 false。

希望这可以帮到您。我花了 1 个小时的时间来解决这个问题。
-1
Bill Kuker
19 年前
只是注意在我的系统上,长度似乎有一个未公开记录的上限为 65536。我当时很懒,没有在 while 循环中读取,直到我将其指向实际数据;)
-2
ein at anti-logic dot com
17 年前
检测关闭连接的正确方法是检查 socket_last_error。

对等方重置连接为 104(可以使用 socket_strerror 或暂时不要抑制错误以找出这些错误),所以。

while($buffer=@socket_read($sock,512,PHP_NORMAL_READ)){
echo $buffer;
}
if(socket_last_error($sock) == 104) {
echo "连接已关闭";
}
-1
magicking89 at hotmail dot com
21 年前
如果您想使用非阻塞套接字,则必须使用 socket_last_error

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

如果您使用它,您的脚本将不会占用所有内存
-4
jgbustos at gmail dot com
17 年前
PHP 在 win32 上的开发人员,在使用 PHP_NORMAL_READ 选项之前,请查看此错误报告

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

简而言之,使用 PHP_NORMAL_READ 将使您对 socket_read() 的调用每次都返回一个空缓冲区。
To Top