如果您在 UDP 套接字上使用 socket_recvfrom 并将其与 MSG_DONTWAIT 标志组合使用,如果没有任何内容可读,它将引发 PHP 警告。据我所知,除了使用 @ 抑制它之外,没有办法解决该警告(即,您无法在调用 socket_recvfrom 之前检查是否有数据)。
(PHP 4 >= 4.1.0, PHP 5, PHP 7, PHP 8)
socket_recvfrom — 从套接字接收数据,无论它是否面向连接
$socket
,&$data
,$length
,$flags
,&$address
,&$port
= null
函数 socket_recvfrom() 使用 socket
从端口 port
上的 address
中接收 data
中的 length
字节数据(如果套接字不是 AF_UNIX
类型)。socket_recvfrom() 可用于从已连接和未连接的套接字收集数据。此外,可以指定一个或多个标志来修改函数的行为。
address
和 port
必须通过引用传递。如果套接字不是面向连接的,address
将被设置为远程主机的互联网协议地址或 UNIX 套接字的路径。如果套接字是面向连接的,address
为 null
。此外,在非连接 AF_INET
或 AF_INET6
套接字的情况下,port
将包含远程主机的端口。
注意: 此函数是二进制安全的。
socket
socket
必须是先前由 socket_create() 创建的 Socket 实例。
data
接收到的数据将被提取到使用 data
指定的变量中。
length
最多将从远程主机提取 length
字节。
flags
flags
的值可以是以下标志的任何组合,使用二进制 OR (|
) 运算符连接。
标志 | 描述 |
---|---|
MSG_OOB |
处理带外数据。 |
MSG_PEEK |
从接收队列的开头接收数据,但不将其从队列中删除。 |
MSG_WAITALL |
阻塞,直到至少接收了 length 个字节。但是,如果捕获到信号或远程主机断开连接,函数可能会返回较少的数据。 |
MSG_DONTWAIT |
设置此标志后,即使函数通常会阻塞,它也会返回。 |
address
如果套接字是 AF_UNIX
类型,address
是文件的路径。否则,对于非连接的套接字,address
是远程主机的 IP 地址,或者如果套接字是面向连接的,则为 null
。
port
此参数仅适用于 AF_INET
和 AF_INET6
套接字,并指定接收数据的远程端口。如果套接字是面向连接的,port
将为 null
。
socket_recvfrom() 返回接收到的字节数,或者如果发生错误,则返回 false
。可以通过调用 socket_last_error() 检索实际的错误代码。此错误代码可以传递给 socket_strerror() 以获取错误的文本解释。
示例 #1 socket_recvfrom() 示例
<?php
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_bind($socket, '127.0.0.1', 1223);
$from = '';
$port = 0;
socket_recvfrom($socket, $buf, 12, 0, $from, $port);
echo "Received $buf from remote address $from and remote port $port" . PHP_EOL;
?>
此示例将在 127.0.0.1 的端口 1223 上启动一个 UDP 套接字,并打印从远程主机接收到的最多 12 个字符。
如果您在 UDP 套接字上使用 socket_recvfrom 并将其与 MSG_DONTWAIT 标志组合使用,如果没有任何内容可读,它将引发 PHP 警告。据我所知,除了使用 @ 抑制它之外,没有办法解决该警告(即,您无法在调用 socket_recvfrom 之前检查是否有数据)。
MSG_DONTWAIT 似乎不存在于 Windows 套接字中。但是 socket_set_nonblock() 似乎可以解决问题。
请注意!在某些 PHP 版本中,MSG_DONTWAIT 标志未定义(请参见 https://bugs.php.net/bug.php?id=48326)
我对 socket_recvfrom() 的返回值感到困惑,它在失败时说 -1,但当我这样调用时
if (($len = @socket_recvfrom($sock, $result, 32, 0, $ip, $port)) == -1) {
if ($this->_debug) {
echo "socket_read() failed: " . socket_strerror(socket_last_error()) . "\n";
}
return false;
}
变量 $len = false,当我将缓冲区长度从 32 更改为 4096 时,它就变正确了。
使用 UDP 套接字的 DNS 中继
<?php
while(TRUE) {
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if($socket === FALSE)
{
echo 'Socket_create 失败: '.socket_strerror(socket_last_error())."\n";
}
if(!socket_bind($socketD, "0.0.0.0", 53)) {
socket_close($socketD);
echo 'socket_bind 失败: '.socket_strerror(socket_last_error())."\n";
}
socket_recvfrom($socket,$buf,65535,0,$clientIP,$clientPort);
$stz = bin2hex($buf);
$tx = "";
for($i=0;$i<(strlen($stz)-26-10)/2;$i++)
{
$e = "00";
$e[0] = $stz[$i*2+26];
$e[1] = $stz[$i*2+27];
$f = hexdec($e);
if($f > 0 && $f < 32) $tx .= "."; else
$tx .= sprintf("%c",$f);
}
echo "$clientIP <".$tx.">\n";
$fp = fsockopen("udp://72.174.110.4",53,$errno,$errstr);
if (!$fp)
{
echo "错误: $errno - $errstr<br />\n";
}
else
{
fwrite($fp,$buf);
$ret = $buf;
$ret = fread($fp,667);
fclose($fp);
}
}
socket_send($socket,$ret,667,0);
}
?>