PHP多线程编程 之 简介(官方文档)


A Brief Introduction to Multi-Threading in PHP 简短介绍PHP中的多线程
Foreword 前言
Execution 执行
Sharing 共享
Synchronization 同步
Pitfalls 陷阱
WTF ?? ?

Preface 前言

If you are a PHP programmer who spends a lot of time at the console, or someone who is interested in high performance modern programming of PHP, this document is for you.
The intention here is to provide information concise and short enough that you (and the community at large) remember it; in the hope that one day all of this will be common knowledge among PHP programmers.
By the end of the document, you should have a clear understanding of how, and why, pthreads exists, and executes.
读完整个文档,你应该有对how, and why, pthreads exists, and executes有一个清晰的认识。
If you have any comments, suggestions or insults please forward them to krakjoe@php.net

Insults will be ignored.

Foreword 前言

Since PHP4, May 22nd 2000, PHP has been equipped to execute isolated instances of the interpreter in multiple threads within a single process without any context interfering with another. We call this TSRM, it is a rarely studied omnipresent part of PHP that nobody really talks about.
从2000年5月22日发布的PHP4开始,PHP已经具备在没有任何其它上下文干扰的单进程的多线程中执行独立的解释器实例。我们称之为TSRM,it is a rarely studied omnipresent part of PHP,没有人真正谈论。
If you have ever used XAMPP or PHP on Windows, it’s likely that you used a threaded PHP without even knowing it.
TSRM has the ability to create isolated instances of the interpreter, which is how pthreads executes userland threads in PHP. The instances of the interpreter are as isolated as they are when executing any threaded build of PHP, the Apache2 Worker MPM PHP5 Module for example. The job of pthreads is to facilitate communication and synchronization between the otherwise isolated contexts.
TSRM有创建独立解释器实例的能力,它是pthreads为何能在PHP中执行用户态线程的原因。The instances of the interpreter are as isolated as they are when executing any threaded build of PHP,比如Apache2的Worker MPM PHP5模块。pthreads的工作是方便和其它独立的上下文之间通信和同步。
Exactly how TSRM works is beyond the scope of this document, and would only confuse the reader (and subject), suffice to say that PHP has been able to work in a multi-threaded environment for more than a decade. The implementation is stable; there is however one well known, but completely misunderstood pitfall, which I shall explain the facts of, and clarify: PHP is a wrapper around third parties, every part of PHP is implemented like this, if a third party does not implement their library in a re-entrant (thread safe) way then the PHP wrapper for that library will fail and or cause unexpected behaviour during execution. A well known example of such a library is locale. It should be clear that this is beyond the control of PHP or pthreads. Such libraries are well known (documented) and or obvious, the vast majority of extensions will have no problem executing in a pthreads application.
Threading in user land was never a concern for the PHP team, and it remains as such today. You should understand that in the world where PHP does its business, there’s already a defined method of scaling – add hardware. Over the many years PHP has existed, hardware has got cheaper and cheaper and so this became less and less of a concern for the PHP team. While it was getting cheaper, it also got much more powerful; today, our mobile phones and tablets have dual and quad core architectures and plenty of RAM to go with it, our desktops and servers commonly have 8 or 16 cores, 16 and 32 gigabytes of RAM, though we may not always be able to have two within budget and having two desktops is rarely useful for most of us.
PHP开发小组从不关心用户空间中的线程,到今天仍然是这样。你应该明白,在PHP的世界里执行其业务,已经有一个定义缩放的方法 – 添加硬件。PHP已经存在多年,硬件越来越便宜,所以PHP团队对用户空间线程关注越来越少。虽然越来越便宜,但是却更加强大,我们的手机和平板电脑有双核和四核架构和大量的RAM,我们的台式机和服务器通常有8个或16个内核,16和32G的RAM…
In addition to the concerns of the PHP team, there are concerns of the programmer: PHP was written for the non-programmer, it is many hobbyists native tongue. The reason PHP is so easily adopted is because it is an easy language to learn and write. Multi-threaded programming is not easy for most, even with the most coherent and reliable API, there are different things to think about, and many misconceptions. The PHP group do not wish for user land multi-threading to be a core feature, it has never been given serious attention – and rightly so. PHP should not be complex, for everyone.
除了PHP开发小组所关注的问题,也有程序员关注:PHP是针对非程序员编写的,它是许多业余者的native tongue。PHP那么容易adopted的原因是因为它是一种容易学和编写的语言。即使是最连贯和可靠的API,大多数的多线程编程是不容易的,有不同的事情要考虑,以及许多误解。 PHP开发组不希望用户态多线程成为一个核心功能,它从来没有得到重视 – 这是正确的。PHP对每个人不应该是复杂的。
All things considered, there are still benefits to be had from allowing PHP to utilize its production ready and tested features to allow a means of making the most out of what we have, when adding more isn’t always an option, and for a lot of tasks is never really needed if you can take advantage of all you have.

