PHP Conference Japan 2024

socket_accept

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

socket_accept接受 socket 上的連線

說明

socket_accept(Socket $socket): Socket|false

在使用 socket_create() 建立 socket socket、使用 socket_bind() 繫結至名稱,以及使用 socket_listen() 告知監聽連線後,此函式將接受該 socket 上的傳入連線。成功建立連線後,將會傳回一個新的 Socket 實例,可用於通訊。如果 socket 上有多個連線排隊,則會使用第一個。如果沒有待處理的連線,socket_accept() 將會封鎖,直到有連線出現。如果使用 socket_set_blocking()socket_set_nonblock()socket 設為非封鎖,則會傳回 false

socket_accept() 傳回的 Socket 實例不得用於接受新的連線。但是,原始監聽 socket socket 仍會保持開啟且可以重複使用。

參數

socket

使用 socket_create() 建立的 Socket 實例。

傳回值

成功時傳回一個新的 Socket 實例,失敗時傳回 false。可以呼叫 socket_last_error() 來擷取實際的錯誤碼。此錯誤碼可以傳遞給 socket_strerror() 來取得錯誤的文字說明。

更新日誌

版本 說明
8.0.0 成功時,此函式現在傳回 Socket 實例;之前,傳回的是 resource

參見

新增註解

使用者提供的註解 5 個註解

24
lars at opdenkamp dot eu
16 年前
如果您想建立一個在每個用戶端連線上分支的守護程序,您會發現 PHP 中存在一個錯誤。子程序在結束時會將 SIGCHLD 傳送給父程序,但父程序在等待 socket_accept (封鎖) 時無法處理訊號。

以下是一段程式碼,可以建立非封鎖的分支伺服器。

#!/usr/bin/php -q
<?php
/**
* 監聽請求並在每次連線時 Fork
*/

$__server_listening = true;

error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
declare(
ticks = 1);

become_daemon();

/* nobody/nogroup,請變更為您的主機上非特權使用者的 uid/gid */
change_identity(65534, 65534);

/* 處理訊號 */
pcntl_signal(SIGTERM, 'sig_handler');
pcntl_signal(SIGINT, 'sig_handler');
pcntl_signal(SIGCHLD, 'sig_handler');

/* 將此變更為您自己的主機/連接埠 */
server_loop("127.0.0.1", 1234);

/**
* 將身分變更為非特權使用者
*/
function change_identity( $uid, $gid )
{
if( !
posix_setgid( $gid ) )
{
print
"無法將 setgid 設定為 " . $gid . "!\n";
exit;
}

if( !
posix_setuid( $uid ) )
{
print
"無法將 setuid 設定為 " . $uid . "!\n";
exit;
}
}

/**
* 建立伺服器 Socket 並監聽連入的客戶端連線
* @param string $address 要監聽的位址
* @param int $port 要監聽的連接埠
*/
function server_loop($address, $port)
{
GLOBAL
$__server_listening;

if((
$sock = socket_create(AF_INET, SOCK_STREAM, 0)) < 0)
{
echo
"建立 Socket 失敗: ".socket_strerror($sock)."\n";
exit();
}

if((
$ret = socket_bind($sock, $address, $port)) < 0)
{
echo
"繫結 Socket 失敗: ".socket_strerror($ret)."\n";
exit();
}

if( (
$ret = socket_listen( $sock, 0 ) ) < 0 )
{
echo
"監聽 Socket 失敗: ".socket_strerror($ret)."\n";
exit();
}

socket_set_nonblock($sock);

echo
"等待客戶端連線\n";

while (
$__server_listening)
{
$connection = @socket_accept($sock);
if (
$connection === false)
{
usleep(100);
}elseif (
$connection > 0)
{
handle_client($sock, $connection);
}else
{
echo
"錯誤: ".socket_strerror($connection);
die;
}
}
}

/**
* 訊號處理函式
*/
function sig_handler($sig)
{
switch(
$sig)
{
case
SIGTERM:
case
SIGINT:
exit();
break;

case
SIGCHLD:
pcntl_waitpid(-1, $status);
break;
}
}

/**
* 處理新的客戶端連線
*/
function handle_client($ssock, $csock)
{
GLOBAL
$__server_listening;

$pid = pcntl_fork();

if (
$pid == -1)
{
/* fork 失敗 */
echo "fork 失敗!\n";
die;
}elseif (
$pid == 0)
{
/* 子程序 */
$__server_listening = false;
socket_close($ssock);
interact($csock);
socket_close($csock);
}else
{
socket_close($csock);
}
}

function
interact($socket)
{
/* 與您的客戶端交談 */
}

/**
* 透過 Fork 並關閉父程序來成為守護程序
*/
function become_daemon()
{
$pid = pcntl_fork();

if (
$pid == -1)
{
/* fork 失敗 */
echo "fork 失敗!\n";
exit();
}elseif (
$pid)
{
/* 關閉父程序 */
exit();
}else
{
/* 子程序成為我們的守護程序 */
posix_setsid();
chdir('/');
umask(0);
return
posix_getpid();

}
}

?>
18
Boylett
16 年前
如果您想要伺服器上有複數個客戶端,您必須使用非阻塞模式。

<?php

$clients
= array();
$socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
socket_bind($socket,'127.0.0.1',$port);
socket_listen($socket);
socket_set_nonblock($socket);

while(
true)
{
if((
$newc = socket_accept($socket)) !== false)
{
echo
"客戶端 $newc 已連線\n";
$clients[] = $newc;
}
}

?>
4
Greg MacLellan
20 年前
此資源傳回的 Socket 將會是非阻塞的,無論監聽 Socket 設定為何。這實際上對所有 FCNTL 修飾符皆為真。
2
renmengyang567 at gmail dot com
5 年前
<說明>
這個案例是 TCP 客戶端與 TCP 伺服器端之間非常簡單的互動

<?php
// 為 simple-tcp-server 建立
$sock = socket_create(AF_INET, SOCK_STREAM, getprotobyname('tcp'));
socket_bind($sock, '127.0.0.1',5000);
socket_listen($sock,1);
$clnt_sock = socket_accept($sock); //阻塞
$st = "hello world ^_^";
socket_write($clnt_sock, $st,strlen($st));
socket_close($clnt_sock);
socket_close($sock);
?>

<?php
//為 simple-tcp-client 建立
$clnt_sock = socket_create(AF_INET, SOCK_STREAM, getprotobyname('tcp'));
socket_connect($clnt_sock, '127.0.0.1', 5000);
$ret= socket_read($clnt_sock, 1024);
print
"來自 simple-tcp-server:".$ret.PHP_EOL;
socket_close($clnt_sock);
?>

<fruit>
來自 simple-tcp-server:hello world ^_^
-1
gmkarl at gmail dot com
17 年前
請注意,當 socket 處於阻塞狀態等待連線時,使用 pcntl_signal 設定的訊號處理函數不會被呼叫;訊號會被靜默吸收,並在建立連線時才呼叫處理函數。
To Top