1. 程式人生 > >多程序監聽同一個埠 php

多程序監聽同一個埠 php

最近在看nginx設計原理時思考到兩個問題,便是:

  • 多個程序能否監聽同個埠?
  • 單個程序能否監聽多個埠?

當然隨著學習的深入,答案均是肯定的,在這個過程中筆者為了驗證,用php寫了兩個例子,在這裡分享出來,供有需要的php同學學習跟理解。

在分享例子之前,需要先介紹兩個php在socket程式設計中常用的擴充套件,pcntl和libevent:

1. pcntl

php本身並不支援多程序,但通過擴充套件pcntl便可以實現fork功能,fork程式設計的大概原理是,每次呼叫fork函式,作業系統就會產生一個子程序,兒子程序所有的堆疊資訊都是原封不動複製父程序的,而在fork之後,父程序與子程序實際上是相互獨立的,父子程序不會相互影響。也就是說,fork呼叫位置之前的所有變數,父程序和子程序是一樣的,但fork之後則取決於各自的動作,且資料也是獨立的;因為資料已經完整的複製給了子程序。而唯一能夠區分父子程序的方法就是判斷fork的返回值。如果為0,表示是子程序,如果為正數,表示為父程序,且該正數為子程序的PID(程序號),而如果是-1,表示子程序建立失敗。

2. libevent

linux網路程式設計中有三大事件處理,IO(socket)、訊號和定時器,理解並處理好這三者,linux網路程式設計就理解了一半,而libevent則是對這三者處理提供了一個很好的封裝,大大簡化了socket程式設計中事件處理的難度,非常推薦對這塊感興趣的同學去深入學習。

一、多個程序監聽同個埠

<?php

/**
* 
*/
class Server
{
    protected $ip = '127.0.0.1';
    protected $port = 5000;
    protected $sock = null;

    public function
main()
{ if(($this->sock = socket_create(AF_INET,SOCK_STREAM,SOL_TCP)) < 0) { echo "socket_create() 失敗的原因是:".socket_strerror($sock)."\n"; return ; } if(($ret = socket_bind($this->sock,$this->ip,$this->port)) < 0) { echo
"socket_bind() 失敗的原因是:".socket_strerror($ret)."\n"; return ; } if(($ret = socket_listen($this->sock,4)) < 0) { echo "socket_listen() 失敗的原因是:".socket_strerror($ret)."\n"; return ; } for ($i=0; $i<3; $i++) { $pid = pcntl_fork(); if (-1 === $pid) { throw new Exception("fork fail"); } elseif (0 === $pid) { echo "fork pid:".getmypid()."\n"; while (1) { if(($msgsock = socket_accept($this->sock)) < 0) { echo "socket_accept() failed: reason: " . socket_strerror($msgsock) . " ,pid: ".getmypid()."\n"; break; }else{ $msg ="測試成功 ! \n"; echo $msg."pid: ".getmypid()."\n"; socket_write($msgsock, $msg, strlen($msg)); } } } } while(1) { $status = 0; $pid = pcntl_wait($status,WUNTRACED); if($pid > 0) { echo "pid:$pid exit,status:$status"; } } } } $server = new Server(); $server->main();

二、單個程序監聽多個埠

<?php

/**
* 
*/
class Server
{
    protected $socks = array();
    protected $event_base = null;
    protected $events = array();
    public function __construct()
    {
        $this->event_base = event_base_new();
    }

    protected function acceptConnect($sock)
    {
        echo "acceptConnect pid:".getmypid()."\n";
        //sleep(5);
        if(($msgsock = socket_accept($sock)) < 0) {
            echo "socket_accept() failed: reason: " . socket_strerror($msgsock) . " ,pid: ".getmypid()."\n";
            //break;
        }else{
            $msg ="測試成功,sock:$sock ! \n";
            echo $msg."pid: ".getmypid()."\n";
            socket_write($msgsock, $msg, strlen($msg));
            socket_close($msgsock);
        }
    }

    protected function addEvent($sock,$callback)
    {
        $event = event_new();

        if (!event_set($event, $sock, EV_READ|EV_PERSIST, $callback, null)) {
            echo "event_set faild,pid:".getmypid()."\n";
            return ;
        }

         if (!event_base_set($event,$this->event_base)) {
            echo "event_base_set faild,pid:".getmypid()."\n";
            return ;
        }

        if (!event_add($event)) {
            echo "event_add faild,pid:".getmypid()."\n";
            return ;
        }

        $this->event[] = $event;
    }

    public function listen($ip = '127.0.0.1',$port = '5000')
    {
        if(($sock = socket_create(AF_INET,SOCK_STREAM,SOL_TCP)) < 0) {
            echo "socket_create() 失敗的原因是:".socket_strerror($sock)."\n";
            return ;
        }

        if(($ret = socket_bind($sock,$ip,$port)) < 0) {
            echo "socket_bind() 失敗的原因是:".socket_strerror($ret)."\n";
            return ;
        }

        if(($ret = socket_listen($sock,4)) < 0) {
            echo "socket_listen() 失敗的原因是:".socket_strerror($ret)."\n";
            return ;
        }

        $this->socks[] = $sock;
    }

    public function main()
    {
        if($this->event_base == null)
        {
            echo "event base null";
            return ;
        }
        echo "event base:".$this->event_base."\n";
        foreach ($this->socks as $sock) {
            echo "sock:$sock\n";
            $this->addEvent($sock,array($this,'acceptConnect'));
        }
        echo "libevent success,pid:".getmypid()."\n";
        $result = event_base_loop($this->event_base);        
        echo "event loop result:$result";
    }

}

$server = new Server();
$server->listen('127.0.0.1','5000');
$server->listen('127.0.0.1','5001');
$server->main();