A note about nothing, or more precisely, sharing nothing: The architecture of PHP is referred to as Shared Nothing, this simply means that whenever PHP services a request, via any SAPI, its environment, in the sense of the data structures PHP requires to operate, are isolated from one another. On the surface, pthreads would appear to violate this standard and break the architecture that keeps PHP executing. Relax, this is not so. In fact, another job of pthreads (that is never evident to the programmer) is to maintain that architecture; it does this utilizing copy-on-read and copy-on-write semantics and carefully programmed mutex manipulation. The upshot of this is, any time a user does anything, in the sense of reading or writing to an object, or executing its methods, it is safe to assume that the operation was safe and there is no need for further action like the explicit use of mutex by the programmer.
PHP的架构被称为无共享(Shared Nothing),这意味着任何时候PHP为一个请求服务,通过任意的SAPI,它的环境,在PHP要求去操作的数据结构场景上跟另一个彼此独立的。从表面上看,pthread似乎违背这个标准,并打破了保持PHP的执行架构。放轻松,事实并非如此。事实上,pthread中的另一工作(这个对程序员透明)是维护那个架构;它利用copy-on-read(副本上读) 和copy-on-write(副本上写) semantics和精心编写的互斥操作做到的(指维护架构)。这样做的结果是,用户在任何时间做任何事情,在读取或写入到一个对象,或执行它的方法的场景,它安全的去假设该操作是安全的(意思应该是可以认为它是安全,或假设它就是安全的),没有必要采取进一步行动比如被程序员明确使用互斥锁。
Terms in the foreword that are new to the reader should now be researched, as they may appear throughout this document

Execution 执行

Threading is about dividing your instructions into units of execution, and distributing those units among your processors &| cores in such a way as to maximize the throughput of your application.

This should always be done using as few threads as possible.
pthreads exposes two models of execution. The Thread model and the Worker model, they expose much of the same functionality to the programmer, and are internally very similar, with one key difference: what they consider to be the unit of execution.
A Thread is representative of both an interpreter context and a unit of execution (that’s its ::run method).
A Worker is representative of an interpreter context; its ::run method is used to configure that context. The unit of execution for this model is the Stackables, more precisely Stackable::run.
When the programmer calls Thread::start, a new thread is created, a PHP interpreter context is initialized and then (safely) manipulated to mirror the context that made the call to ::start. Execution continues concurrently in both contexts at this point. Execution in the Thread is passed to the ::run method of the Thread. At the end of the ::run method the context for the Thread is destroyed.
When the programmer calls Worker::start, a new thread is created, a PHP interpreter context is again initialized in the same way as a normal Thread, when execution in the Worker leaves Worker::run, the Worker begins to pop Stackables from the stack and execute them in the order they were stacked. If there are no items on the stack the Worker will wait for some to appear. The Worker will continue to do this until Worker::shutdown is called. If Worker::shutdown is called while items remain on the stack they will be executed first and the context that called Worker::shutdown will block until shutdown can occur.
Great care should be taken to avoid wasting contexts unnecessarily, starting a Thread or Worker is not free. Where you can, use the Worker model, this almost eliminates the tendency to be wasteful while multi-threading. Almost, but not completely …
There is a tendency to be wasteful; it’s a common misunderstanding to think that threading anything can make it faster, it cannot. More threads does not always equate to more throughput, in the same way as more water does not always equate to wetter.
Thinking outside the box is a prerequisite of a good multi-threaded programmer; common sense should dictate that more water does mean wetter, but if you consider the central point of the bottom of the bowl: Once it is wet, it does not matter how much water you place on top, it cannot get wetter …
Too much water, or threads, and you will drown.
The author of pthreads will not take responsibility for drowning programmers, or their code.

