PHP Conference Japan 2024

socket_read

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

socket_read從 Socket 讀取最大長度的位元組

說明

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

函式 socket_read()Socket 實例 socket 讀取資料,該實例由 socket_create()socket_accept() 函式建立。

參數

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 迴圈中
(範例案例,接收少量位元組 - 剛好足夠 1 次呼叫,但您使用迴圈以確保您接收了所有資料)
如果您使用
<? socket_set_block($socket); ?>
您將獲得
迴圈中的第一次呼叫:資料
迴圈中的第二次呼叫:如果不再有資料或 "另一端" 發生任何事情,則會永遠封鎖

所以,您當然會想使用
<? socket_set_nonblock($socket); ?>
您將獲得
迴圈中的第一次呼叫:資料
迴圈中的第二次呼叫:socket_read() 傳回 FALSE (bool),而 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 以偵測連線關閉都可能是明智之舉,因為這個「錯誤」(?) 可能
會被修正...
be fixed...
2
d dot bergloev at gmail dot com
4 年前
在大多數情況下,二進位讀取是讀取資料的正確方式,而「正常讀取」是一種奇怪且懶惰的 PHP 內建模式,主要用於處理終端資料。

如果您想使用二進位讀取來追蹤已關閉的連線,正確的方法不是像某些人建議的那樣從二進位切換到「正常」。正確的方法是建立一些測試情境,看看 PHP 如何處理特定情況。

以下是快速測試顯示的結果,當使用非阻塞 Socket 時。

<?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) {
// 非阻塞式 Socket 沒有任何資料可讀,稍後再試...

} else {
// 連線很可能已關閉,特別是當 $code 為 '0' 時
}

} else {
// 處理資料
}
?>

還有更多錯誤需要考慮,但這會讓你開始。
3
michi at tr51 dot org
20 年前
如果你想在連線到 Flash 客戶端 (v. 6.0 r81) 的 Linux 系統上執行 "socket_read",你必須傳送一個字串到連線的埠

<?php

... // 初始化通訊

$string = "準備好取得/傳送資料\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 回應訊息或接收第一個傳入訊息的方式。

請注意,如果伺服器離線,你會有一個 Socket 資源,但在嘗試 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_* 函式中,沒有辦法檢查 Socket 中是否還有超過 $length 位元組的資料 (如 stream_get_meta_data()['unread_bytes'])。
因此,你需要選擇你偏好的最大 $length (例如 133693415:) 或使用非阻塞模式 (用於接收非常大的資料)。
0
t33th4n at gmail dot com
15 年前
我不知道是否在任何地方明確聲明,但這裡是用 socket_read 函式測試時,檢測 Socket 連線中止/關閉的原始碼

<?php
$buf
= @socket_read($routes[$i][$connectionid]['tunnelsrc'], $buffer_size);
if (
$buf === '')
{
$routes[$i][$connectionid]['disconnected']='來源端連線中止';
}
?>

($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 的惱人問題的方法,它會將每個字元作為一個字串傳送,就是檢查回應是否為 "\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
'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);
?>
0
Niels laukens
18 年前
這段有點令人困惑

socket_read() 在成功時會以字串形式傳回資料,在錯誤時會傳回 FALSE(包括遠端主機已關閉連線的情況)。錯誤代碼可以使用 socket_last_error() 擷取。此代碼可以傳遞給 socket_strerror() 以取得錯誤的文字表示形式。
注意:當沒有更多資料可讀時,socket_read() 會傳回零長度的字串 ("")。

我的測試(在 PHP 5.1.4 上)顯示,當你在關閉的 socket 上使用 socket_read() 時,使用 PHP_NORMAL_READ 會傳回 FALSE,但在 PHP_BINARY_READ 中讀取時會傳回 ""。
0
schst at php-tools dot de
21 年前
您可以在 http://www.php-tools.de 下載通用伺服器類別
這個類別會接受從中讀取的 socket 資料,並將其傳遞給回呼函式。此外,還包含連線處理的方法。
-1
Arvy
6 年前
在非阻塞 socket 上,如果未收到任何資料,即使客戶端斷開連線,該函式也會傳回 ""。

如果您想檢查客戶端是否斷開連線,請使用 ===。

$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
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,您必須使用 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 年前
在 win32 上開發 PHP 的開發人員,請在使用 PHP_NORMAL_READ 選項之前查看此錯誤報告

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

簡而言之,使用 PHP_NORMAL_READ 會使你每次呼叫 socket_read() 都返回一個空的緩衝區。
To Top