<?PHP
/**
* patServer
* Kelas dasar server soket PHP
* Acara yang dapat ditangani:
* * di Mulai
* * diConnect
* * padaConnectionRefused
* * di Tutup
* * pada Shutdown
* * padaReceiveData
*
* @versi 1.1
* @penulis Stephan Schmidt < [email protected] >
* @paket patServer
*/
kelas patServer{
/**
* informasi tentang proyek
* @var array $systemVars
*/
var $systemVars = array(
"Namaaplikasi" => "patServer",
"Versi aplikasi" => "1.1",
"penulis" => array("Stephan Schmidt < [email protected] >", )
);
/**
* port untuk mendengarkan
* @var bilangan bulat $port
*/
var $pelabuhan = 10000;
/**
* domain untuk diikat
* @var string $domain
*/
var $domain = "host lokal";
/**
* jumlah maksimum klien
* @var bilangan bulat $maxClients
*/
var $maxClients = -1;
/**
* ukuran buffer untuk socket_read
* @var bilangan bulat $readBufferSize
*/
var $readBufferSize = 128;
/**
* karakter akhir untuk socket_read
* @var bilangan bulat $readEndCharacter
*/
var $readEndCharacter = "n";
/**
* maksimum simpanan dalam antrian
* @var bilangan bulat $maxQueue
*/
var $maxQueue = 500;
/**
* mode debug
* @var boolean $debug
*/
var $debug = benar;
/**
* mode debug
* @var string $debugMode
*/
var $debugMode = "teks";
/**
* tujuan debug (nama file atau stdout)
* @var string $debugDest
*/
var $debugDest = "stdout";
/**
* array kosong, digunakan untuk socket_select
* @var array $null
*/
var $null = susunan();
/**
* semua deskriptor file disimpan di sini
* @var array $klienFD
*/
var $klienFD = array();
/**
* diperlukan untuk menyimpan informasi klien
* @var array $klienInfo
*/
var $klienInfo = array();
/**
* diperlukan untuk menyimpan informasi server
* @var array $serverInfo
*/
var $serverInfo = array();
/**
* jumlah klien
* @var bilangan bulat $klien
*/
var $klien = 0;
/**
* buat server soket baru
*
* @akses publik
* @param string $domain domain yang akan diikat
* @param integer $port port untuk didengarkan
*/
fungsi patServer( $domain = "localhost", $port = 10000 ){
$ini->domain = $domain;
$ini->pelabuhan = $pelabuhan;
$ini->serverInfo["domain"] = $domain;
$ini->serverInfo["pelabuhan"] = $pelabuhan;
$ini->serverInfo["namaserver"] = $ini->systemVars["Namaaplikasi"];
$ini->serverInfo["serverversion"] = $ini->systemVars["appVersion"];
set_time_limit( 0 );
}
/**
* atur jumlah maksimum koneksi simultan
*
* @akses publik
* @param ke dalam $maxClients
*/
fungsi setMaxClients( $maxClients ){
$ini->maxClients = $maxClients;
}
/**
* atur mode debug
*
* @akses publik
* @param campuran $debug [teks|htmlfalse]
* @param string $tujuan akhir pesan debug (stdout ke output atau nama file jika log harus ditulis)
*/
fungsi setDebugMode( $debug, $dest = "stdout" ){
jika( $debug === salah ){
$ini->debug = salah;
kembali benar;
}
$ini->debug = benar;
$ini->debugMode = $debug;
$ini->debugDest = $dest;
}
/**
* memulai server
*
* @akses publik
* @param ke dalam $maxClients
*/
fungsi mulai(){
$ini->initFD = @socket_create( AF_INET, SOCK_STREAM, 0 );
jika( !$ini->initFD )
die( "patServer: Tidak dapat membuat soket." );
// alamat dapat digunakan kembali
socket_setopt( $ini->initFD, SOL_SOCKET, SO_REUSEADDR, 1 );
// ikat soketnya
if( !@socket_bind ( $ini->initFD, $ini->domain, $ini->port ) ){
@socket_close( $ini->initFD );
die( "patServer: Tidak dapat mengikat soket ke ".$this->domain." pada port ".$this->port." ( ".$this->getLastSocketError( $this->initFd )." ).." );
}
// mendengarkan pada port yang dipilih
jika( !@socket_listen ( $ini->initFD, $ini->maxQueue ) )
die( "patServer: Tidak dapat mendengarkan ( ".$this->getLastSocketError( $this->initFd )." )." );
$this->sendDebugMessage( "Mendengarkan pada port ".$this->port.". Server dimulai pada ".date( "H:i:s", time() ) );
// ini memungkinkan fungsi shutdown untuk memeriksa apakah server sudah dimatikan
$GLOBALS["_patServerStatus"] = "berjalan";
// ini memastikan bahwa server akan dimatikan dengan benar
register_shutdown_function( array( $ini, "shutdown" ) );
jika( metode_ada( $ini, "onStart" ) )
$ini->diMulai();
$ini->serverInfo["mulai"] = waktu();
$ini->serverInfo["status"] = "berjalan";
sementara( benar ){
$readFDs = susunan();
array_push( $readFDs, $ini->initFD );
// ambil semua klien yang sedang menunggu koneksi
untuk( $i = 0; $i < hitungan( $ini->klienFD ); $i++ )
jika( isset( $ini->klienFD[$i] ) )
array_push( $readFDs, $ini->clientFD[$i] );
// blokir dan tunggu data atau koneksi baru
$siap = @socket_select( $readFDs, $ini->null, $ini->null, NULL );
jika( $siap === salah ){
$this->sendDebugMessage( "socket_select gagal." );
$ini->mematikan();
}
// periksa koneksi baru
jika( dalam_array( $ini->initFD, $readFDs ) ){
$klien baru = $ini->acceptConnection( $ini->initFD );
// memeriksa jumlah maksimum koneksi
if( $ini->maxClients > 0 ){
if( $ini->klien > $ini->maksKlien ){
$this->sendDebugMessage( "Terlalu banyak koneksi." );
jika( metode_ada( $ini, "onConnectionRefused" ) )
$ini->onConnectionRefused( $klien baru );
$ini->closeConnection( $klien baru );
}
}
jika( --$siap <= 0 )
melanjutkan;
}
// memeriksa semua klien untuk data yang masuk
untuk( $i = 0; $i < hitungan( $ini->klienFD ); $i++ ){
jika( !isset( $ini->klienFD[$i] ) )
melanjutkan;
jika( dalam_array( $ini->klienFD[$i], $readFDs ) ){
$data = $ini->readFromSocket( $i );
// data kosong => koneksi ditutup
jika( !$data ){
$this->sendDebugMessage( "Koneksi ditutup oleh rekan" );
$ini->closeConnection( $i );
}kalau tidak{
$this->sendDebugMessage( "Diterima ".trim( $data )." dari ".$i );
jika( metode_ada( $ini, "onReceiveData" ) )
$ini->onReceiveData( $i, $data );
}
}
}
}
}
/**
* membaca dari soket
*
* @akses pribadi
* @param integer $clientId id internal klien yang akan dibaca
* @return string $data data yang telah dibaca
*/
fungsi readFromSocket( $clientId ){
// mulai dengan string kosong
$data = "";
// membaca data dari soket
while( $buf = socket_read( $this->clientFD[$clientId], $this->readBufferSize ) ){
$data .= $buf;
$endString = substr( $buf, - strlen( $ini->readEndCharacter ) );
jika( $endString == $ini->readEndCharacter )
merusak;
jika( $buf == BATAL )
merusak;
}
jika( $buf === salah )
$this->sendDebugMessage( "Tidak dapat membaca dari klien ".$clientId." ( ".$this->getLastSocketError( $this->clientFD[$clientId] )." )." );
kembalikan $data;
}
/**
* terima koneksi baru
*
* @akses publik
* @param resource &$socket socket yang menerima koneksi baru
* @return int $clientID ID internal klien
*/
fungsi menerimaKoneksi( &$socket ){
untuk( $i = 0 ; $i <= hitungan( $ini->klienFD ); $i++ ){
if( !isset( $this->clientFD[$i] ) || $this->clientFD[$i] == NULL ){
$ini->clientFD[$i] = soket_accept( $socket );
socket_setopt( $ini->klienFD[$i], SOL_SOCKET, SO_REUSEADDR, 1 );
$peer_host = "";
$peer_port = "";
socket_getpeername( $ini->klienFD[$i], $peer_host, $peer_port );
$ini->Info Klien[$i] = array(
"host" => $peer_host,
"pelabuhan" => $peer_port,
"connectOn" => waktu()
);
$ini->klien++;
$this->sendDebugMessage( "Koneksi baru ( ".$i." ) dari ".$peer_host." pada port ".$peer_port );
jika( metode_ada( $ini, "onConnect" ) )
$ini->onConnect( $i );
kembalikan $i;
}
}
}
/**
* periksa, apakah klien masih terhubung
*
* @akses publik
* @param integer $id id klien
* @return boolean $connected benar jika klien terhubung, salah jika sebaliknya
*/
fungsi Terhubung( $id ){
jika( !isset( $ini->klienFD[$id] ) )
kembali salah;
kembali benar;
}
/**
* Tutup koneksi ke klien
*
* @akses publik
* @param int $clientID ID internal klien
*/
fungsi closeConnection( $id ){
jika( !isset( $ini->klienFD[$id] ) )
kembali salah;
if( metode_ada( $ini, "onClose" ) )
$ini->diTutup( $id );
$this->sendDebugMessage( "Koneksi tertutup ( ".$id." ) dari ".$this->clientInfo[$id]["host"]." pada port ".$this->clientInfo[$id][ "pelabuhan"] );
@socket_close( $ini->clientFD[$id] );
$ini->clientFD[$id] = NULL;
tidak disetel( $ini->clientInfo[$id] );
$ini->klien--;
}
/**
* mematikan server
*
* @akses publik
*/
fungsi mematikan(){
jika( $GLOBALS["_patServerStatus"] != "berjalan" )
KELUAR;
$GLOBALS["_patServerStatus"] = "berhenti";
jika( metode_ada( $ini, "onShutdown" ) )
$ini->onShutdown();
$maxFD = hitungan( $ini->clientFD );
untuk( $i = 0; $i < $maxFD; $i++ )
$ini->closeConnection( $i );
@socket_close( $ini->initFD );
$this->sendDebugMessage( "Matikan server." );
KELUAR;
}
/**
* dapatkan jumlah klien saat ini
*
* @akses publik
* @return int $clients jumlah klien
*/
fungsi getClients(){
kembalikan $ini->klien;
}
/**
* mengirim data ke klien
*
* @akses publik
* @param int $clientId ID klien
* @param string $data data yang akan dikirim
* @param boolean $debugData flag untuk menunjukkan apakah data yang ditulis ke soket juga harus dikirim sebagai pesan debug
*/
fungsi sendData( $clientId, $data, $debugData = benar ){
jika( !isset( $ini->clientFD[$clientId] ) || $ini->clientFD[$clientId] == NULL )
kembali salah;
jika( $debugData )
$this->sendDebugMessage( "mengirim: "" .$data . "" ke: $clientId" );
jika( !@socket_write ( $ini->clientFD[$clientId], $data ) )
$this->sendDebugMessage( "Tidak dapat menulis '".$data."' client ".$clientId." ( ".$this->getLastSocketError( $this->clientFD[$clientId] )." )." ) ;
}
/**
* mengirim data ke semua klien
*
* @akses publik
* @param string $data data yang akan dikirim
* @param array $exclude id klien yang akan dikecualikan
*/
fungsi BroadcastData( $data, $exclude = array(), $debugData = true ){
jika( !kosong( $kecualikan ) && !is_array( $kecualikan ) )
$kecualikan = array( $kecualikan );
untuk( $i = 0; $i < hitungan( $ini->klienFD ); $i++ ){
if( isset( $this->clientFD[$i] ) && $this->clientFD[$i] != NULL && !in_array( $i, $exclude ) ){
jika( $debugData )
$this->sendDebugMessage( "mengirim: "" .$data . "" ke: $i" );
jika( !@socket_write ( $ini->klienFD[$i], $data ) )
$this->sendDebugMessage( "Tidak dapat menulis '".$data."' klien ".$i." ( ".$this->getLastSocketError( $this->clientFD[$i] )." )." ) ;
}
}
}
/**
* mendapatkan informasi terkini tentang klien
*
* @akses publik
* @param int $clientId ID klien
* @return array $info informasi tentang klien
*/
fungsi getClientInfo( $clientId ){
jika( !isset( $ini->clientFD[$clientId] ) || $ini->clientFD[$clientId] == NULL )
kembali salah;
kembalikan $ini->clientInfo[$clientId];
}
/**
* kirim pesan debug
*
* @akses pribadi
* @param string $msg pesan untuk di-debug
*/
fungsi sendDebugMessage( $pesan ){
jika( !$ini->debug )
kembali salah;
$pesan = tanggal( "Ymd H:i:s", waktu() ) . " " . $pesan;
beralih( $ini->debugMode ){
kasus "teks":
$pesan = $pesan."n";
merusak;
kasus "html":
$pesan = htmlkarakter khusus( $pesan ) . "<br />n";
merusak;
}
if( $this->debugDest == "stdout" || kosong( $this->debugDest ) ){
gema $pesan;
menyiram();
kembali benar;
}
error_log( $pesan, 3, $ini->debugDest );
kembali benar;
}
/**
* mengembalikan string untuk kesalahan soket terakhir
*
* @akses publik
* @return string $error kesalahan terakhir
*/
fungsi getLastSocketError( &$fd ){
$lastError = soket_last_error( $fd );
kembalikan "pesan:". soket_strerror( $lastError ) . " / Kode: ".$lastError;
}
fungsi padaReceiveData($ip,$data){
$ini->broadcastData( $data,array(), benar );
}
}
$patServer = patServer baru();
$patServer->mulai();
?>