Sharing 共享

Threading would be rather useless if threads could not manipulate a common set of data, which appears to be a problem in a shared nothing architecture. I don’t see shared nothing as a hindrance, I see it as a rather big helpful push in the right direction.
One of the normal problems for a programmer writing multi-threaded code is the safety and synchronization of data, it is normally very very easy to corrupt an array if 10 threads manipulate it at once.
Shared Nothing solves this problem; if no two contexts ever manipulate the same data then they cannot corrupt each others stack, the architecture is maintained along with its stability.
Objects descending from pthreads utilize a thread safe member storage table that works slightly differently to any other objects. When you write a member to such an object, the table is locked, the data is copied, and then stored in the table and the lock is released. When a subsequent read of that member occurs, the table is locked, the data is copied for return and the lock is released. This means that no two contexts ever manipulate the same physical data – Share Nothing.
Some data does not lend itself to being easily copied, PHP has a solution to this in the form of the serialization API. Serialization is utilized on arrays, and objects not descended from pthreads. Objects descended from pthreads are never serialized, as such you should always use pthreads objects as containers for data you intend to manipulate in multiple contexts.
All objects descending from pthreads can be manipulated, by any context with a reference, as arrays and objects, they also include methods for manipulating members in a thread safe manner. There shouldn’t be a kind of data set you cannot implement with what is exposed by pthreads, and basic sets (arrays) are built in.
所有来自pthreads的对象都可以被任何的带有该对象引用的上下文操作,作为数组和对象,它们也包括可以以线程安全的方式操作成员的方法(就是来自pthreads的对象的方法可以线程安全的操作它的属性)。There shouldn’t be a kind of data set you cannot implement with what is exposed by pthreads, and basic sets (arrays) are built in.(不应该有一种你不能实现的数据集由pthreads来提供,基础的集合(数组)是内建的???)

This is all done in such a way that minimizes memory usage while still maintaining architecture and safety. It may seem wasteful, but it’s a small price to pay, that diminishes with the price of memory.

Synchronization 同步

Sharing isn’t enough, the last piece of the puzzle is synchronization. This is going to be a topic completely alien to a lot of programmers.
While your are executing, and sharing, you must also be able to control when to share, and when to execute; it is no good trying to manipulate data that does not exist !!
Synchronization can be used to put a thread into a receptive, but sleepy state, known as waiting, and can be used to awaken such a thread, known as notifying.
Synchronizing with a unit of execution is easy, but does come with a danger of misuse, which I hope to give a brief, simple explanation of that will stick in your mind and help you to avoid misuse.
Make this your mantra: Only ever wait FOR something


The above code looks simple enough, but what or who is it waiting for, and what happens if whatever they are waiting for has already sent notification … waiting forever is the price for not paying attention to your own mantra.
The syntax of synchronization may look a bit strange, here’s an explanation that gives you a good reason to keep typing all that stuff: when you call ::synchronized a mutex (lock) is acquired, when you call ::wait, that mutex is atomically locked and unlocked to allow other contexts to acquire it while the waiting context blocks on a condition waiting for notification.
同步的语法看起来有一点怪,这里有一个解释将给你一个保持所有东西的好原因:当你调用synchronized时一个互斥(锁)被获取,当你调用wait,那个互斥自动地上锁和解锁以允许另外上下文获取它while the waiting context blocks on a condition waiting for notification. (当等待上下文因条件堵塞等待通知时??)

Waiting for something looks like this:等待某些东西看起来像这样:

    if (!$this->data) {
/* I can manipulate $this->data and know it exists */

While notification looks like this:通知则看起来像这样:

    $that->data = “some”;
}, $that);

