<?PHP
-
* แพทเซิร์ฟเวอร์
* คลาสฐานเซิร์ฟเวอร์ซ็อกเก็ต PHP
* เหตุการณ์ที่สามารถจัดการได้:
* * เปิดเริ่มต้น
* * บนเชื่อมต่อ
* * เมื่อการเชื่อมต่อถูกปฏิเสธ
* * เปิดปิด
* * เปิดปิดเครื่อง
* * onReceiveData
-
* @เวอร์ชั่น 1.1.1
* @ผู้เขียน Stephan Schmidt < [email protected] >
* @แพ็คเกจ patServer
-
คลาส patServer{
-
* ข้อมูลเกี่ยวกับโครงการ
* @var อาร์เรย์ $systemVars
-
var $systemVars = array(
"appName" => "patServer",
"appVersion" => "1.1",
"author" => array("Stephan Schmidt < [email protected] >", )
-
-
* พอร์ตเพื่อฟัง
* @var จำนวนเต็ม $port
-
var $พอร์ต = 10,000;
-
* โดเมนที่จะผูกไว้
* @var สตริง $domain
-
var $domain = "localhost";
-
* จำนวนลูกค้าสูงสุด
* @var จำนวนเต็ม $maxClients
-
var $maxClients = -1;
-
* ขนาดบัฟเฟอร์สำหรับ socket_read
* @var จำนวนเต็ม $readBufferSize
-
var $readBufferSize = 128;
-
* อักขระสิ้นสุดสำหรับ socket_read
* @var จำนวนเต็ม $readEndCharacter
-
var $readEndCharacter = "n";
-
* จำนวน Backlog สูงสุดในคิว
* @var จำนวนเต็ม $maxQueue
-
var $maxQueue = 500;
-
* โหมดแก้ไขข้อบกพร่อง
* @var บูลีน $debug
-
var $debug = จริง;
-
* โหมดแก้ไขข้อบกพร่อง
* @var สตริง $debugMode
-
var $debugMode = "ข้อความ";
-
* ปลายทางการดีบัก (ชื่อไฟล์หรือ stdout)
* @var string $debugDest
-
var $debugDest = "stdout";
-
* อาร์เรย์ว่าง ใช้สำหรับ socket_select
* @var array $null
-
var $null = อาร์เรย์();
-
* file descriptor ทั้งหมดถูกเก็บไว้ที่นี่
* @var อาร์เรย์ $clientFD
-
var $clientFD = อาร์เรย์();
-
* จำเป็นในการจัดเก็บข้อมูลลูกค้า
* @var array $clientInfo
-
var $clientInfo = array();
-
* จำเป็นในการจัดเก็บข้อมูลเซิร์ฟเวอร์
* @var อาร์เรย์ $serverInfo
-
var $serverInfo = array();
-
* จำนวนลูกค้า
* @var จำนวนเต็ม $clients
-
var $ลูกค้า = 0;
-
* สร้างเซิร์ฟเวอร์ซ็อกเก็ตใหม่
-
* @เข้าถึงสาธารณะ
* @param string $domain โดเมนที่จะผูกไว้
* @param จำนวนเต็ม $port พอร์ตที่จะฟัง
-
ฟังก์ชั่น patServer( $domain = "localhost", $port = 10,000 ){
$นี่->โดเมน = $โดเมน;
$นี่->พอร์ต = $พอร์ต;
$this->serverInfo["domain"] = $domain;
$this->serverInfo["พอร์ต"] = $พอร์ต;
$this->serverInfo["servername"] = $this->systemVars["appName"];
$this->serverInfo["serverversion"] = $this->systemVars["appVersion"];
set_time_limit ( 0 );
}
/**
* กำหนดจำนวนการเชื่อมต่อพร้อมกันสูงสุด
-
* @เข้าถึงสาธารณะ
* @param int $maxClients
-
ฟังก์ชั่น setMaxClients( $maxClients ){
$this->maxClients = $maxClients;
}
/**
* ตั้งค่าโหมดแก้ไขข้อบกพร่อง
-
* @เข้าถึงสาธารณะ
* @param ผสม $debug [ข้อความ | htmlfalse]
* @param string $dest ปลายทางของข้อความดีบัก (stdout ไปยังเอาต์พุตหรือชื่อไฟล์หากควรเขียนบันทึก)
-
ฟังก์ชั่น setDebugMode( $debug, $dest = "stdout" ){
ถ้า( $debug === false ){
$นี่ -> ดีบัก = เท็จ;
กลับเป็นจริง;
}
$this->debug = true;
$this->debugMode = $debug;
$this->debugDest = $dest;
}
/**
* เริ่มเซิร์ฟเวอร์
-
* @เข้าถึงสาธารณะ
* @param int $maxClients
-
ฟังก์ชั่นเริ่มต้น () {
$this->initFD = @socket_create( AF_INET, SOCK_STREAM, 0 );
ถ้า( !$this->initFD )
die( "patServer: ไม่สามารถสร้างซ็อกเก็ตได้" );
// ที่อยู่อาจนำมาใช้ซ้ำได้
socket_setopt( $นี่->initFD, SOL_SOCKET, SO_REUSEADDR, 1 );
// ผูกซ็อกเก็ต
if( !@socket_bind ( $this->initFD, $this->โดเมน, $this->พอร์ต ) ){
@socket_close( $นี่->initFD );
die( "patServer: ไม่สามารถผูกซ็อกเก็ตกับ ".$this->domain." บนพอร์ต ".$this->port" ( ".$this->getLastSocketError( $this->initFd )." )." );
}
// ฟังบนพอร์ตที่เลือก
ถ้า( !@socket_listen ( $this->initFD, $this->maxQueue ) )
die( "patServer: ไม่สามารถฟังได้ ( ".$this->getLastSocketError( $this->initFd )." )." );
$this->sendDebugMessage( "Listening on port ".$this->port.". Server start at ".date( "H:i:s", time() ) );
// ช่วยให้ฟังก์ชันปิดเครื่องสามารถตรวจสอบว่าเซิร์ฟเวอร์ปิดตัวลงแล้วหรือไม่
$GLOBALS["_patServerStatus"] = "กำลังทำงานอยู่";
// สิ่งนี้ทำให้แน่ใจได้ว่าเซิร์ฟเวอร์จะถูกปิดอย่างถูกต้อง
register_shutdown_function( อาร์เรย์( $นี่, "ปิดเครื่อง" ) );
ถ้า( method_exists( $this, "onStart" ) )
$นี่->onStart();
$this->serverInfo["started"] = เวลา();
$this->serverInfo["status"] = "กำลังทำงานอยู่";
ในขณะที่ (จริง) {
$readFDs = อาร์เรย์();
array_push( $readFDs, $this->initFD );
// ดึงข้อมูลไคลเอนต์ทั้งหมดที่กำลังรอการเชื่อมต่อ
สำหรับ( $i = 0; $i < นับ( $this->clientFD ); $i++ )
ถ้า( isset( $this->clientFD[$i] ) )
array_push( $readFDs, $this->clientFD[$i] );
// บล็อกและรอข้อมูลหรือการเชื่อมต่อใหม่
$ready = @socket_select( $readFDs, $this->null, $this->null, NULL );
ถ้า( $พร้อม === เท็จ ){
$this->sendDebugMessage( "socket_select failed." );
$นี่->ปิดเครื่อง();
}
// ตรวจสอบการเชื่อมต่อใหม่
if( in_array( $this->initFD, $readFDs ) ){
$newClient = $this->acceptConnection( $this->initFD );
// ตรวจสอบจำนวนการเชื่อมต่อสูงสุด
ถ้า( $นี่->maxClients > 0 ){
ถ้า( $this->ลูกค้า > $this->maxClients ){
$this->sendDebugMessage( "มีการเชื่อมต่อมากเกินไป" );
ถ้า( method_exists( $this, "onConnectionRefused" ) )
$this->onConnectionRefused( $newClient );
$this->closeConnection( $newClient );
-
}
ถ้า( --$พร้อม <= 0 )
ดำเนินการต่อ;
}
// ตรวจสอบไคลเอ็นต์ทั้งหมดเพื่อดูข้อมูลขาเข้า
สำหรับ( $i = 0; $i <นับ( $this->clientFD ); $i++ ){
ถ้า( !isset( $this->clientFD[$i] ) )
ดำเนินการต่อ;
if( in_array( $this->clientFD[$i], $readFDs ) ){
$data = $this->readFromSocket( $i );
// ข้อมูลว่าง => การเชื่อมต่อถูกปิด
ถ้า( !$ข้อมูล ){
$this->sendDebugMessage( "การเชื่อมต่อถูกปิดโดยเพียร์" );
$นี่->ปิดการเชื่อมต่อ( $i );
}อื่น{
$this->sendDebugMessage( "ได้รับแล้ว ".trim( $data )." จาก ".$i );
ถ้า( method_exists( $this, "onReceiveData" ) )
$this->onReceiveData( $i, $data );
-
-
-
-
}
/**
* อ่านจากซ็อกเก็ต
-
* @ เข้าถึงส่วนตัว
* @param จำนวนเต็ม $clientId รหัสภายในของไคลเอ็นต์ที่จะอ่าน
* @return string ข้อมูล $data ที่ถูกอ่าน
-
ฟังก์ชั่น readFromSocket( $clientId ){
// เริ่มต้นด้วยสตริงว่าง
$ข้อมูล = "";
// อ่านข้อมูลจากซ็อกเก็ต
ในขณะที่( $buf = socket_read( $this->clientFD[$clientId], $this->readBufferSize ) ){
$data .= $buf;
$endString = substr( $buf, - strlen( $this->readEndCharacter ) );
ถ้า( $endString == $this->readEndCharacter )
หยุดพัก;
ถ้า( $buf == โมฆะ )
หยุดพัก;
}
ถ้า( $buf === false )
$this->sendDebugMessage( "ไม่สามารถอ่านจากไคลเอนต์ ".$clientId." ( ".$this->getLastSocketError( $this->clientFD[$clientId] )." )" );
ส่งคืนข้อมูล $;
}
/**
* ยอมรับการเชื่อมต่อใหม่
-
* @เข้าถึงสาธารณะ
* @param resources &$socket socket ที่ได้รับการเชื่อมต่อใหม่
* @return int $clientID ID ภายในของไคลเอ็นต์
-
ฟังก์ชั่นยอมรับการเชื่อมต่อ( &$ซ็อกเก็ต){
สำหรับ( $i = 0 ; $i <= นับ( $this->clientFD ); $i++ ){
if( !isset( $this->clientFD[$i] ) || $this->clientFD[$i] == NULL ){
$this->clientFD[$i] = socket_accept( $socket );
socket_setopt( $this->clientFD[$i], SOL_SOCKET, SO_REUSEADDR, 1 );
$peer_host = "";
$peer_port = "";
socket_getpeername( $this->clientFD[$i], $peer_host, $peer_port );
$this->clientInfo[$i] = array(
"โฮสต์" => $peer_host,
"พอร์ต" => $peer_port,
"connectOn" => เวลา ()
-
$นี่->ลูกค้า++;
$this->sendDebugMessage( "การเชื่อมต่อใหม่ ( ".$i." ) จาก ".$peer_host." บนพอร์ต ".$peer_port );
ถ้า( method_exists( $this, "onConnect" ) )
$นี่->onConnect( $i );
ส่งคืน $i;
-
-
}
/**
* ตรวจสอบว่าไคลเอนต์ยังคงเชื่อมต่ออยู่หรือไม่
-
* @เข้าถึงสาธารณะ
* @param จำนวนเต็ม $id รหัสลูกค้า
* @return boolean $connected true หากไคลเอนต์เชื่อมต่ออยู่ หากเป็นอย่างอื่นจะเป็น false
-
ฟังก์ชั่นเชื่อมต่อแล้ว( $id ){
ถ้า( !isset( $this->clientFD[$id] ) )
กลับเท็จ;
กลับเป็นจริง;
}
/**
* ปิดการเชื่อมต่อไปยังลูกค้า
-
* @เข้าถึงสาธารณะ
* @param int $clientID ID ภายในของไคลเอ็นต์
-
ฟังก์ชั่น closeConnection( $id ){
ถ้า( !isset( $this->clientFD[$id] ) )
กลับเท็จ;
ถ้า( method_exists( $this, "onClose" ) )
$นี่->onClose( $id );
$this->sendDebugMessage( "การเชื่อมต่อแบบปิด ( ".$id." ) จาก ".$this->clientInfo[$id]["host"]." บนพอร์ต ".$this->clientInfo[$id][ "ท่าเรือ"] );
@socket_close( $this->clientFD[$id] );
$this->clientFD[$id] = NULL;
unset( $this->clientInfo[$id] );
$นี่->ลูกค้า--;
}
/**
* ปิดเซิร์ฟเวอร์
-
* @เข้าถึงสาธารณะ
-
ฟังก์ชั่นปิดเครื่อง(){
ถ้า( $GLOBALS["_patServerStatus"] != "กำลังทำงาน" )
ออก;
$GLOBALS["_patServerStatus"] = "หยุดแล้ว";
ถ้า( method_exists( $this, "onShutdown" ) )
$นี่->onShutdown();
$maxFD = จำนวน( $this->clientFD );
สำหรับ( $i = 0; $i < $maxFD; $i++ )
$นี่->ปิดการเชื่อมต่อ( $i );
@socket_close( $นี่->initFD );
$this->sendDebugMessage( "ปิดเซิร์ฟเวอร์" );
ออก;
}
/**
* รับจำนวนลูกค้าปัจจุบัน
-
* @เข้าถึงสาธารณะ
* @return int $client จำนวนลูกค้า
-
ฟังก์ชัน getClients(){
ส่งคืนลูกค้า $this->;
}
/**
* ส่งข้อมูลให้กับลูกค้า
-
* @เข้าถึงสาธารณะ
* @param int $clientId ID ของไคลเอ็นต์
* @param string $data ข้อมูลที่จะส่ง
* @param boolean $debugData ตั้งค่าสถานะเพื่อระบุว่าข้อมูลที่เขียนไปยังซ็อกเก็ตควรถูกส่งเป็นข้อความดีบักด้วยหรือไม่
-
ฟังก์ชั่น sendData( $clientId, $data, $debugData = true ){
if( !isset( $this->clientFD[$clientId] ) || $this->clientFD[$clientId] == NULL )
กลับเท็จ;
ถ้า( $debugData )
$this->sendDebugMessage( "sending: "" . $data . "" ถึง: $clientId" );
ถ้า( !@socket_write ( $this->clientFD[$clientId], $data ) )
$this->sendDebugMessage( "ไม่สามารถเขียน '".$data."' ไคลเอนต์ ".$clientId" ( ".$this->getLastSocketError( $this->clientFD[$clientId] )." )." ) ;
}
/**
* ส่งข้อมูลให้กับลูกค้าทั้งหมด
-
* @เข้าถึงสาธารณะ
* @param string $data ข้อมูลที่จะส่ง
* @param array $exclude รหัสไคลเอ็นต์ที่จะยกเว้น
-
ฟังก์ชั่น BroadcastData( $data, $exclude = array(), $debugData = true ){
ถ้า( !empty( $ไม่รวม ) && !is_array( $ไม่รวม ) )
$ไม่รวม = อาร์เรย์( $ไม่รวม );
สำหรับ( $i = 0; $i <นับ( $this->clientFD ); $i++ ){
if( isset( $this->clientFD[$i] ) && $this->clientFD[$i] != NULL && !in_array( $i, $ไม่รวม ) ){
ถ้า( $debugData )
$this->sendDebugMessage( "ส่ง: "" . $data . "" ถึง: $i" );
ถ้า( !@socket_write ( $this->clientFD[$i], $data ) )
$this->sendDebugMessage( "ไม่สามารถเขียน '".$data."' ลูกค้า ".$i." ( ".$this->getLastSocketError( $this->clientFD[$i] )." )." ) ;
-
-
}
/**
* รับข้อมูลปัจจุบันเกี่ยวกับลูกค้า
-
* @เข้าถึงสาธารณะ
* @param int $clientId ID ของไคลเอ็นต์
* @return array $info ข้อมูลเกี่ยวกับไคลเอนต์
-
ฟังก์ชั่น getClientInfo( $clientId ){
if( !isset( $this->clientFD[$clientId] ) || $this->clientFD[$clientId] == NULL )
กลับเท็จ;
ส่งคืน $this->clientInfo[$clientId];
}
/**
* ส่งข้อความแก้ไขข้อบกพร่อง
-
* @ เข้าถึงส่วนตัว
* @param string $msg ข้อความที่ต้องการแก้ไข
-
ฟังก์ชั่น sendDebugMessage( $msg ){
ถ้า( !$this->debug )
กลับเท็จ;
$msg = date( "Ymd H:i:s", time() ) . - $ข้อความ;
สวิตช์( $this->debugMode ){
กรณี "ข้อความ":
$msg = $msg"n";
หยุดพัก;
กรณี "html":
$msg = htmlอักขระพิเศษ( $msg ) . "<br />n";
หยุดพัก;
}
if( $this->debugDest == "stdout" || ว่างเปล่า( $this->debugDest ) ){
สะท้อน $msg;
ล้าง();
กลับเป็นจริง;
}
error_log( $msg, 3, $this->debugDest );
กลับเป็นจริง;
}
/**
* ส่งคืนสตริงสำหรับข้อผิดพลาดซ็อกเก็ตครั้งล่าสุด
-
* @เข้าถึงสาธารณะ
* @return string $error ข้อผิดพลาดครั้งล่าสุด
-
ฟังก์ชั่น getLastSocketError( &$fd ){
$lastError = socket_last_error( $fd );
กลับ "ข้อความ:" . socket_strerror( $lastError ) . " / รหัส: ".$lastError;
-
ฟังก์ชั่น onReceiveData($ip,$data){
$this->broadcastData( $data,array(), true );
-
-
$patServer = patServer ใหม่();
$patServer->เริ่มต้น();
-