<?PHP
/**
* باتسيرفر
* فئة قاعدة خادم مقبس PHP
* الأحداث التي يمكن التعامل معها:
* * عند البدء
* * على الاتصال
* * تم رفض الاتصال
* * عند الإغلاق
* * عند إيقاف التشغيل
* * onReceiveData
*
* @الإصدار 1.1
* @author ستيفان شميدت < [email protected] >
* @package باتسيرفر
*/
فئة باتسيرفر {
/**
* معلومات عن المشروع
* @var array $systemVars
*/
var $systemVars = array(
"appName" => "patServer",
"appVersion" => "1.1"،
"author" => array("ستيفان شميدت < [email protected] >"، )
);
/**
* منفذ للاستماع
* @var عدد صحيح $port
*/
فار $port = 10000؛
/**
* المجال المراد الارتباط به
* @var string $domain
*/
فار $domain = "localhost";
/**
* الحد الأقصى لعدد العملاء
* @var عدد صحيح $maxClients
*/
var $maxClients = -1;
/**
* حجم المخزن المؤقت لـsocket_read
* @var عدد صحيح $readBufferSize
*/
فار $readBufferSize = 128;
/**
* الحرف النهائي لـ المقبس_القراءة
* @var integer $readEndCharacter
*/
var $readEndCharacter = "n";
/**
* الحد الأقصى للتراكم في قائمة الانتظار
* @var عدد صحيح $maxQueue
*/
فار $maxQueue = 500;
/**
* وضع التصحيح
* @var boolean $debug
*/
var $debug = true;
/**
* وضع التصحيح
* @var string $debugMode
*/
var $debugMode = "text";
/**
* وجهة التصحيح (اسم الملف أو stdout)
* @var string $debugDest
*/
var $debugDest = "stdout";
/**
* مصفوفة فارغة، تستخدم لـsocket_select
* @var array $null
*/
فار $null = array();
/**
* يتم تخزين جميع واصفات الملفات هنا
* @var array $clientFD
*/
var $clientFD = array();
/**
* مطلوب لتخزين معلومات العميل
* @var array $clientInfo
*/
var $clientInfo = array();
/**
* مطلوب لتخزين معلومات الخادم
* @var array $serverInfo
*/
var $serverInfo = array();
/**
* عدد العملاء
* @var عدد صحيح $clients
*/
فار $ العملاء = 0؛
/**
* إنشاء خادم مأخذ توصيل جديد
*
* @الوصول العام
* @param string $domain domain للربط به
* @param عدد صحيح منفذ $port للاستماع إليه
*/
الدالة patServer( $domain = "localhost", $port = 10000 ){
$this->domain = $domain;
$this->port = $port;
$this->serverInfo["domain"] = $domain;
$this->serverInfo["port"] = $port;
$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" ){
إذا(تصحيح $ === خطأ){
$this->debug = false;
عودة صحيحة؛
}
$this->debug = true;
$this->debugMode = $debug;
$this->debugDest = $dest;
}
/**
* بدء تشغيل الخادم
*
* @الوصول العام
* @param int $maxClients
*/
بداية الوظيفة (){
$this->initFD = @socket_create( AF_INET, SOCK_STREAM, 0 );
إذا( !$هذا->initFD )
die("patServer: تعذر إنشاء مأخذ توصيل.");
// يمكن إعادة استخدام العنوان
المقبس_setopt( $this->initFD, SOL_SOCKET, SO_REUSEADDR, 1 );
// ربط المقبس
إذا( !@socket_bind ( $this->initFD, $this->domain, $this->port ) ){
@socket_ Close( $this->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( "الاستماع على المنفذ ".$this->port.". بدأ الخادم في ".date( "H:i:s", time() ) );
// هذا يسمح لوظيفة إيقاف التشغيل بالتحقق مما إذا كان الخادم مغلقًا بالفعل
$GLOBALS["_patServerStatus"] = "قيد التشغيل";
// هذا يضمن إيقاف تشغيل الخادم بشكل صحيح
Register_shutdown_function( array( $this, "shutdown" ) );
إذا (method_exists( $this، "onStart" ) )
$this->onStart();
$this->serverInfo["started"] = time();
$this->serverInfo["status"] = "running";
بينما (صحيح){
$readFDs = array();
array_push( $readFDs, $this->initFD );
// جلب كافة العملاء الذين ينتظرون الاتصالات
for( $i = 0; $i < count( $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." );
$this->shutdown();
}
// التحقق من وجود اتصال جديد
إذا (in_array( $this->initFD, $readFDs ) ){
$newClient = $this->acceptConnection( $this->initFD );
// التحقق من الحد الأقصى لعدد الاتصالات
إذا( $this->maxClients > 0 ){
إذا( $this->العملاء > $this->maxClients ){
$this->sendDebugMessage( "عدد كبير جدًا من الاتصالات." );
إذا (method_exists( $this، "onConnectionRefused" ) )
$this->onConnectionRefused( $newClient );
$this->إغلاقConnection( $newClient );
}
}
إذا (-$جاهز <= 0)
يكمل؛
}
// التحقق من جميع العملاء بحثًا عن البيانات الواردة
for( $i = 0; $i < count( $this->clientFD ); $i++ ){
إذا ( !isset( $this->clientFD[$i] ) )
يكمل؛
إذا (in_array( $this->clientFD[$i], $readFDs ) ){
$data = $this->readFromSocket( $i);
// بيانات فارغة => تم إغلاق الاتصال
إذا(!$بيانات){
$this->sendDebugMessage( "تم إغلاق الاتصال بواسطة النظير" );
$this->CloseConnection( $i);
}آخر{
$this->sendDebugMessage( "تم الاستلام ".trim( $data )." من ".$i );
إذا (method_exists( $this، "onReceiveData" ) )
$this->onReceiveData( $i, $data );
}
}
}
}
}
/**
* القراءة من المقبس
*
* @الوصول خاص
*param integer $clientId المعرف الداخلي للعميل الذي سيتم القراءة منه
*return سلسلة $بيانات البيانات التي تمت قراءتها
*/
وظيفة readFromSocket( $clientId ){
// ابدأ بسلسلة فارغة
بيانات $ = "";
// قراءة البيانات من المقبس
بينما( $buf = مقبس_قراءة( $this->clientFD[$clientId], $this->readBufferSize ) ){
بيانات $ .= $buf;
$endString = substr( $buf, - strlen( $this->readEndCharacter ) );
إذا( $endString == $this->readEndCharacter )
استراحة؛
إذا ($buf == NULL)
استراحة؛
}
إذا($buf === خطأ)
$this->sendDebugMessage( "تعذرت القراءة من العميل ".$clientId." ( ".$this->getLastSocketError( $this->clientFD[$clientId] )." );
إرجاع بيانات $؛
}
/**
* قبول اتصال جديد
*
* @الوصول العام
* @param Resource &$socket مقبس الذي تلقى الاتصال الجديد
*return int $clientID المعرف الداخلي للعميل
*/
وظيفة قبول الاتصال( &$socket ){
for( $i = 0 ; $i <= count( $this->clientFD ); $i++ ){
إذا( !isset( $this->clientFD[$i] ) || $this->clientFD[$i] == NULL ){
$this->clientFD[$i] = مقبس_قبول( $socket );
المقبس_setopt( $this->clientFD[$i], SOL_SOCKET, SO_REUSEADDR, 1 );
$peer_host = "";
$peer_port = "";
المقبس_getpeername( $this->clientFD[$i], $peer_host, $peer_port );
$this->clientInfo[$i] = المصفوفة(
"المضيف" => $peer_host،
"المنفذ" => $peer_port،
"connectOn" => الوقت()
);
$هذا->العملاء++;
$this->sendDebugMessage( "اتصال جديد ( ".$i." ) من ".$peer_host." على المنفذ ".$peer_port );
إذا (method_exists( $this، "onConnect" ) )
$this->onConnect( $i);
إرجاع $i;
}
}
}
/**
* التحقق مما إذا كان العميل لا يزال متصلاً
*
* @الوصول العام
* @param عدد صحيح معرف العميل $id
* @return boolean $connected صحيح إذا كان العميل متصلاً، وخطأ خلاف ذلك
*/
الوظيفة متصلة(معرف $){
إذا ( !isset( $this->clientFD[$id] ) )
عودة كاذبة.
عودة صحيحة؛
}
/**
* اتصال وثيق بالعميل
*
* @الوصول العام
* @param int $clientID المعرف الداخلي للعميل
*/
وظيفة إغلاق الاتصال(معرف $){
إذا ( !isset( $this->clientFD[$id] ) )
عودة كاذبة.
إذا (method_exists( $this، "onClose" ) )
$this->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" ) )
$this->onShutdown();
$maxFD = count( $this->clientFD );
ل( $i = 0; $i < $maxFD; $i++ )
$this->CloseConnection( $i);
@socket_ Close( $this->initFD );
$this->sendDebugMessage( "إيقاف تشغيل الخادم." );
مخرج؛
}
/**
* الحصول على العدد الحالي من العملاء
*
* @الوصول العام
* @return int $clients عدد العملاء
*/
وظيفة الحصول على العملاء () {
إرجاع $this->clients;
}
/**
* إرسال البيانات إلى العميل
*
* @الوصول العام
*param int $clientId معرف العميل
*param سلسلة بيانات البيانات $ لإرسالها
* علامة @param boolean $debugData للإشارة إلى ما إذا كان يجب أيضًا إرسال البيانات المكتوبة إلى المقبس كرسالة تصحيح
*/
الدالة sendData( $clientId, $data, $debugData = true ){
إذا ( !isset( $this->clientFD[$clientId] ) || $this->clientFD[$clientId] == NULL )
عودة كاذبة.
إذا ($debugData)
$this->sendDebugMessage( "sending: "" . $data . "" to: $clientId" );
إذا( !@socket_write ( $this->clientFD[$clientId], $data ) )
$this->sendDebugMessage( "تعذرت كتابة '".$data."' العميل ".$clientId." ( ".$this->getLastSocketError( $this->clientFD[$clientId] .")." ) ;
}
/**
* إرسال البيانات لجميع العملاء
*
* @الوصول العام
*param سلسلة بيانات البيانات $ لإرسالها
* @param array $ استبعاد معرفات العملاء المراد استبعادها
*/
وظيفة بث البيانات( $data، $exclude = array()، $debugData = true ){
إذا (! فارغة( $exclude ) && !is_array( $exclude ) )
$استبعاد = مصفوفة(استبعاد $);
for( $i = 0; $i < count( $this->clientFD ); $i++ ){
if( isset( $this->clientFD[$i] ) && $this->clientFD[$i] != NULL && !in_array( $i, $exclude ) ){
إذا ($debugData)
$this->sendDebugMessage( "sending: "" . $data . "" to: $i" );
إذا( !@socket_write ( $this->clientFD[$i], $data ) )
$this->sendDebugMessage( "تعذرت كتابة '".$data."' العميل ".$i." ( ".$this->getLastSocketError( $this->clientFD[$i] )." )." ) ;
}
}
}
/**
* الحصول على المعلومات الحالية عن العميل
*
* @الوصول العام
*param int $clientId معرف العميل
*return array $info معلومات حول العميل
*/
الدالة getClientInfo($clientId){
إذا ( !isset( $this->clientFD[$clientId] ) || $this->clientFD[$clientId] == NULL )
عودة كاذبة.
إرجاع $this->clientInfo[$clientId];
}
/**
* إرسال رسالة التصحيح
*
* @الوصول خاص
*param سلسلة رسالة $msg لتصحيح الأخطاء
*/
الدالة sendDebugMessage( $msg ){
إذا (!$هذا->تصحيح)
عودة كاذبة.
$msg = date( "Ymd H:i:s", time() ) . " " . $رسالة;
التبديل( $هذا->debugMode ){
حالة "النص":
$msg = $msg."n";
استراحة؛
الحالة "أتش تي أم أل":
$msg = htmlspecialchars( $msg ) . "<br />n";
استراحة؛
}
إذا( $this->debugDest == "stdout" || فارغ( $this->debugDest ) ){
صدى $msg;
تدفق ()؛
عودة صحيحة؛
}
error_log( $msg, 3, $this->debugDest );
عودة صحيحة؛
}
/**
* سلسلة العودة لخطأ المقبس الأخير
*
* @الوصول العام
*return string $error الخطأ الأخير
*/
الدالة getLastSocketError( &$fd ){
$lastError = المقبس_last_error( $fd );
إرجاع "الرسالة: " . مأخذ التوصيل_ستريرور($lastError). "/ الكود: ".$lastError;
}
وظيفة onReceiveData($ip,$data){
$this->broadcastData( $data,array(), true );
}
}
$patServer = new patServer();
$patServer->start();
?>