In the notification example, you ensure that the context that is waiting is not left hanging around forever because if you have acquired the synchronization lock and the object is not waiting then it need not wait (by the time it can acquire the synchronization lock the data is already set). A call to notify will ensure if you managed to acquire the synchronization lock because it was atomically released by the waiting thread, the waiting thread is awoken and will continue executing.
This kind of explicit synchronization can make for powerful programming, study it well.

Pitfalls 陷阱

The garbage collection built into PHP was never prepared for this kind of prolonged execution, if pthreads followed the PHP way and edited reference counts of objects when we accepted them (as an argument to a method, or as the data for a member property), then memory usage soars, it becomes difficult to retain control of your own code.
So we do not do the done thing; in a pthreads application, you are responsible for the objects you create, you are also responsible for retaining a reference to objects that are going to be executed, or accessed from other executing contexts, until that execution or access has taken place.
This circumvents the problem of out of control memory usage, but it creates another problem; dreaded segfaults.
Segmentation faults occur when you instruct a processor to address memory that it cannot access, they result in abortion of execution. The prime suspect when you encounter segmentation faults during development is objects being referenced that were already destroyed in the context that originally created the object.
Avoiding these segmentation faults sounds much more complex than it in reality is, this can be illustrated best with a (bad) example(用例子来解释):

