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 notes

cottton at i-stats dot net
9 年前
引用
"注意
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()。
(祝您好运)
匿名
21 年前
Windows telnet 一次发送/接收一个字符。尝试将 PHP_NORMAL_READ 添加到 socket_read 的末尾,这可能会有所帮助。
nuitari-php at nuitari dot net
17 年前
PHP_NORMAL_READ - 读取在 \n 或 \r 处停止。

这似乎是字面意思。
如果有 \r,它将停止读取,即使紧随其后的是 \n。您必须再次调用读取才能消除 \n。
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 来检测连接是否关闭
可能很明智,因为这个“错误”(?)可能
会得到修复...
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 {
// 处理数据
}
?>

还有更多需要考虑的错误,但这将帮助您入门。
michi at tr51 dot org
20 年前
如果您想对连接到闪存客户端(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));

...
// 做一些事情,最后关闭连接
?>
berniev
7 年前
从文档或注释中不清楚...
PHP_NORMAL_MODE 与 PHP_BINARY 不同,前者无论 socket_set_nonblock 如何都会阻塞,而后者会尊重阻塞和非阻塞。
Anonymous
16 年前
在非阻塞连接上,它可能无法返回请求的全部长度。
dhaubert dot ti at gmail dot com
8 年前
一种通过套接字等待消息响应或接收第一个传入消息的方法。

请注意,如果服务器离线,您将拥有一个套接字资源,但在尝试 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;
}
eng.mrkto.com
13 年前
在阻塞模式下,socket_* 函数似乎没有办法检查套接字中是否还有超过 $length 字节可用(如 stream_get_meta_data()['unread_bytes'])。
因此,您需要选择您喜欢的最大 $length(例如 133693415:)或使用非阻塞模式(用于真正的大数据接收)。
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 制作一个加密隧道脚本,并且很烦人,因为我无法检测到任何一方的连接中止。
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);
}
}
nad0r1 at hush dot ai
16 年前
另一种方法是绕过 telnet 令人讨厌的事情,它将每个字符作为一个字符串发送,方法是检查响应是否为“\r\n”,这是用户按下回车键时 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
'Socket ' . $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);
?>
Niels laukens
17 年前
这段文字令人困惑

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

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

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

$ret=socket_read($socket);

$ret=="" : 已连接但未收到数据或客户端已断开连接
$ret==="" : 客户端已断开连接
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。

我希望这能帮到您。 我花了一个小时才解决这个问题。
Bill Kuker
19 年前
请注意,在我的系统上,长度似乎有一个未记录的上限为 65536。 我当时很懒,没有在循环中调用 read() 直到指向实际数据 ;)。
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 "连接已关闭";
}
magicking89 at hotmail dot com
20 年前
如果要使用非阻塞套接字,则必须使用 socket_last_error

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

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

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

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