文章詳情頁
在php上使用fork以及socket的sample
瀏覽:2日期:2024-02-21 09:05:37
最近剛好遇到一個(gè)頭大的問題寫了這個(gè)code讓大家參考一下吧家裏的無線AP功能不太好,他只提供把外部真實(shí)IP map 到 Nat裡面的某個(gè)IP不能指定某個(gè)port map到某個(gè)內(nèi)部IP的Port可是我已經(jīng)把外部的IP Map到內(nèi)部的Linux Server上,但是我又想從外部使用VNC連到內(nèi)部的一臺(tái)Windows電腦。所以就寫了這個(gè)程式原理是這樣這個(gè)程式會(huì)在Linux Server上開一個(gè)Port作Listen的動(dòng)作當(dāng)外部連到這個(gè)Port時(shí),程式會(huì)再開啟另一個(gè)連線連到內(nèi)部Windows的VNC上把外部的封包原封不動(dòng)的丟到VNC的連線上,然後把VNC連線傳回的資料原封不動(dòng)的再丟回外部的Port程式碼:#!/usr/bin/php -q<?php$IP='192.168.1.1'//Windows電腦的IP$Port='5900'//VNC使用的Port$ServerPort='9999'//Linux Server對(duì)外使用的Port$RemoteSocket=false//連線到VNC的SocketfunctionSignalFunction($Signal){//這是主Process的訊息處理函數(shù)global$PID//Child Process的PIDswitch ($Signal){caseSIGTRAP:caseSIGTERM://收到結(jié)束程式的Signalif($PID){//送一個(gè)SIGTERM的訊號(hào)給Child告訴他趕快結(jié)束掉嘍posix_kill($PID,SIGTERM);//等待Child Process結(jié)束,避免zombiepcntl_wait($Status);}//關(guān)閉主Process開啟的SocketDestroySocket();exit(0)//結(jié)束主Processbreak;caseSIGCHLD:/*當(dāng)Child Process結(jié)束掉時(shí),Child會(huì)送一個(gè)SIGCHLD訊號(hào)給Parrent當(dāng)Parrent收到SIGCHLD,就知道Child Process已經(jīng)結(jié)束嘍 ,該做一些結(jié)束的動(dòng)作*/unset($PID)//將$PID清空,表示Child Process已經(jīng)結(jié)束pcntl_wait($Status)//避免Zombiebreak;default:}}functionChildSignalFunction($Signal){//這是Child Process的訊息處理函數(shù)switch ($Signal){caseSIGTRAP:caseSIGTERM://Child Process收到結(jié)束的訊息DestroySocket()//關(guān)閉Socketexit(0)//結(jié)束Child Processdefault:}}functionProcessSocket($ConnectedServerSocket){//Child Process Socket處理函數(shù)//$ConnectedServerSocket -> 外部連進(jìn)來的Socketglobal$ServerSocket,$RemoteSocket,$IP,$Port$ServerSocket=$ConnectedServerSocketdeclare(ticks = 1)//這一行一定要加,不然沒辦法設(shè)定訊息處理函數(shù)。//設(shè)定訊息處理函數(shù)if(!pcntl_signal(SIGTERM, 'ChildSignalFunction')) return;if(!pcntl_signal(SIGTRAP, 'ChildSignalFunction')) return;//建立一個(gè)連線到VNC的Socket$RemoteSocket=socket_create(AF_INET, SOCK_STREAM,SOL_TCP);//連線到內(nèi)部的VNC@$RemoteConnected=socket_connect($RemoteSocket,$IP,$Port);if(!$RemoteConnected) return; //無法連線到VNC 結(jié)束//將Socket的處理設(shè)為Nonblock,避免程式被Block住if(!socket_set_nonblock($RemoteSocket)) return;if(!socket_set_nonblock($ServerSocket)) return;while(true){//這邊我們採用pooling的方式去取得資料$NoRecvData=false//這個(gè)變數(shù)用來判別外部的連線是否有讀到資料$NoRemoteRecvData=false//這個(gè)變數(shù)用來判別VNC連線是否有讀到資料@$RecvData=socket_read($ServerSocket,4096,PHP_BINARY_READ);//從外部連線讀取4096 bytes的資料@$RemoteRecvData=socket_read($RemoteSocket,4096,PHP_BINARY_READ);//從vnc連線連線讀取4096 bytes的資料if($RemoteRecvData===''){//VNC連線中斷,該結(jié)束嘍echo'Remote Connection Closen'return;;;;}if($RemoteRecvData===false){/*由於我們是採用nonblobk模式這裡的情況就是vnc連線沒有可供讀取的資料*/$NoRemoteRecvData=true//清除掉Last Errrorsocket_clear_error($RemoteSocket);}if($RecvData===''){//外部連線中斷,該結(jié)束嘍echo'Client Connection Closen'return;}if($RecvData===false){/*由於我們是採用nonblobk模式這裡的情況就是外部連線沒有可供讀取的資料*/$NoRecvData=true//清除掉Last Errrorsocket_clear_error($ServerSocket);}if($NoRecvData&&$NoRemoteRecvData){//如果外部連線以及VNC連線都沒有資料可以讀取時(shí),//就讓程式睡個(gè)0.1秒,避免長(zhǎng)期佔(zhàn)用CPU資源usleep(100000);//睡醒後,繼續(xù)作pooling的動(dòng)作讀取socketcontinue;}//Recv Dataif(!$NoRecvData){//外部連線讀取到資料while(true){//把外部連線讀到的資料,轉(zhuǎn)送到VNC連線上@$WriteLen=socket_write($RemoteSocket,$RecvData);if($WriteLen===false){//由於網(wǎng)路傳輸?shù)膯栴},目前暫時(shí)無法寫入資料//先睡個(gè)0.1秒再繼續(xù)嘗試。usleep(100000);continue;}if($WriteLen===0){//遠(yuǎn)端連線中斷,程式該結(jié)束了echo'Remote Write Connection Closen'return;}//從外部連線讀取的資料,已經(jīng)完全送給VNC連線時(shí),中斷這個(gè)迴圈。if($WriteLen==strlen($RecvData)) break;//如果資料一次送不完就得拆成好幾次傳送,直到所有的資料全部送出為止$RecvData=substr($RecvData,$WriteLen);}}if(!$NoRemoteRecvData){//這邊是從VNC連線讀取到的資料,再轉(zhuǎn)送回外部的連線//原理跟上面差不多不再贅述while(true){@$WriteLen=socket_write($ServerSocket,$RemoteRecvData);if($WriteLen===false){usleep(100000);continue;}if($WriteLen===0){echo'Remote Write Connection Closen'return;}if($WriteLen==strlen($RemoteRecvData)) break;$RemoteRecvData=substr($RemoteRecvData,$WriteLen);}}}}functionDestroySocket(){//用來關(guān)閉已經(jīng)開啟的Socketglobal$ServerSocket,$RemoteSocketif($RemoteSocket){//如果已經(jīng)開啟VNC連線//在Close Socket前必須將Socket shutdown不然對(duì)方不知到你已經(jīng)關(guān)閉連線了@socket_shutdown($RemoteSocket,2);socket_clear_error($RemoteSocket);//關(guān)閉Socketsocket_close($RemoteSocket);;;;}//關(guān)閉外部的連線@socket_shutdown($ServerSocket,2);socket_clear_error($ServerSocket);socket_close($ServerSocket);}//這裡是整個(gè)程式的開頭,程式從這邊開始執(zhí)行//這裡首先執(zhí)行一次fork$PID=pcntl_fork();if($PID==-1) die('could not fork');//如果$PID不為0表示這是Parrent Process//$PID就是Child Process//這是Parrent Process 自己結(jié)束掉,讓Child成為一個(gè)Daemon。if($PID) die('Daemon PID:$PIDn');//從這邊開始,就是Daemon模式在執(zhí)行了//將目前的Process跟終端機(jī)脫離成為daemon模式if(!posix_setsid()) die('could not detach from terminaln');//設(shè)定daemon 的訊息處理函數(shù)declare(ticks = 1);if(!pcntl_signal(SIGTERM, 'SignalFunction')) die('Error!!!n');if(!pcntl_signal(SIGTRAP, 'SignalFunction')) die('Error!!!n');if(!pcntl_signal(SIGCHLD, 'SignalFunction')) die('Error!!!n');//建立外部連線的Socket$ServerSocket=socket_create(AF_INET, SOCK_STREAM,SOL_TCP);//設(shè)定外部連線監(jiān)聽的IP以及Port,IP欄位設(shè)0,表示經(jīng)聽所有介面的IPif(!socket_bind($ServerSocket,0,$ServerPort)) die('Cannot Bind Socket!n');//開始監(jiān)聽Portif(!socket_listen($ServerSocket)) die('Cannot Listen!n');//將Socket設(shè)為nonblock模式if(!socket_set_nonblock($ServerSocket)) die('Cannot Set Server Socket to Block!n');//清空$PID變數(shù),表示目前沒有任何的Child Processunset($PID);while(true){//進(jìn)入pooling模式,每隔1秒鐘就去檢查有沒有連線進(jìn)來。sleep(1);//檢查有沒有連線進(jìn)來@$ConnectedServerSocket=socket_accept($ServerSocket);if($ConnectedServerSocket!==false){//有人連進(jìn)來嘍//起始一個(gè)Child Process用來處理連線$PID=pcntl_fork();if($PID==-1) die('could not fork');if($PID) continue;//這是daemon process,繼續(xù)回去監(jiān)聽。//這裡是Child Process開始//執(zhí)行Socket裡函數(shù)ProcessSocket($ConnectedServerSocket);//處理完Socket後,結(jié)束掉SocketDestroySocket();//結(jié)束Child Processexit(0);}}?>
標(biāo)簽:
PHP
排行榜