class W extends Worker {
    public function run(){}
class S extends Stackable {
    public function run(){}
/* 1 */
$w = new W();
/* 2 */
$j = array(
    new S(), new S(), new S()
/* 3 */
foreach ($j as $job)
/* 4 */
$j = array();

The above example will always segfault; steps 1-3 are perfectly normal, but before the Worker is started the stacked objects are deleted, resulting in a segfault when the Worker is allowed to start. Your code will not always look so explicit, but if you can see a route where this could conceivably happen, then program a different way.
Other symptoms of this kind of programming error are the fatal error

Call to a member function member() on a non-object in /my/code.php

and the notice

Trying to get property of non-object in /my/code.php

If you experience these errors, carefully look over your code and make sure everything you have passed to any other context exists all the time it is being referenced or executed in any other context.

This is probably the hardest part of creating applications with pthreads, but it doesn’t take a lot to avoid; plan with care, and program with even more care.

WTF ??

I hear the criticism that I have taken something simple, that’s PHP, and made it more complex by exposing this kind of functionality. I hear you; I would argue that I have taken something complex, and made it relatively simple.

Something being complex, or difficult, is no kind of justification for avoiding it. The complexity of anything should decrease as your knowledge increases, if it does not, then you are not taking in the right kind of information. This is the nature of learning.

To the idea that I haven’t made anything simple; oh rly? If the task is simple: get two things done at once, the implementation is simple. The fact that you are even considering complex ideas is the thing you should be paying attention to!!

To the rest of the nay-sayers: Progress is made by pushing forwards, when we all push at once, we make more progress !!

Even if you hate the idea, I hope I’ve said enough to convince you to give it a try before you form a long lasting opinion that will affect your decisions in the future, what is the worst that can happen !?


PHP多线程编程 之 worker测试

class S extends Stackable{
	public function run(){
		echo "S Stackable Run -->\t".Thread::getCurrentThreadID()."--".microtime(true)."\n";
class SS extends Stackable{
        public function run(){
                echo "SS Stackable Run -->\t".Thread::getCurrentThreadID()."--".microtime(true)."\n";
class T extends Worker{
	public function run(){
		echo "Worker Run -->\t\t".Thread::getCurrentThreadID()."--".microtime(true)."\n";

$t = new T();

$s = new S();
$ss = new SS();



echo "Super -->\t\t".Thread::getCurrentThreadID()."--".microtime(true)."\n";


/usr/local/php-5.5.15/bin/php tw.php
Worker Run -->		140129030506240--1406547417.7107
S Stackable Run -->	140129030506240--1406547418.7123
SS Stackable Run -->	140129030506240--1406547419.7295
Super -->		140129225074624--1406547419.73



/usr/local/php-5.5.15/bin/php tw.php
Worker Run -->		140428491556608--1406547565.413
Super -->		140428686124992--1406547565.4134
S Stackable Run -->	140428491556608--1406547566.414
Segmentation fault



PHP多线程编程 – 实例之Fetch


class TestObject {
	public $val;

class Fetching extends Thread {
	public function run(){
		echo "Begin Fetching run method: ".Thread::getCurrentThreadId()."\n";
		* of course ...
		$this->sym = 10245;
		$this->arr = array(
			"1", "2", "3"
		* objects do work, no preparation needed ...
		* read/write objects isn't finalized ..
		* so do the dance to make it work ...
		$obj = new TestObject();
		$obj->val = "testval";
		$this->obj = $obj;
		* will always work
		$this->objs = serialize($this->obj);
		* nooooooo
		$this->res = fopen("php://stdout", "w");
		* tell the waiting process we have created symbols and fetch will succeed

			echo "Begin ".Thread::getCurrentThreadId()." notify.\n"; 
			echo "End ".Thread::getCurrentThreadId()." notify.\n";
		/* wait for the process to be finished with the stream */
		    echo "Begin ".Thread::getCurrentThreadId()." wait.\n";
			echo "End ".Thread::getCurrentThreadId()." wait.\n";
		echo "End Thread run method: ".Thread::getCurrentThreadId()."\n";

$thread = new Fetching();


	echo "Begin ".Thread::getCurrentThreadId()." wait.\n";
	echo "End ".Thread::getCurrentThreadId()." wait.\n";
}, $thread);
* we just got notified that there are symbols waiting
foreach(array("sym", "arr", "obj", "objs", "res") as $symbol){
	printf("\$thread->%s: ", $symbol);	
	$fetched = $thread->$symbol;
	if ($fetched) {
			* manual unserialize
			case "objs":
			default: var_dump($fetched);

/* notify the thread so it can destroy resource */
}, $thread);


/usr/local/php-5.5.15/bin/php Fetch.php
Begin Fetching run method: 140107248678656
Begin 	140107248678656 notify.
End  	140107248678656 notify.
Begin 140107248678656 wait.
Begin 140107443247040 wait.



	echo "Begin ".Thread::getCurrentThreadId()." wait.\n";
	echo "End ".Thread::getCurrentThreadId()." wait.\n";
}, $thread);

这段代码让当前线程堵塞(Begin 140107443247040 wait.)。同时这段代码和全局文件处于同一个线程中,所以它不会继续执行以下代码,这个时候实际两个线程都堵塞了,所以它会一直堵塞下去。


/usr/local/php-5.5.15/bin/php Fetch.php
Begin Fetching run method: 139715053770496
Begin 139715248338880 wait.
Begin 139715053770496 notify.
End 139715053770496 notify.
End 139715248338880 wait.
Begin 139715053770496 wait.
$thread->sym: int(10245)

$thread->arr: array(3) {
  string(1) "1"
  string(1) "2"
  string(1) "3"

$thread->obj: object(TestObject)#2 (1) {
  string(7) "testval"

$thread->objs: object(TestObject)#2 (1) {
  string(7) "testval"

$thread->res: resource(4) of type (stream)

End 139715053770496 wait.
End Fetching run method: 139715053770496


永久链接: http://blog.ifeeline.com/1111.html

PHP多线程编程 – 实例之Benchmark


class T extends Thread {
	public function run() {}

$max = @$argv[1] ? $argv[1] : 100;
$sample = @$argv[2] ? $argv[2] : 5;

printf("Start(%d) ...", $max);
$it = 0;
do {
    $s = microtime(true);
    /* begin test */
    $ts = [];
    while (count($ts)<$max) {
        $t = new T();
    $ts = [];
    /* end test */
    //每秒的线程数 每秒事务量(TPS)
    $ti [] = $max/(microtime(true)-$s);
} while ($it++ < $sample);

printf(" %.3f tps\n", array_sum($ti) / count($ti));


/usr/local/php-5.5.15/bin/php Benchmark.php 200 2
Start(200) ...... 523.638 tps
/usr/local/php-5.5.15/bin/php Benchmark.php 500 2
Start(500) ...... 218.953 tps
/usr/local/php-5.5.15/bin/php Benchmark.php 1000 2
Start(1000) ...... 139.939 tps

永久链接: http://blog.ifeeline.com/1107.html

PHP函数参考 – 进程控制扩展 – pthreads(安装与实验)

PHP多线程编程需要一个PECL库pthreads的支持(PHP 5.3+),但是PHP本身必须是线程安全的,要开启PHP的线程安全模式,必须在编译PHP时加上–enable-maintainer-zts。



cd php-5.5.15
./configure --prefix=/usr/local/php-5.5.15 --with-config-file-path=/usr/local/php-5.5.15/etc --with-mysql=mysqlnd --with-mysqli=mysqlnd --with-iconv-dir --with-freetype-dir=/usr --with-jpeg-dir=/usr --with-png-dir=/usr --with-zlib --with-libxml-dir=/usr --enable-xml --disable-rpath --enable-bcmath --enable-shmop --enable-sysvsem --enable-inline-optimization --with-curl --enable-mbregex --enable-mbstring --with-mcrypt=/usr --with-gd --enable-gd-native-ttf --with-openssl --with-mhash --enable-pcntl --enable-sockets --with-xmlrpc --enable-zip --enable-soap --enable-opcache --with-pdo-mysql --enable-maintainer-zts --enable-fpm
make install

cd pthreads-2.0.7
./configure --with-php-config=/usr/local/php-5.5.15/php/bin/php-config
make install

vi /usr/local/php-5.5.15/etc/php.ini
extension = “pthreads.so”

/usr/local/php-5.5.15/bin/php -m | grep pthreads

/usr/local/php-5.5.15/bin/php -i | grep "Thread Safety"
Thread Safety => enabled



class Async extends Thread {
	public function __construct($method, $params){
		$this->method = $method;
		$this->params = $params;
		$this->result = null;

	public function run(){
		if (($this->result=call_user_func_array($this->method, $this->params))) {
			return true;
		} else return false;

	public static function call($method, $params){
		$thread = new Async($method, $params);
			return $thread;

function http_curl_get($url,$userAgent="") {  
      $userAgent = $userAgent ? $userAgent : 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2)';   
      $curl = curl_init();  
      curl_setopt($curl, CURLOPT_URL, $url);  
      curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);  
      curl_setopt($curl, CURLOPT_TIMEOUT, 5);  
      curl_setopt($curl, CURLOPT_USERAGENT, $userAgent);  
      $result = curl_exec($curl);  
      return $result;  
for ($i=0; $i < 20; $i++){
	$urls[] = array("name"=>"localhost","url"=>"".$i);

$t = microtime(true); 
$threads = array();
foreach ($urls as $k => $v)  {
	$threads[$k] = Async::call("http_curl_get",array($v["url"]));
$rslt = array();
	$sft = array_shift($threads);
	if($sft == NULL){
			$rslt[] = $sft->result;
$e = microtime(true); 
echo "多线程执行时间	:".($e-$t)." -- ($t $e)\n\n";
print_r($rslt);		//证明获取到了数据

$rslt_new = array();
$t = microtime(true);  
foreach ($urls as $k => $v){  
      $rslt_new[$k] = http_curl_get($v["url"]);  
$e = microtime(true);  
echo "For循环     	:".($e-$t)." -- ($t $e)\n\n";
print_r($rslt_new);	//证明获取到了数据

sleep(1); 	//睡眠1秒,模拟网络延迟
echo $_GET["d"]."->". date("Y-m-d H:i:s")." -- ".microtime(true);


/usr/local/php-5.5.15/bin/php sync.php

多线程执行时间	:2.0598430633545 -- (1406385589.4594 1406385591.5192)
    [0] => 0->2014-07-26 22:39:50 -- 1406385590.4706
    [1] => 1->2014-07-26 22:39:50 -- 1406385590.4746
    [2] => 2->2014-07-26 22:39:50 -- 1406385590.4886
    [3] => 3->2014-07-26 22:39:50 -- 1406385590.4956
    [4] => 4->2014-07-26 22:39:50 -- 1406385590.4996
    [5] => 5->2014-07-26 22:39:50 -- 1406385590.5056
    [6] => 6->2014-07-26 22:39:50 -- 1406385590.5096
    [7] => 7->2014-07-26 22:39:50 -- 1406385590.5136
    [8] => 8->2014-07-26 22:39:50 -- 1406385590.5176
    [9] => 9->2014-07-26 22:39:50 -- 1406385590.5226
    [10] => 10->2014-07-26 22:39:50 -- 1406385590.6717
    [11] => 11->2014-07-26 22:39:51 -- 1406385591.4716
    [12] => 12->2014-07-26 22:39:51 -- 1406385591.4756
    [13] => 13->2014-07-26 22:39:51 -- 1406385591.4896
    [14] => 14->2014-07-26 22:39:51 -- 1406385591.4967
    [15] => 15->2014-07-26 22:39:51 -- 1406385591.5007
    [16] => 16->2014-07-26 22:39:51 -- 1406385591.5066
    [17] => 17->2014-07-26 22:39:51 -- 1406385591.5106
    [18] => 18->2014-07-26 22:39:51 -- 1406385591.5157
    [19] => 19->2014-07-26 22:39:51 -- 1406385591.5186

For循环     	:20.12659406662 -- (1406385591.5195 1406385611.6461)
    [0] => 0->2014-07-26 22:39:52 -- 1406385592.5268
    [1] => 1->2014-07-26 22:39:53 -- 1406385593.5609
    [2] => 2->2014-07-26 22:39:54 -- 1406385594.576
    [3] => 3->2014-07-26 22:39:55 -- 1406385595.5936
    [4] => 4->2014-07-26 22:39:56 -- 1406385596.6037
    [5] => 5->2014-07-26 22:39:57 -- 1406385597.6053
    [6] => 6->2014-07-26 22:39:58 -- 1406385598.607
    [7] => 7->2014-07-26 22:39:59 -- 1406385599.6086
    [8] => 8->2014-07-26 22:40:00 -- 1406385600.6107
    [9] => 9->2014-07-26 22:40:01 -- 1406385601.6123
    [10] => 10->2014-07-26 22:40:02 -- 1406385602.6143
    [11] => 11->2014-07-26 22:40:03 -- 1406385603.6163
    [12] => 12->2014-07-26 22:40:04 -- 1406385604.6184
    [13] => 13->2014-07-26 22:40:05 -- 1406385605.6204
    [14] => 14->2014-07-26 22:40:06 -- 1406385606.6224
    [15] => 15->2014-07-26 22:40:07 -- 1406385607.6245
    [16] => 16->2014-07-26 22:40:08 -- 1406385608.6266
    [17] => 17->2014-07-26 22:40:09 -- 1406385609.6286
    [18] => 18->2014-07-26 22:40:10 -- 1406385610.6437
    [19] => 19->2014-07-26 22:40:11 -- 1406385611.6456



PHP函数参考 – 其它服务 – Sockets(Socket编程)

PHP的Socket支持放入了“其它服务 – Sockets”一节(http://cn2.php.net/manual/zh/book.sockets.php)。实际上这部分内容和PHP的“其它基本扩展 — Streams”一节有重复,不过Streams比Socket更加抽象和具体。

PHP中的socket编程跟C socket编程非常类似。要让PHP支持socket编程,在编译PHP时必须在配置中添加–enable-sockets 配置项来启用。

./configure --prefix=/usr/local/php-5.5.15 --with-config-file-path=/usr/local/php-5.5.15/etc \
--with-mysql=mysqlnd --with-mysqli=mysqlnd --with-iconv-dir --with-freetype-dir=/usr \ 
--with-jpeg-dir=/usr --with-png-dir=/usr --with-zlib --with-libxml-dir=/usr --enable-xml \
--disable-rpath --enable-bcmath --enable-shmop --enable-sysvsem --enable-inline-optimization \
--with-curl --enable-mbregex --enable-mbstring --with-mcrypt=/usr --with-gd --enable-gd-native-ttf \
--with-openssl --with-mhash --enable-pcntl --enable-sockets --with-xmlrpc --enable-zip --enable-soap 
--enable-opcache --with-pdo-mysql --enable-maintainer-zts --enable-fpm


$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($server, '', 8080);

        while($client = socket_accept($server)){
                //socket_set_option($client, SOL_SOCKET, SO_RCVTIMEO, array("sec" => 5, "usec" => 0));

                $buf = socket_read($client,8192);
                echo "Read data from client success is-->\n".$buf."\n\n";

                $writeto = "$i --> Server Server Server";
                        echo "Write ($writeto) to client success.\n\n";


$client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($client, "", 8080);

$to = "Client Client Client";
        echo "Write ($to) to server success.\n\n";

while($out = socket_read($client,8192)){
        echo "\n\nRead data from server sussess-->\n";
        echo $out."\n\n";



/usr/local/php-5.5.15/bin/php php_socket_client.php 
Write (Client Client Client) to server success.

Read data from server sussess-->
1 --> Server Server Server

/usr/local/php-5.5.15/bin/php php_socket_server.php 
Read data from client success is-->
Client Client Client

Write (1 --> Server Server Server) to client success.


1) resource socket_create ( int $domain , int $type , int $protocol )
$domain 指定应用的协议,跟C Socket一致,只有AF_INET、AF_INET6和AF_UNIX
$type 指定套接字使用的类型,一般如果是TCP就是SOCK_STREAM,UDP就是SOCK_DGRAM
$protocol 设置指定 domain 套接字下的具体协议。TCP 或 UDP,可以直接使用常量 SOL_TCP 和 SOL_UDP
socket_create() 正确时返回一个套接字,失败时返回 FALSE。要读取错误代码,可以调用 socket_last_error()。这个错误代码可以通过 socket_strerror() 读取文字的错误说明。

2)bool socket_bind ( resource $socket , string $address [, int $port = 0 ] )
绑定地址和端口到$socket。在使用 socket_listen()之前,这个操作必须先完成。 如果如果创建的套接字是AF_INET,$address就是IP地址,$port就是端口号;如果套接字是AF_UNIX,$address本地套接字路径,$port不需要指定。

函数socket_create() 创建的socket默认是一个主动类型的,socket_listen函数将socket变为被动类型的,等待客户的连接请求。


3) bool socket_listen ( resource $socket [, int $backlog = 0 ] )

4) resource socket_create_listen ( int $port [, int $backlog = 128 ] )

5) resource socket_accept ( resource $socket )

socket_accept ()函数返回的是一个具体的套接字。以后的通信交互都是基于这个新的套接字的。

6)string socket_read ( resource $socket , int $length [, int $type = PHP_BINARY_READ ] )
从套接字读取数据,注意$type默认是PHP_BINARY_READ,表示二进制数据,字节流。如果设置成PHP_NORMAL_READ 就是遇到“\r”或“\n”就返回。

PHP还提供了int socket_recv ( resource $socket , string &$buf , int $len , int $flags )函数,把返回的的数据写入到引用变量$buf中,函数返回接收到的字节数量。

PHP还提供了int socket_recvfrom ( resource $socket , string &$buf , int $len , int $flags , string&$name [, int &$port ] )函数,它从套接字中读取数据,不管这个套接字是不是面向链接的。

7) int socket_write ( resource $socket , string $buffer [, int $length = 0 ] )

PHP还提供了int socket_send ( resource $socket , string $buf , int $len , int $flags ),它向套接字中写指定长度字节的数据。

PHP还提供了int socket_sendto ( resource $socket , string $buf , int $len , int $flags , string $addr [,int $port = 0 ] )函数它向套接字写指定长度的数据而不管这个套接字是否是面向链接的。

8 int socket_last_error ([ resource $socket ] )

9 void socket_close ( resource $socket )

永久链接: http://blog.ifeeline.com/1100.html