php中socket服务的模型下的编程方式(同步和异步)

    前面我们花了一段时间来搭建高性能的socket服务,可以同时处理大量的连接,但这是在没有具体业务的情况下。

    如果我们启用了一个单进程的server,但里面的一个业务耗时1秒,那么在这1秒内是阻塞的,后续的请求会等待,如果并发三个请求,那么三个请求的执行时间会分别昌1秒,2秒,3秒.提高并发的方法有以下几种:

    1:多启动进程,提高并发数

    2:优化业务,减少耗时间相当于减少阻塞时间,提高并发数

    3:异步编程,避免阻塞,提高并发数

    这里我们重点介绍第三种方法,以访问第三方http为例。

    代码如下:

<?php
//同步读取
function get_data_blocking(){
    $socket = stream_socket_client("tcp://test.raventech.cn:80", $errno, $errstr, 6);
    fwrite($socket, "GET /sleep1.php HTTP/1.0\r\nHost: test.raventech.cn\r\nAccept: */*\r\n\r\n");
    $str = "";
    while (!feof($socket)) {
        $str .= fgets($socket, 1024);
    }
    fclose($socket);
    return $str;
}

//异步读取
function get_data_unblocking(){
    $socket = stream_socket_client("tcp://test.raventech.cn:80", $errno, $errstr, 6);
    stream_set_blocking($socket, 0);
    fwrite($socket, "GET /sleep1.php HTTP/1.0\r\nHost: test.raventech.cn\r\nAccept: */*\r\n\r\n");
    $write  = NULL;
    $except = NULL;
    while( $socket ){
        $read   = array($socket);
        $num_changed_streams = stream_select($read, $write, $except, 0);
        if ( $num_changed_streams > 0 ) {
            foreach($read as $r){
                $str = fread($r,2048);
                fclose($socket);
                $socket = false;
                return $str;
            }
        }
        usleep(100);
    }
}

//真正的异步读取--利用server的IO复用事件来提高并发
class Get_data_event{

    public $onMessage = null;
    private $str='';

    function __construct(&$server){
        $socket = stream_socket_client("tcp://test.xtgxiso.cn:80", $errno, $errstr, 6);
        stream_set_blocking($socket, 0);
        fwrite($socket, "GET /sleep1.php HTTP/1.0\r\nHost: test.xtgxiso.cn\r\nAccept: */*\r\n\r\n");
        $server->add_socket($socket, array($this, 'read'));
    }

    public function read($socket){
        while (1) {
            $buffer = fread($socket, 1024);
            if ($buffer === '' || $buffer === false) {
                break;
            }
            $this->str .= $buffer;
        }
        if( $this->onMessage && $this->str ) {
            call_user_func($this->onMessage, $this->str);
        }
        $this->str = '';
        return false;
    }

}

/**
 * 单进程IO复用select
 */
class Xtgxiso_server
{
    public $socket = false;
    public $master = array();
    public $onConnect = null;
    public $onMessage = null;
    public $other_socket_callback = array();

    function __construct($host="0.0.0.0",$port=1215)
    {
        $this->socket = stream_socket_server("tcp://".$host.":".$port,$errno, $errstr);
        if (!$this->socket) die($errstr."--".$errno);
        stream_set_blocking($this->socket,0);
        $id = (int)$this->socket;
        $this->master[$id] = $this->socket;
    }

    public function add_socket($socket,$callback){
        $id = (int)$socket;
        $this->master[$id] = $socket;
        $this->other_socket_callback[$id] = $callback;
    }

