用 Socket 和 Pcntl 实现一个多进程服务器(一)

用 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 " );
?>
posted @ 2009-09-27 15:01 暮夏 阅读( ...) 评论( ...) 编辑 收藏
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值