socket服务的模型以及实现–单进程IO复用select

Posted 飞鸿影的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了socket服务的模型以及实现–单进程IO复用select相关的知识,希望对你有一定的参考价值。

利用多进程来提高并发连接数并不理想,进程消耗太大了,这次我们利用IO复用技术来实现。

 
   
   
 
  1. <?php

  2. /**

  3. * 单进程IO复用select

  4. * 同时处理多个连接

  5. */

  6. class Xtgxiso_server

  7. {

  8.    public $socket = false;

  9.    public $master = array();

  10.    public $onConnect = null;

  11.    public $onMessage = null;

  12.    public $onClose = null;

  13.    function __construct($host="0.0.0.0",$port=1215)

  14.    {

  15.        $this->socket = stream_socket_server("tcp://".$host.":".$port,$errno, $errstr);

  16.        if (!$this->socket) die($errstr."--".$errno);

  17.        stream_set_blocking($this->socket,0);

  18.        $id = (int)$this->socket;

  19.        $this->master[$id] = $this->socket;

  20.    }

  21.    public function run(){

  22.        $read = $this->master;

  23.        $receive = array();

  24.        echo  "start run...\n";

  25.        while ( 1 ) {

  26.            $read = $this->master;

  27.            //echo  "waiting...\n";

  28.            $mod_fd = @stream_select($read, $_w = NULL, $_e = NULL, 60);

  29.            if ($mod_fd === FALSE) {

  30.                break;

  31.            }

  32.            foreach ( $read as $k => $v ) {

  33.                if ( $v === $this->socket ) {

  34.                    //echo "new conn\n";

  35.                    $conn = stream_socket_accept($this->socket);

  36.                    if($this->onConnect) {

  37.                        call_user_func($this->onConnect, $conn);

  38.                    }

  39.                    $id = (int)$conn;

  40.                    $this->master[$id] = $conn;

  41.                } else {

  42.                    //echo "read data\n";

  43.                    if ( !isset($receive[$k]) ){

  44.                        $receive[$k]="";

  45.                    }

  46.                    $buffer = fread($v, 10);

  47.                    //echo $buffer."\n";

  48.                    if ( strlen($buffer) === 0 ) {

  49.                        if ( $this->onClose ){

  50.                            call_user_func($this->onClose,$v);

  51.                        }

  52.                        fclose($v);

  53.                        $id = (int)$v;

  54.                        unset($this->master[$id]);

  55.                    } else if ( $buffer === FALSE ) {

  56.                        if ( $this->onClose ){

  57.                            call_user_func($this->onClose, $this->master[$key_to_del]);

  58.                        }

  59.                        fclose($v);

  60.                        $id = (int)$v;

  61.                        unset($this->master[$id]);

  62.                    } else {

  63.                        $pos = strpos($buffer, "\n");

  64.                        if ( $pos === false) {

  65.                            $receive[$k] .= $buffer;

  66.                            //echo "received:".$buffer.";not all package,continue recdiveing\n";

  67.                        }else{

  68.                            $receive[$k] .= trim(substr ($buffer,0,$pos+1));

  69.                            $buffer = substr($buffer,$pos+1);

  70.                            if($this->onMessage) {

  71.                                call_user_func($this->onMessage,$v,$receive[$k]);

  72.                            }

  73.                            switch ( $receive[$k] ){

  74.                                case "quit":

  75.                                    echo "client close conn\n";

  76.                                    fclose($v);

  77.                                    $id = (int)$v;

  78.                                    unset($this->master[$id]);

  79.                                    break;

  80.                                default:

  81.                                    //echo "all package:\n";

  82.                                    //echo $receive[$k]."\n";

  83.                                    break;

  84.                            }

  85.                            $receive[$k]='';

  86.                        }

  87.                    }

  88.                }

  89.            }

  90.            usleep(10000);

  91.        }

  92.    }

  93. }

  94. $server =  new Xtgxiso_server();

  95. $server->onConnect = function($conn){

  96.    echo "onConnect -- accepted " . stream_socket_get_name($conn,true) . "\n";

  97.    fwrite($conn,"conn success\n");

  98. };

  99. $server->onMessage = function($conn,$msg){

  100.    echo "onMessage --" . $msg . "\n";

  101.    fwrite($conn,"received ".$msg."\n");

  102. };

  103. $server->onClose = function($conn){

  104.    echo "onClose --" . stream_socket_get_name($conn,true) . "\n";

  105.    fwrite($conn,"onClose "."\n");

  106. };

  107. $server->run();

这样我们同时处理的连接数就不受限于进程数了,相对于靠多进程来提高连接数的方式明显好了很多。


以上是关于socket服务的模型以及实现–单进程IO复用select的主要内容,如果未能解决你的问题,请参考以下文章

socket编程:多路复用I/O服务端客户端之poll

Redis单线程为何可以处理大量请求?

Redis中的单线程模型

12 TCP服务器 IO多路复用

理解网络IO模型

nginx 多进程 + io多路复用 实现高并发