    public function run(){
        $read = $this->master;
        $receive = array();
        echo  "start run...\n";
        while ( 1 ) {
            $read = $this->master;
            //echo  "waiting...\n";
            $mod_fd = @stream_select($read, $_w = NULL, $_e = NULL, 60);
            if ($mod_fd === FALSE) {
                break;
            }
            foreach ( $read as $k => $v ) {
                $id = (int)$v;
                if ( $v === $this->socket ) {
                    //echo "new conn\n";
                    $conn = stream_socket_accept($this->socket);
                    if ($this->onConnect) {
                        call_user_func($this->onConnect, $conn);
                    }
                    $id = (int)$conn;
                    $this->master[$id] = $conn;
                } else if ( @$this->other_socket_callback[$id] ){
                    call_user_func_array($this->other_socket_callback[$id], array($v));
                } else {
                    //echo "read data\n";
                    if ( !isset($receive[$k]) ){
                        $receive[$k]="";
                    }
                    $buffer = fread($v, 1024);
                    //echo $buffer."\n";
                    if ( strlen($buffer) === 0 ) {
                        if ( $this->onClose ){
                            call_user_func($this->onClose,$v);
                        }
                        fclose($v);
                        $id = (int)$v;
                        unset($this->master[$id]);
                    } else if ( $buffer === FALSE ) {
                        if ( $this->onClose ){
                            call_user_func($this->onClose, $this->master[$key_to_del]);
                        }
                        fclose($v);
                        $id = (int)$v;
                        unset($this->master[$id]);
                    } else {
                        $pos = strpos($buffer, "\r\n\r\n");
                        if ( $pos === false) {
                            $receive[$k] .= $buffer;
                            //echo "received:".$buffer.";not all package,continue recdiveing\n";
                        }else{
                            $receive[$k] .= trim(substr ($buffer,0,$pos+4));
                            $buffer = substr($buffer,$pos+4);
                            if($this->onMessage) {
                                call_user_func($this->onMessage,$v,$receive[$k]);
                            }
                            $receive[$k]='';
                        }
                    }
                }
            }
            usleep(10000);
        }
    }
}

$server =  new Xtgxiso_server();

$server->onConnect = function($conn){
    echo "onConnect -- accepted " . stream_socket_get_name($conn,true) . "\n";
};

$server->onMessage = function($conn,$msg) use ( $server ) {
    /*
    $respone ="";//响应内容
    $respone = "HTTP/1.1 200 OK\r\n";
    $respone .= "Server: openresty\r\n";
    $respone .= "Content-Type: text/html; charset=utf-8\r\n";
    $body = time().rand(111111,999999);
    $len = strlen($body);
    $respone .= "Content-Length:$len\r\n";
    $respone .= "Connection: close\r\n";
    $respone .= "\r\n$body\r\n\r\n";
    echo "onMessage --" . $msg . "\n";
    */

    //同步读取
    //$respone = get_data_blocking();
    //fwrite($conn,$respone);

    //异步读取
    //$respone = get_data_unblocking();
    //fwrite($conn,$respone);

    //真正异步
    $data = new Get_data_event($server);
    $data->onMessage = function($str) use($conn){
        fwrite($conn,$str);
    };

};

$server->onClose = function($conn){
    echo "onClose --" . "\n";
};

$server->run();
    第三方服务sleep1.php的代码比较简单

<?php
sleep(1);//模拟耗时
echo "OK";
    通过以上代码示例,我们分别注释运行 同步读取,异步读取,真正异步,来观察server的并发.测试方法可以写个test.html来模拟三个并发.

<script src="http://127.0.0.1:1215/?id=1"></script>
<script src="http://127.0.0.1:1215/?id=2"></script>
<script src="http://127.0.0.1:1215/?id=3"></script>
    通过测试发现,真正异步的是并发的,每个请求耗时1秒,这样我们总算明白什么是真正的非阻塞异步编程了,关键就在共用IO复用.

时间: 2024-09-13 05:35:05

php中socket服务的模型下的编程方式(同步和异步)的相关文章

php socket服务的模型以及实现 多进程IO复用libevent

