多程序監聽同一個埠 php
阿新 • • 發佈:2019-01-03
最近在看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();