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