端口复用技术,这样就可以很好的解决惊群问题和stream_socket_server性能瓶颈的问题. <?php /**  * 多进程IO复用libevent  * 同时处理多个连接  * 端口复用---建议php7  */ class Xtgxiso_server {     public $socket = false;     public $master = array();     public $onConnect = null;     public $onMessage = nu

Android中Socket的应用分析_Android

本文实例分析了Android中Socket的应用.分享给大家供大家参考,具体如下: Android 提供的常用的网络编程包括针对TCP/IP协议的Socket通信.Socket是一种跨平台的编程方式,可以在异构语言之间进行通信. Socket程序的开发原理,是要实现服务器端和客户端. 服务器,使用ServerSocket监听指定的端口,端口可以随意指定(由于1024以下的端口通常属于保留端口,在一些操作系统中不可以随意使用,所以建议使用大于1024的端口),等待客户连接请求,客户连接后,会话产生

Android中Socket的应用分析

本文实例分析了Android中Socket的应用.分享给大家供大家参考,具体如下: Android 提供的常用的网络编程包括针对TCP/IP协议的Socket通信.Socket是一种跨平台的编程方式,可以在异构语言之间进行通信. Socket程序的开发原理,是要实现服务器端和客户端. 服务器,使用ServerSocket监听指定的端口,端口可以随意指定(由于1024以下的端口通常属于保留端口,在一些操作系统中不可以随意使用,所以建议使用大于1024的端口),等待客户连接请求,客户连接后,会话产生

ajax中的async属性值之同步和异步及同步和异步区别_AJAX相关

jquery中ajax方法有个属性async用于控制同步和异步,默认是true,即ajax请求默认是异步请求,有时项目中会用到AJAX同步.这个同步的意思是当JS代码加载到当前AJAX的时候会把页面里所有的代码停止加载,页面出现假死状态,当这个AJAX执行完毕后才会继续运行其他代码页面假死状态解除.而异步则这个AJAX代码运行中的时候其他代码一样可以运行. ajax中async这个属性,用于控制请求数据的方式,默认是true,即默认以异步的方式请求数据. 一.async值为true (异步) 当

ajax中的async属性值之同步和异步及同步和异步区别

jquery中ajax方法有个属性async用于控制同步和异步,默认是true,即ajax请求默认是异步请求,有时项目中会用到AJAX同步.这个同步的意思是当JS代码加载到当前AJAX的时候会把页面里所有的代码停止加载,页面出现假死状态,当这个AJAX执行完毕后才会继续运行其他代码页面假死状态解除.而异步则这个AJAX代码运行中的时候其他代码一样可以运行. ajax中async这个属性,用于控制请求数据的方式,默认是true,即默认以异步的方式请求数据. 一.async值为true (异步) 当

SSAS:在SQL Server 2012下查看SSAS分析服务的模型及模型简介

在SSDT中部署一个 SSAS 项目到本地服务器上出现错误 You cannot deploy the model because the localhost deployment server is not running in multidimensional mode. 错误原因是因为我在本地安装 SQL Server 2012 的时候只选择安装了 Tabular Mode, 而这个Demo项 目是多维数据集项目, 在 SQL Server 2012 中被称之为 Multidimensio

socket服务端必须写在wcf或winform中吗

问题描述 没做过socket,现在要做socket的服务端,我现在将代码写在web项目的类库中,不清楚socket服务是否随着web项目启动而启动,还是需要重新起一个winform或wcf项目,请指点下呀 解决方案 解决方案二:和通常的socket一样写就可以了解决方案三:需要单独写一个socket程序,可以是控制台,winform,wpf,跟wcf不是一回事.解决方案四:写个单独服务,部署在服务器.解决方案五:也可以卸载web项目的gloax文件中解决方案六:也可以windowsservice

socket-Java中的Socket服务中如何实现客户端和服务端多次通讯

问题描述 Java中的Socket服务中如何实现客户端和服务端多次通讯 比如,服务器给客户端发一个信息.然后服务端根据客户端发送的信息判断,再回复一个信息,就是多次读和写的问题 解决方案 可以利用多线程, 服务器端不断接收客户端连接, 连接一个就开一个线程处理 客户端的交互, 解决方案二: 这里有一个简单的例子,Client端发送消息给Server端,并读取Server端的回复信息:而Server端则先读取Client端的数据,再回复数据.Server端: public class Main {

很幽默的讲解六种Socket I/O模型

很幽默的讲解六种Socket I/O模型   信息来源:幻影论坛     作 者: flyinwuhan (制怒·三思而后行) 本文简单介绍了当前Windows支持的各种Socket I/O模型,如果你发现其中存在什么错误请务必赐教. 一:select模型二:WSAAsyncSelect模型三:WSAEventSelect模型四:Overlapped I/O 事件通知模型五:Overlapped I/O 完成例程模型六:IOCP模型 老陈有一个在外地工作的女儿,不能经常回来,老陈和她通过信件联系