php stream_select 以及其他流函数的用法
在学习socket的使用学到流,但是一直不明白,这些流函数的执行步骤
1、stream_socket_client函数是和服务器建立连接,然后返回句柄fd。记住这个只是建立tcp连接。
由于并发建立多个连接,建立连接的fd都放在 $sockets数组里面。
假如本例:
请求Client1的socket fd=5555
请求Client2的socket fd=5556
$sockets[0] = 5555
$sockets[1] = 5556
2、stream_select会调用操作系统io函数select来工作,
$read = $write = $sockets;
select的机制:检查$read或者$write存放fd是否可以操作。
比如$read,有数据就把$read的fd对应标志位设置为1,即通知应用层已经有数据可以读了。
3、然后就遍历$read的fd读取数据。
上面就达到了并发请求的目的。
不明白的我打个比方:
1)、目的:从A5555,B5556两地搬砖回来,每个司机搬回来的砖头都是放在指定地方。
2)、连接:你派两个司机fd1,fd2分别开车去获取:
fd1 = stream_socket_client(A5555)
fd2 = stream_socket_client (A5556)
3)、select机制:这时你等待获取砖头,谁的卸载地(缓冲区)有砖头就通知你。
即告诉两个司机,如果在timeout时间内运回砖头,那就把fd对应的标志位设置为1,
即如果司机fd1运回砖头,fd1对应的标志位为1.
注意的是stream_select参数:timeout一般是设置为0,即立即返回。
4)、并行获取结果:
轮训从fd1,fd2的所在的缓冲区获取砖头。
如果不是select机制,我们看看如何处理:
1)、目的:从A5555,B5556两地搬砖回来,每个司机搬回来的砖头都是放在指定地方。
1)、连接:你派两个司机fd1,fd2分别开车去获取:
fd1 = stream_socket_client(A5555)
fd2 = stream_socket_client (A5556)
3)、直接获取结果:
因为没人通知你缓冲区已经有数据,你只能串行执行。
你只能从这两个卸载地(缓冲区)其中之一把砖头搬完再去另外地方搬砖。
比如先在司机fd1的缓冲区搬砖完了,再去司机fd2的缓冲区搬砖。
4) 结果就是串行获取。
只是学习用还是要在实际业务中用?
现在是学习
1、学习的话,我建议把基础知识学得扎实,php的stream_select函数调用系统函数select来实现,你要完全弄明白:
你要完全弄明白:1、了解网络知识:https://blog.csdn.net/hguisu/category_1075597.html 。2、了解系统io:https://blog.csdn.net/hguisu/article/details/7453390 和相关事件机制 https://blog.csdn.net/hguisu/article/details/38638183 ;3、了解socket编程:https://blog.csdn.net/hguisu/article/details/7444092
<?php
include 'common.php';
$hosts = array("5556",'5555');
$timeout = 15;
$status = array();
$sockets = array();
foreach ($hosts as $id => $host) {
$s = stream_socket_client("127.0.0.1:$host", $errno, $errstr, $timeout,
STREAM_CLIENT_ASYNC_CONNECT|STREAM_CLIENT_CONNECT);
if ($s) {
$sockets[$id] = $s;
$status[$id] = "in progress";
} else {
$status[$id] = "failed, $errno $errstr";
}
}
/* Now, wait for the results to come back in */
while (count($sockets)) {
$read = $write = $sockets;
$e=null;
$n = stream_select($read, $write, $e, $timeout);//无法理解该处 是怎么执行,时间有限,我想尽快理解,执行路径
if ($n > 0) {
foreach ($read as $key=>$r) {
$id = array_search($r, $sockets);
$data = fread($r, 8192);dump($key);
if (strlen($data) == 0) {
if ($status[$id] == "in progress") {
$status[$id] = "failed to connect";
}
fclose($r);
unset($sockets[$id]);
} else {
$status[$id] .= $data;
}
}
} else {
/* timed out waiting; assume that all hosts associated
* with $sockets are faulty */
foreach ($sockets as $id => $s) {
$status[$id] = "timed out " . $status[$id];
}
break;
}
exit;
}
foreach ($hosts as $id => $host) {
echo "Host: $host";
echo "Status: " . $status[$id] . "nn";
}
1、stream_socket_client函数是和服务器建立连接,然后返回句柄fd。
我还是不太明白, stream_select($read, $write, $e, $timeout) 第二个参数有什么意义
第二参数在这段代码没有意义,但是如果你的并发http请求是post提交数据,这个$write就有意义,客户端可以同时提交数据并发请求。
具体怎样用呢
<?php
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_USER_NOTICE);
ini_set('display_errors', 1);
$hosts = array("www.baidu.com");
$timeout = 15;
$status = array();
$sockets = array();
foreach ($hosts as $id => $host) {
$s = stream_socket_client("tcp://$host:80", $errno, $errstr, $timeout,
STREAM_CLIENT_ASYNC_CONNECT|STREAM_CLIENT_CONNECT);
// $s = fsockopen($host, 80);
if ($s) {
//fwrite($s, "GET /s?wd=stream_select HTTP/1.0\r\nHost: $host\r\nAccept: */*\r\n\r\n");
$sockets[$id] = $s;
$status[$id] = "in progress";
} else {
$status[$id] = "failed, $errno $errstr";
}
}
/* Now, wait for the results to come back in */
while (count($sockets)) {
$read = $write = $sockets;
$e=null;
//不论在php5.6还是php7.4版本,测试发现只触发$write事件,$read返回一直为空,这个需要从stream_select源码去研究原因。
//目前今天还没有时间去看源码。
$n = stream_select($read, $write, $e,0);
if ($n > 0) {
/* writeable sockets can accept an HTTP request */
foreach ($write as $w) {
if(!is_resource($w))continue;
$id = array_search($w, $sockets);
$host = $hosts[$id];
$path = "/";
$req .= "GET $path HTTP/1.1\r\n";
$req .= "Host: $host\r\n";
$req .= "\r\n";
fwrite($w, $req);
$status[$id] = "waiting for response";
}
/* readable sockets either have data for us, or are failed connection attempts */
foreach ($read as $key=>$r) {
$id = array_search($r, $sockets);
$data = fread($r, 8192);
if (strlen($data) == 0) {
if ($status[$id] == "in progress") {
$status[$id] = "failed to connect";
}
fclose($r);
unset($sockets[$id]);
} else {
$status[$id] .= $data;
}
}
} else {
/* timed out waiting; assume that all hosts associated
* with $sockets are faulty */
foreach ($sockets as $id => $s) {
$status[$id] = "timed out " . $status[$id];
}
break;
}
}
foreach ($hosts as $id => $host) {
echo "Host: $host";
echo "Status: " . $status[$id] . "nn";
}
<?php
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_USER_NOTICE);
ini_set('display_errors', 1);
$hosts = array("www.baidu.com","www.baidu.com");
$timeout = 15;
$status = array();
$sockets = array();
foreach ($hosts as $id => $host) {
$s = stream_socket_client("tcp://$host:80", $errno, $errstr, $timeout,
STREAM_CLIENT_ASYNC_CONNECT|STREAM_CLIENT_CONNECT);
if ($s) {
//建立tcp连接后,写入http请求数据,可以并发请求。
fwrite($s, "GET / HTTP/1.0\r\nHost: $host\r\nAccept: */*\r\n\r\n");
$sockets[$id] = $s;
$status[$id] = "in progress";
} else {
$status[$id] = "failed, $errno $errstr";
}
}
/* Now, wait for the results to come back in */
while (count($sockets)) {
$read = $write = $sockets;
$e=null;
$n = stream_select($read, $write, $e,0);
if ($n > 0) {
/* readable sockets either have data for us, or are failed connection attempts */
foreach ($read as $key=>$r) {
$id = array_search($r, $sockets);
$data = fread($r, 8192);
if (strlen($data) == 0) {
if ($status[$id] == "in progress") {
$status[$id] = "failed to connect";
}
fclose($r);
unset($sockets[$id]);
} else {
$status[$id] .= $data;
}
}
} else {
/* timed out waiting; assume that all hosts associated
* with $sockets are faulty */
foreach ($sockets as $id => $s) {
$status[$id] = "timed out " . $status[$id];
}
break;
}
}
foreach ($hosts as $id => $host) {
echo "Host: $host";
echo "Status: " . $status[$id] . "nn";
echo "==================end==================================";
}
http请求流程:
1) 建立tcp连接,即三次握手,即$s = stream_socket_client
2) 发送http请求数据, 使用fwrite函数:
例如请求百度首页的http get请求格式:
GET / HTTP/1.0\r\n
Host: $host\r\n
Accept: */*\r\n
\r\n
3) 服务器返回结果:
fread函数读取socket
如果实在不明白,可以加我qq 275442185来沟通交流。
既然是流就是说双方都可以同时进行读和写操作。
不像HTTP是单向的读或者写。
不知道这样说能明白不?
https://www.cnblogs.com/dlight/p/biwen.html
你说的我都不明白,我说的是解释代码的运行意思,却给我一堆概念
https://www.php.net/manual/en/function.stream-select.php
这里有很多php的文档