何涛

最近更新

技术文章‎ > ‎

基于stream的PHP的TcpServer类

发布者:何涛,发布时间:2009-7-16 下午11:43
这个类,现在在linux下,只能高于5.2的版本才能正常使用。

例子

<?php
include("TcpServer.class.php");

class Security extends TcpServerHandle{

    var $init_string ='<?xml version="1.0" encoding="UTF-8"?><cross-domain-policy><allow-access-from domain="*" to-ports="80,999
8,10000,10001,10002"/></cross-domain-policy>';
    public function HandleNewConnection(){
    //  print_r("CT:".count($this->getClients())."\n");
        $this->write("Welcome...\n");
        //$this->close();
    }
    public function HandleData($data){
            echo "RECV{{$data}},LEN{".strlen($data)."}\n";
            $this->write("You Send:$data");
    }
}
$server = new TcpServer($argv[1]);
$server->max_clients = 1000;
$server->bind("Security");
$server->run();
?>


TcpServer类

TcpServer

<?php
/*
  +----------------------------------------------------------------------+
  | PHP Version 5                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 1997-2008 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 3.01 of the PHP license,      |
  | that is bundled with this package in the file LICENSE, and is        |
  | available through the world-wide-web at the following url:           |
  | http://www.php.net/license/3_01.txt                                  |
  | If you did not receive a copy of the PHP license and are unable to   |
  | obtain it through the world-wide-web, please send a note to          |
  | license@php.net so we can mail you a copy immediately.               |
  +----------------------------------------------------------------------+
  | Authors: Hetal
  +----------------------------------------------------------------------+
*/


class TcpServerHandle{
    /**
     * 所有属性全是只读,请不要修改
     **/

    /**
     * 当前所有连接
     **/
    //var  $_clients;//所有在线的人的sock
    /**
     * 当前连接socket
     **/
    var  $_sock;
    /**
     * 当前连接的ID号
     **/
    var  $id;
    /**
     * 当前连接的客户端ip
     **/
    var  $ip;
    /**
     * 当前连接的客户端磁口
     */
    var  $port;
   
    /**
     * 新连接进入时调用
     **/
    public function HandleNewConnection(){
    }
    /**
     * 有数据可读时调用
     **/
    public function HandleData($data){
    }

    /**
     * 连接断开时调用
     **/
    public function HandledisConnected(){

    }
    /**
     * 写数据
     **/
    public function write($data){
        return stream_socket_sendto($this->_sock,$data);
    }
    /**
     * 关闭连接
     **/
    public function close(){
        fclose($this->_sock);
        unset(TcpServer::$_clients[$this->_sock]);
        unset(TcpServer::$_objects[$this->_sock]);
    }
    /**
     * 获取服务端所有连接
     **/
    public function getClients(){
        return TcpServer::$_clients;
    }
}

/**
 *
 */

class TcpServer{

    /**
     * 最大连接数,linux支持1000
     **/
    var $max_clients = 150;
   
    /**
     * 主要socket
     **/
    private  $_sock;
    private  $_objectName;
    private $_port;
    static  $_objects;
    static  $_clients;
    public function __construct($port=9999){
        $this->_port = $port;
    }
    public function __desctruct(){
        fclose($this->_sock);
    }
    public function bind($objectName){
        $this->_objectName = $objectName;
    }
    public function run(){
        $this->_sock = stream_socket_server("tcp://0.0.0.0:".$this->_port, $errno, $errstr);
        if(!$this->_sock){
            die( "$errstr ($errno)<br />\n");
        }
        stream_set_blocking ($this->_sock,1);

        TcpServer::$_clients = array();
        TcpServer::$_clients[$this->_sock] = $this->_sock;

        while (true) {

            $read = TcpServer::$_clients;
            if (false===($num_changed_streams = stream_select($read, $write = NULL, $except = NULL, NULL))){
                continue;
            }elseif($num_changed_streams>0){
                for ($i = 0; $i < $num_changed_streams; ++$i) {
                    if ($read[$i] === $this->_sock) {
                        //新连接

                        if(false !==($newsock = stream_socket_accept($this->_sock))){
                            if(count(TcpServer::$_clients)>$this->max_clients){
                                echo count(TcpServer::$_clients).":"."超过了限制[".$this->max_clients."]了\n";
                                fclose($newsock);
                            }else{
                                stream_set_timeout($newsock, 2);

                                $socket_name = explode(":", stream_socket_get_name($newsock,true));
                               
                                TcpServer::$_objects[$newsock] = new $this->_objectName;
                                TcpServer::$_objects[$newsock]->_sock = $newsock;
                                TcpServer::$_objects[$newsock]->id = (int)$newsock;
                                TcpServer::$_objects[$newsock]->ip = $socket_name[0];
                                TcpServer::$_objects[$newsock]->port = $socket_name[1];

                                TcpServer::$_clients[$newsock] = $newsock;

                                $command="HandleNewConnection";
                                if(method_exists (TcpServer::$_objects[$newsock],$command)){
                                    call_user_func_array(array(&TcpServer::$_objects[$newsock], $command), array());
                                }
                            }
                        }

                    }else{
                        $read_sock = $read[$i];
                        $data = "";
                        while(($c=fgetc($read_sock))!==false && $c!="\0" && $c!="\n" && $c!="\r"){
                            $data .=$c;
                        }
                        if ($c === false) {

                            $info = stream_get_meta_data($read_sock);
                            if ($info['timed_out']) {
                                fclose($read_sock);
                            }
                            $command="HandledisConnected";
                            if(method_exists (TcpServer::$_objects[$read_sock],$command)){
                                call_user_func_array(array(&TcpServer::$_objects[$read_sock], $command), array());//, array($read_sock));
                            }
           
                            unset(TcpServer::$_objects[$read_sock]);
                            unset(TcpServer::$_clients[$read_sock]);
                            continue;
                        }elseif($data!=""){
                            $command = "HandleData";
                            if(method_exists (TcpServer::$_objects[$read_sock],$command)){
                                call_user_func_array(array(&TcpServer::$_objects[$read_sock], $command), array($data));
                            }
                        }


                    }
                }
            }
        }
    }
}
?>