php stream_select 以及其他流函数的用法

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的文档