zoukankan      html  css  js  c++  java
  • 用 Socket 和 Pcntl 实现一个多进程服务器(一)

        要建立一个简单的服务,如果不考虑性能方面的问题,比如并发100 左右的服务,可以简单的用 Socket + Pcntl。
     来实现,我准备写一个系列的教程,让新手就能进行编写socket 服务。
        下面要实现的是这样一个服务,就是能进行加减乘除的四则运算。数字可以是任意大的数。可以用下面的命令测试这个服务:
    telnet 122.224.124.251 8086
    就会进入下面的界面:


     Welcome to the PHP Test Server.

     To quit, type 'quit'.
    #


    输入quit 就可以退出。
    下面演示功能:
    输入: 11111111111111111111111 * 222222222222222222222222222

    # 11111111111111111111111 * 222222222222222222222222222
    # result is : 2469135802469135802469111108641975308641975308642.
    就能把结果计算出来。

    这个演示的服务,可以多个人同时进行 运算。这个或许就是一个基本的多线程服务,比如,web服务器
    就是一个多线程服务,但是,它要处理大量线程,进程的并发问题。所以比较复杂。
    下面是代码, 具体的解释就在后面的教程中了。

    这个类是处理的是进程控制,具体的逻辑处理封装在了 clientHandle 这个回调函数里面。通过修改这个回调
    函数的内容,你也能很快的定制一个自己的服务器。
    <?php
    class Simple_Server 
    {   
        
    private $sock;
        
        
    private $csock;
        
        
    private $isListen = true;
        
        
    private $callback;
        
        
    private $user;
        
        
    private $uid;
        
        
    private $gid;
        
        
    private $userHome;
        
        
        
    private $scriptName = "simple-server";
        
        
        
        
    /**
         * use $user set the user run the script.
         * fock a thread, init the socket, and wait user to request.
         *
         
    */
        
    function __construct($callback, $ip = '127.0.0.1', $port = '8086',$user = 'daemon')
        {
            
    error_reporting(E_ALL); 
            
    ini_set("display_errors", 0);
            
    set_time_limit(0); 
            
    ob_implicit_flush();
            
    declare(ticks = 1);
            
    $this->callback = $callback;
            
    $this->user = $user;
            
            
    $this->getUserInfo();
            
    $this->changeIdentity();
            
    $this->daemon();

            pcntl_signal(SIGTERM
    , array($this, 'sigHandler'));
            pcntl_signal(SIGINT
    ,  array($this, 'sigHandler'));
            pcntl_signal(SIGCHLD
    , array($this, 'sigHandler'));

            
    $this->run($ip, $port);
        }

        
    function run($address, $port
        { 
            
    if(($this->sock = socket_create(AF_INET, SOCK_STREAM, 0)) === false
            { 
                
    $this->error("failed to create socket: ".socket_strerror($this->sock)); 
            }
            
            
    $sock = $this->sock;
            
            
    if(($ret = socket_bind($sock, $address, $port)) === false
            { 
                
    $this->error("failed to bind socket: ".socket_strerror($ret));
            }

            
    if(($ret = socket_listen($sock, 0)) === false
            { 
                
    $this->error("failed to listen to socket: ".socket_strerror($ret));
            }

            socket_set_nonblock(
    $sock); 

            
    $this->log("waiting for clients to connect");

            
    while ($this->isListen) 
            { 
                
    $this->csock = @socket_accept($sock); 
                
    if ($this->csock === false
                { 
                    
    usleep(1000); //1ms
                } else if ($this->csock > 0) {
                    
    $this->client(); 
                } 
    else { 
                    
    $this->error("error: ".socket_strerror($this->csock));
                }
            }
        }

        
    /*
          * Handle a new client connection 
          
    */ 
        
    function client()
        { 
            
    $this->log('begin client');
            
    $ssock = $this->sock;
            
    $csock = $this->csock;
            
    $pid = pcntl_fork(); 
            
    if ($pid == -1
            {
                
    $this->error("fock clinet child error.");
            } 
    else if ($pid == 0)  {
                
    $pid = posix_getpid();
                
    $this->log("begin client child ($pid).");
                
    /* child process */ 
                
    $this->isListen = false;
                
    $this->log("close sock in child");
                socket_close(
    $ssock);
                
    $this->log("begin handle user logic.");
                
    $callback = $this->callback;
                
    call_user_func($callback, $csock, $this);
                
    $this->log("end handle user logic.");
                
    $this->log("close client sock in child.");
                socket_close(
    $csock);
                
    $this->log("end client");
            } 
    else  {
                
    $this->log("close csock in child");
                socket_close(
    $csock); 
            }
        }

        
    function __destruct()
        {
            @socket_close(
    $this->sock);
            @socket_close(
    $this->csock);
            
    $pid = posix_getpid();
            
    $this->log("end daemon in __destruct pid($pid).");
        }

        
    function getUserInfo()
        {
            
    $uid_name = posix_getpwnam($this->user);
            
    $this->uid = $uid_name['uid'];
            
    $this->gid = $uid_name['gid'];
            
    $this->userHome = $uid_name['dir'];
        }

        
    function changeIdentity() 
        {
            
    if(!posix_setuid($this->uid)) 
            { 
                
    $this->error("Unable to setuid to " . $this->uid); 
            }
        }

        
    /*
         * Signal handler 
         
    */ 
        
    function sigHandler($sig
        { 
            
    switch($sig
            { 
                
    case SIGTERM: 
                
    case SIGINT: 
                    
    exit();
                
    break

                
    case SIGCHLD: 
                    pcntl_waitpid(
    -1, $status); 
                
    break;
            }
        }

        
    function error($msg)
        {
            
    $str = date("Y-m-d H:i:s". " " . $msg . "\n";
            
    file_put_contents(dirname(__FILE__. "/error.log", $str, FILE_APPEND);
            
    exit(0);
        }

        
    function log($msg)
        {
            
    $str = date("Y-m-d H:i:s". " " . $msg . "\n";
            
    file_put_contents(dirname(__FILE__. "/message.log", $str, FILE_APPEND);
        }

        
    function daemon() 
        { 
            
    $ppid = posix_getpid();
            
    $this->log("begin parent daemon pid ($ppid)");
            
    $pid = pcntl_fork(); 
            
    if ($pid == -1
            {
                
    /* fork failed */ 
                
    $this->error("fork failure!"); 
            } 
    else if ($pid) { 
                
    /* close the parent */
                
    $this->log("end parent daemon pid($ppid) exit.");
                
    exit(); 
            } 
    else  { 
                
    /* child becomes our daemon */ 
                posix_setsid(); 
                
    chdir($this->userHome);
                
    umask(0);
                
    $pid = posix_getpid();
                
    $this->log("begin child daemon pid($pid).");
            }
        }
    }

    function clientHandle($msgsock, $obj)
    {
        
    /* Send instructions. */
        
    $br = "\r\n";
        
    $msg = "$br Welcome to the PHP Test Server. $br $br To quit, type 'quit'.$br";

        
    $obj->log($msg);
        socket_write(
    $msgsock, $msg, strlen($msg));
        
    $nbuf = '';
        socket_set_block(
    $msgsock);
        
    bcscale(4);  // defalult 4 eg. 1 + 2.00001 = 3
        do {
            
    if (false === ($nbuf = socket_read($msgsock, 2048, PHP_NORMAL_READ))) {
                
    $obj->error("socket_read() failed: reason: " . socket_strerror(socket_last_error($msgsock)));
            }

            
    if (!$nbuf = trim($nbuf)) {
                
    continue;
            }

            
    if ($nbuf == 'quit') {
                
    break;
            }
            
    if ($nbuf == 'shutdown') {
                
    break;
            }
            
            
    if (empty($nbuf)) continue;
            
            
    preg_match("/([\d.]+)[\s]*([+\-*\/x])[\s]*([\d.]+)/i", $nbuf , $matches);
            
    $op   = @$matches[2];
            
    $left = @$matches[1];
            
    $right = @$matches[3];

            
    $result = NULL;
            
    if ($op == "+") {
                
    $result = bcadd($left, $right);
            } 
    else if ($op == "-") {
                
    $result = bcsub($left, $right);
            } 
    else if ($op == "x" || $op == "x" || $op == "*") {
                
    $result = bcmul($left, $right);
            } 
    else if ($op == "/") {
                
    $result = bcdiv($left, $right);
            } 
    else {
                
    $talkback = "# error: expression \"$nbuf\" error.$br";
            }
            
    if ($result === NULL) {
                socket_write(
    $msgsock, $talkback, strlen($talkback));
            } 
    else {
                
    $result = rtrim($result, ".0");
                
    $talkback = "# result is : $result.$br";
                socket_write(
    $msgsock, $talkback, strlen($talkback));
            }
            
    $nbuf = '';
        } 
    while (true);
    }

    $server = new Simple_Server("clientHandle", "122.224.124.251");
    ?>
  • 相关阅读:
    【原】Sql2005 实现递归
    asp.net core api路由及多语言切换的实现
    一条语句实现查询各类别前10条记录
    【转】javascript操作cookies 以及 正确使用cookies的属性
    php 文件POST上传的原理及实现
    css3对background的调整与增强
    浅议javascript 中继承模式 (javascript高级程序设计)
    递归和递推:javascript求斐波那契数列的尾递归方法(转载)
    python中xrange和range的异同
    [译] JavaScript核心指南(JavaScript Core) 【转】
  • 原文地址:https://www.cnblogs.com/niniwzw/p/1575002.html
Copyright © 2011-2022 走看看