<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[向东博客 专注WEB应用 构架之美 --- 构架之美，在于尽态极妍 | 应用之美，在于药到病除]]></title> 
<link>http://www.jackxiang.com/index.php</link> 
<description><![CDATA[赢在IT，Playin' with IT,Focus on Killer Application,Marketing Meets Technology.]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[向东博客 专注WEB应用 构架之美 --- 构架之美，在于尽态极妍 | 应用之美，在于药到病除]]></copyright>
<item>
<link>http://www.jackxiang.com/post/5560/</link>
<title><![CDATA[[他山之石]PHP扩展之网络socket扩展导读]]></title> 
<author>jack &lt;xdy108@126.com&gt;</author>
<category><![CDATA[Php/Js/Shell/Go]]></category>
<pubDate>Sat, 08 Sep 2012 14:54:09 +0000</pubDate> 
<guid>http://www.jackxiang.com/post/5560/</guid> 
<description>
<![CDATA[ 
	没想到在CSDN上看到一个兄弟写的文章成连载了，于是细看了下，发现对PHP的Socket的编码及扩展实现写得不错，建议看下其扩展写法，很有学习教育意义，再就是对Epoll模型的分析很到位，觉得写得都不错，特摘录下Socket这块的PHP扩展代码分析结合其PHP的Soket服务器写法，原文来自：<br/>http://blog.csdn.net/shagoo/article/details/6647961<br/>http://blog.csdn.net/shagoo/article/details/6396089<br/>《Socket深度探究4PHP（一）》和《Socket深度探究4PHP（二）》 Socket 深度探究 4 PHP （三） 应该是写得很有体系的难得一见的不错的好文章。<br/><br/>我在此仅仅对PHPSocket网络扩展作摘录，如下：<br/>select/poll 的同步模型：属于同步非阻塞 IO 模型，代码如下：<br/><br/>select_server.php<br/>[php] view plaincopy<br/>&lt;?php&nbsp;&nbsp;<br/>/** <br/> * SelectSocketServer Class <br/> * By James.Huang &lt;shagoo#gmail.com&gt; <br/>**/&nbsp;&nbsp;<br/>set_time_limit(0);&nbsp;&nbsp;<br/>class SelectSocketServer&nbsp;&nbsp; <br/>&#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private static $socket;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private static $timeout = 60;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private static $maxconns = 1024;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private static $connections = array();&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;function SelectSocketServer($port)&nbsp;&nbsp; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;global $errno, $errstr;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ($port &lt; 1024) &#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;die(&quot;Port must be a number which bigger than 1024/n&quot;);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$socket = socket_create_listen($port);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!$socket) die(&quot;Listen $port failed&quot;);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;socket_set_nonblock($socket); // 非阻塞&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while (true)&nbsp;&nbsp; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$readfds = array_merge(self::$connections, array($socket));&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$writefds = array();&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 选择一个连接，获取读、写连接通道&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (socket_select($readfds, $writefds, $e = null, $t = self::$timeout))&nbsp;&nbsp; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 如果是当前服务端的监听连接&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (in_array($socket, $readfds)) &#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 接受客户端连接&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$newconn = socket_accept($socket);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$i = (int) $newconn;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$reject = &#039;&#039;;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (count(self::$connections) &gt;= self::$maxconns) &#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$reject = &quot;Server full, Try again later./n&quot;;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 将当前客户端连接放入 socket_select 选择&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self::$connections[$i] = $newconn;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 输入的连接资源缓存容器&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$writefds[$i] = $newconn;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 连接不正常&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ($reject) &#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;socket_write($writefds[$i], $reject);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unset($writefds[$i]);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self::close($i);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125; else &#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;Client $i come./n&quot;;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// remove the listening socket from the clients-with-data array&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$key = array_search($socket, $readfds);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unset($readfds[$key]);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 轮循读通道&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach ($readfds as $rfd) &#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 客户端连接&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$i = (int) $rfd;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 从通道读取&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$line = @socket_read($rfd, 2048, PHP_NORMAL_READ);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ($line === false) &#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 读取不到内容，结束连接&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;Connection closed on socket $i./n&quot;;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self::close($i);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;continue;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$tmp = substr($line, -1);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ($tmp != &quot;/r&quot; &amp;&amp; $tmp != &quot;/n&quot;) &#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 等待更多数据&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;continue;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 处理逻辑&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$line = trim($line);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ($line == &quot;quit&quot;) &#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;Client $i quit./n&quot;;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self::close($i);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ($line) &#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;Client $i &gt;&gt;&quot; . $line . &quot;/n&quot;;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 轮循写通道&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach ($writefds as $wfd) &#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$i = (int) $wfd;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$w = socket_write($wfd, &quot;Welcome Client $i!/n&quot;);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;function close ($i)&nbsp;&nbsp; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;socket_shutdown(self::$connections[$i]);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;socket_close(self::$connections[$i]);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unset(self::$connections[$i]);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;<br/>&#125;&nbsp;&nbsp;<br/>new SelectSocketServer(2000);&nbsp;&nbsp;<br/><br/>select_client.php<br/>[php] view plaincopy<br/>&lt;?php&nbsp;&nbsp;<br/>/** <br/> * SelectSocket Test Client <br/> * By James.Huang &lt;shagoo#gmail.com&gt; <br/>**/&nbsp;&nbsp;<br/>function debug ($msg)&nbsp;&nbsp;<br/>&#123;&nbsp;&nbsp;<br/>//&nbsp;&nbsp;echo $msg;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;error_log($msg, 3, &#039;/tmp/socket.log&#039;);&nbsp;&nbsp;<br/>&#125;&nbsp;&nbsp;<br/>if ($argv[1]) &#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$socket_client = stream_socket_client(&#039;tcp://0.0.0.0:2000&#039;, $errno, $errstr, 30);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>//&nbsp;&nbsp;stream_set_timeout($socket_client, 0, 100000);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;if (!$socket_client) &#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;die(&quot;$errstr ($errno)&quot;);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125; else &#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$msg = trim($argv[1]);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for ($i = 0; $i &lt; 10; $i++) &#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$res = fwrite($socket_client, &quot;$msg($i)/n&quot;);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;usleep(100000);&nbsp;&nbsp;<br/>//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;debug(fread($socket_client, 1024)); // 将产生死锁，因为 fread 在阻塞模式下未读到数据时将等待&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fwrite($socket_client, &quot;quit/n&quot;); // add end token&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;debug(fread($socket_client, 1024));&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fclose($socket_client);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;<br/>&#125;&nbsp;&nbsp;<br/>else &#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$phArr = array();&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;for ($i = 0; $i &lt; 10; $i++) &#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$phArr[$i] = popen(&quot;php &quot;.__FILE__.&quot; &#039;&#123;$i&#125;:test&#039;&quot;, &#039;r&#039;);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;foreach ($phArr as $ph) &#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pclose($ph);&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>//&nbsp;&nbsp;for ($i = 0; $i &lt; 10; $i++) &#123;&nbsp;&nbsp;<br/>//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;system(&quot;php &quot;.__FILE__.&quot; &#039;&#123;$i&#125;:test&#039;&quot;);&nbsp;&nbsp;<br/>//&nbsp;&nbsp;&#125;&nbsp;&nbsp;<br/>&#125;&nbsp;&nbsp;<br/><br/>以上代码的逻辑也很简单，select_server.php 实现了一个类似聊天室的功能，你可以使用 telnet 工具登录上去，和其他用户文字聊天，也可以键入“quit”命令离开；而 select_client.php 则模拟了一个登录用户连续发 10 条信息，然后退出。这里也分析两个问题：<br/><br/>A&gt; 这里如果我们执行 php select_client.php 程序将会同时打开 10 个连接，同时进行模拟登录用户操作；观察服务端打印的数据你会发现服务端确实是在同时处理这些连接，这就是多路复用实现的非阻塞 IO 模型，当然这个模型并没有真正的实现异步，因为最终服务端程序还是要去通道里面读取数据，得到结果后同步返回给客户端。如果这次你也使用 telnet 命令同时打开多个客户端，你会发现服务端可以同时处理这些连接，这就是非阻塞 IO，当然比古老的阻塞 IO 效率要高多了，但是这种模式还是有局限的，继续看下去你就会发现了~<br/><br/>B&gt; 我在 select_server.php 中设置了几个参数，大家可以调整试试：<br/>$timeout ：表示的是 select 的超时时间，这个一般来说不要太短，否则会导致 CPU 负载过高。<br/>$maxconns ：表示的是最大连接数，客户端超过这个数的话，服务器会拒绝接收。这里要提到的一点是，由于 select 是通过句柄来读写的，所以会受到系统默认参数 __FD_SETSIZE 的限制，一般默认值为 1024，修改的话需要重新编译内核；另外通过测试发现 select 模式的性能会随着连接数的增大而线性便差（详情见《Socket深度探究4PHP（二）》），这也就是 select 模式最大的问题所在，所以如果是超高并发服务器建议使用下一种模式。<br/><br/><br/>根据上面这一段来阅读PHP的扩展源代码是相当不错的：<br/>PHP 源码的 ext 目录下面，因此我们我需要先进入 ext/sockets/ 目录，做过 PHP 扩展的同学应该都很熟悉下面的一些文件了，这次我们主要分析的是 php_sockets.h 和 sockets.c 这两个 C 源码文件。<br/><br/>ext/sockets/php_sockets.h<br/><br/>这个头文件很简单，我们主要看一下下面列出的几个重点：<br/><br/>32 行：<br/>[cpp] view plaincopy<br/>#ifdef PHP_WIN32&nbsp;&nbsp;<br/>#include &lt;winsock.h&gt;&nbsp;&nbsp;<br/>#else&nbsp;&nbsp;<br/>#if HAVE_SYS_SOCKET_H&nbsp;&nbsp;<br/>#include &lt;sys/socket.h&gt;&nbsp;&nbsp;<br/>#endif&nbsp;&nbsp;<br/>#endif&nbsp;&nbsp;<br/><br/>以上就是 PHP 对于不同环境 Socket 底层调用的定义了，我们可以看到不管是 Unix 还是 Windows 环境，PHP均调用的是系统标准的 BSD Socket 库。然后我们看下面这个重要的结构体定义：<br/><br/>82 行：<br/>[cpp] view plaincopy<br/>typedef struct &#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;PHP_SOCKET bsd_socket;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;type;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;error;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;blocking;&nbsp;&nbsp;<br/>&#125; php_socket;&nbsp;&nbsp;<br/><br/>这个就是 php socket 的存储结构了，此结构体在以下的代码阅读中将会大量出现，里面的几个字段很容易理解：bsd_socket 就是标准的 socket 类型，type 表示 socket 类型（PF_UNIX/AF_UNIX），error 是错误代码，blocking 则表示是否阻塞。<br/><br/>ext/sockets/sockets.c<br/><br/>这个文件比较长，为了直接切入重点，我们会按照《Socket 深度探索 4 PHP （一） 》中 select_server.php 部分代码来按顺序分析一下在最经典的 select 模式中我们用到的主要方法：<br/><br/>&gt;socket_create_listen<br/><br/>859 行：PHP_FUNCTION(socket_create_listen)<br/>这个函数很简单，初始化 php_sock 并获取 socket 需要监听的端口，然后传入下面的 php_open_listen_sock 函数进行加工，最后调用 ZEND_REGISTER_RESOURCE 宏返回 php_sock。<br/><br/>347行：static int php_open_listen_sock(php_socket **php_sock, int port, int backlog TSRMLS_DC)<br/>此函数基本上就是 socket 的标准初始化过程：socket(...) -&gt; bind(...) -&gt; listen(...)（详见 368 行至 391 行）。<br/>[cpp] view plaincopy<br/>sock-&gt;bsd_socket = socket(PF_INET, SOCK_STREAM, 0);&nbsp;&nbsp;<br/>sock-&gt;blocking = 1;&nbsp;&nbsp;<br/>...&nbsp;&nbsp;<br/>sock-&gt;type = PF_INET;&nbsp;&nbsp;<br/>...&nbsp;&nbsp;<br/>if (bind(sock-&gt;bsd_socket, (struct sockaddr *)&amp;la, sizeof(la)) != 0) &#123;&nbsp;&nbsp;<br/>...&nbsp;&nbsp;<br/>&#125;&nbsp;&nbsp;<br/>if (listen(sock-&gt;bsd_socket, backlog) != 0) &#123;&nbsp;&nbsp;<br/>...&nbsp;&nbsp;<br/>&#125;&nbsp;&nbsp;<br/><br/>&gt;socket_set_nonblock<br/><br/>906 行：PHP_FUNCTION(socket_set_nonblock)<br/>这个函数也很简单，从 ZEND_FETCH_RESOURCE 取出 runtime 中的 php_sock 然后调用 php_set_sock_blocking 函数来设置 sockfd 的阻塞或者非阻塞（此函数可以参考 main/network.c 第 1069 行，我们可以看到 PHP 是使用 fcntl 函数来设置的）。<br/><br/>&gt;socket_select<br/><br/>785 行：PHP_FUNCTION(socket_select)<br/>也是标准的 select 函数调用，过程如下：FD_ZERO(...) -&gt; php_sock_array_to_fd_set(...) -&gt; select(...) -&gt; php_sock_array_from_fd_set(...)，可能比较特殊的就是 php_sock_array_from_fd_set() 和 php_sock_array_from_fd_set() 两个函数，这是由于我们要先把 PHP 的 fd 数组转换成原生 fd 集合，才能调用原生的 select 函数，而最后系统还把 fd 集合重新转回到 PHP 的 fd 数组（具体代码参考 799 行至 851 行）。<br/><br/>&gt;socket_accept<br/><br/>881 行：PHP_FUNCTION(socket_accept)<br/>此函数基本上也就是 socket 原生 accept 函数的包装，具体代码可参考 397 行：php_accept_connect 函数中的逻辑，最后调用 ZEND_REGISTER_RESOURCE 宏返回 new_sock，若失败程序会清理使用的 out_socket 资源。<br/><br/>&gt;socket_write<br/><br/>986 行：PHP_FUNCTION(socket_write)<br/>按照以上的思路看这个函数也非常简单，详见 986 行，唯一值得注意的是对于不同操作系统调用的函数有点不同，代码（见 1004 行）如下：<br/>[cpp] view plaincopy<br/>#ifndef PHP_WIN32&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;retval = write(php_sock-&gt;bsd_socket, str, MIN(length, str_len));&nbsp;&nbsp;<br/>#else&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;retval = send(php_sock-&gt;bsd_socket, str, min(length, str_len), 0);&nbsp;&nbsp;<br/>#endif&nbsp;&nbsp;<br/><br/>&gt;socket_read<br/><br/>1021 行：PHP_FUNCTION(socket_read)<br/>此函数是用于接受 socket 的数据，调用的原生函数是 recv()，不过这里需要注意的是 PHP 为我们提供两种获取方式：<br/>1、PHP_NORMAL_READ<br/>按行读取，具体代码见 419 行：php_read 函数的逻辑，我们注意到此函数在非阻塞模式下会立即返回，否则将会读取直至遇到 &#92;n 或者 &#92;r 字符。<br/>2、PHP_BINARY_READ<br/>代码见 1045 行：retval = recv(php_sock-&gt;bsd_socket, tmpbuf, length, 0); 相当原生和“环保”。<br/>最后，如果返回值为 -1 则会进行一些错误记录和系统清理工作。<br/><br/>&gt;socket_close<br/><br/>970 行：PHP_FUNCTION(socket_close)<br/>清理 socket 运行时所用的资源。<br/><br/>&gt;socket_shutdown<br/><br/>1968 行：PHP_FUNCTION(socket_shutdown)<br/>调用原生 shutdown 函数来关闭 socket。<br/><br/>分析下来，PHP 的 socket 模块中绝大部分的代码还是使用的是系统标准的原生 socket 库，其中唯一有可能造成性能隐患的就是 select 中 PHP 的 fd 数组与原生 fd 集合转换，至于其他的一些简单的数据拷贝基本对效率不会有什么影响。总的来说，PHP 的 socket 模块应该效率还是比较高的，但是在使用的时候还是需要注意到一些资源的及时释放，因为毕竟是 Daemon 程序，需要不断运行的，而且 PHP 的数据结构是很占内存（是原生 C 的 4 倍左右）的。<br/><br/>node.js<br/><br/>最后，我们看看现在很流行的 Node.js（http://nodejs.org/），它采用了 JavaScript 的语言引擎，语法非常的简洁，对闭包的完美支持让它特别适合做异步 IO 的代码编写，下面是一个最简单的 HTTP Server，只用仅仅六行代码：<br/>[javascript] view plaincopy<br/>var http = require(&#039;http&#039;);&nbsp;&nbsp;<br/>http.createServer(function (req, res) &#123;&nbsp;&nbsp;<br/>&nbsp;&nbsp;res.writeHead(200, &#123;&#039;Content-Type&#039;: &#039;text/plain&#039;&#125;);&nbsp;&nbsp;<br/>&nbsp;&nbsp;res.end(&#039;Hello World&#92;n&#039;);&nbsp;&nbsp;<br/>&#125;).listen(8000, &quot;127.0.0.1&quot;);&nbsp;&nbsp;<br/>console.log(&#039;Server running at http://127.0.0.1:8000/&#039;);&nbsp;&nbsp;<br/><br/>运行起来感受一下，有没有惊艳的感觉啊？事实上用它来写一些简单的服务确实很不错，有兴趣的朋友可以多研究研究（中文社区：http://cnodejs.org/），它有 8000 行 C++ 代码，2000 行 javascript 代码，使用 Google 的 V8 引擎（和 Mongodb 一样），相当的很小巧精悍。下面是我在使用过程总结出中几个要点，大家可以参考：<br/><br/>1、使用 V8 引擎（和 Mongodb 一样），内置 JSON，代码简洁，使用方便。<br/>2、使用单线程非阻塞 I/O 中的 select 方式，比较稳定（但是对于超高并发有点力不从心）。<br/>3、一些第三方应用接口不是很稳定，比如 Mongodb 的接口，并发 200 出现卡死现象，Mysql 接口也比 fast-cgi 差很多。<br/>4、注意使用 try&#123;...&#125;catch&#123;...&#125; 来捕获错误；使用 process.on(&#039;uncaughtException&#039;, function(err)&#123;...&#125;); 来处理未捕获的错误，否则出错会导致整个服务退出。<br/><br/>当然，Node.js 还在不断的更新发展中，虽然目前我在公司的服务架构中还不敢使用它，我还是很希望它能够迅速成长起来，这样子我们开发服务中间件的时候，就会多出一个很棒的选项啦~
]]>
</description>
</item><item>
<link>http://www.jackxiang.com/post/5560/#blogcomment63938</link>
<title><![CDATA[[评论] [他山之石]PHP扩展之网络socket扩展导读]]></title> 
<author>小红帽 &lt;aliang032@163.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Sat, 12 Apr 2014 18:11:49 +0000</pubDate> 
<guid>http://www.jackxiang.com/post/5560/#blogcomment63938</guid> 
<description>
<![CDATA[ 
	呵呵，我去年刚好也是看到石头君的文章，照着博主的思路写了一个php socket 服务器框架workerman，采用的libevebt + 非阻塞 + 多路复用。压测了下，性能很强悍，不必c写的差多少，而且比想象中的稳定，在公司用了几个月了，没出现啥大问题。
]]>
</description>
</item>
</channel>
</rss>