Jest to blog poświęcony nowoczelnym technologiom ułatwiającym tworzenie aplikacji internetowych. Znajdziesz tutaj porady na temat CSS3, JavaScript, designu, web-usability, standardów W3C.
Cześć! Nazywam się Michał Środek. Z zawodu programista php, z zamiłowania gitarzysta oraz fanatyk GNU/Linuksa(openSUSE® w laptopie). W branży aplikacji internetowych od 9 lat. Prywatnie bez dzieci i kota.
Pracuję wciąż nad własnym elastycznym i wydajnym frameworkiem MVC, kilkoma portalami internetowymi oraz mniejszymi bibliotekami php. Czekam na wasze opinie, zgłoszenia błędów oraz pomysły na dalszy rozwój.
Ta część strony jest w trakcie budowy a moje prace tymczasowo niedostępne.
W przypadku pytań, ofert pracy oraz ciekawych pomysłów proszę się ze mną kontaktować. Możesz mnie znaleźć i wysłać PW na php.pl(SHiP), jamendo.com(michalsrodek), goldenLine.pl, facebook.com lub nk.pl
Spróbujmy udoskonalić nasz serwer tak aby posiadał możliwości prostego chatu — wysyłanie wiadomości ogólnych do wszystkich użytkowników oraz prywatnych tylko do jednej osoby. Mój kod urósł prawie do 150 linijek przy czym nie uwzględnia on kilku rzeczy, o których wspomnę podczas pisania protokołu lub tworzenia klientów.(w końcu nie piszę MUD-a i telnet muszę zastąpić czymś innym).
Poprawiony kod wygląda tak:
<?php set_time_limit(0); class GameClient { public $id; private $socket; public function __construct(&$socket, $id) { $this->socket = $socket; $this->id = $id; } public function sendMessage($message) { socket_write($this->socket, $message."\n".chr(0)); echo 'Wysylam wiadomosc do '.$this->id.': '.$message."\n"; } } class GameServer { private $master; private $address; private $port; private $maxClients; private $sockets = array(); private $clients = array(); public function __construct($address = '127.0.0.123', $port=14117, $maxClients = 10) { $this->address = $address; $this->port = $port; $this->maxClients = $maxClients; $this->master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_bind($this->master, $this->address, $this->port); socket_listen($this->master); $this->sockets[] = $this -> master; while(true) { $changedSockets = $this->sockets; socket_select($changedSockets,$write=null,$exceptions=null,null); foreach($changedSockets as $socket) { // zmiana gniazda głównego => nowe polaczenie if($socket == $this->master) { $socket = socket_accept($this->master); $this->sockets[] = $socket; $id = array_search($socket, $this->sockets); $this->clients[] = new GameClient(&$this->sockets[$id], $id); } // zmiana gniazda jednego z klientów else { $input = socket_read($socket, 1024); $input = trim($input," \t\n\r"); if(strlen($input)==0 || $input=='exit') { $output = 'Bye bye!'."\n".chr(0); // send data to socket socket_write($socket, $output); $id = array_search($socket, $this->sockets); unset($this->sockets[$id]); unset($this->clients[$id-1]); socket_close($socket); } else { $command = (strpos($input,' ')!==false) ? substr($input,0, strpos($input,' ')) : $input; switch($command) { case 'hello': $output = 'Hello!'; break; case 'list': // identyfikatory wszystkich gniazd $all = array_keys($this->sockets); // identyfikator uzytkownika $id = array_search($socket, $this->sockets); // usuniecie ID glownego gniazda oraz gniazda uzytkownika $all = array_diff($all, Array(0, $id)); if(empty($all)) $output='Nie ma innych osob'; else $output='ID innych osob to: '.implode(', ',$all); break; default: if(is_numeric($command)) { // identyfikator uzytkownika $id = array_search($socket, $this->sockets); // wyslanie wiadomosci do wszystkich if($command=='0') { foreach($this->clients as $client) { // nie wysylamy do siebie if($client->id!=$id) $client->sendMessage(substr($input,strpos($input,' ')+1)); } $output = 'Do wszystkich'; } // nie wysylamy do siebie, uzytkownik istnieje elseif($command!=$id && isset($this->sockets[$command])) { // jeden mniejszy ponieważ $this->master znajduje sie // w tablicy gniazd a nie jest klientem $this->clients[$command-1]->sendMessage(substr($input,strpos($input,' ')+1)); $output = 'OK'; } else $output ='Bledny ID'; } else $output = 'Niezrozumiale polecenie: '.$input.''; } socket_write($socket, $output."\n".chr(0)); } } } } } public function __desctruct() { socket_close($this->master); } } $gameServer = new GameServer(); ?>
Czas na krótką analizę. Pierwsze co rzuca się w oczy to nowa klasa GameClient. Jest to klasa, z którą wiążę pewne plany. Obiekty tej klasy przechowują dwie rzeczy — gniazdo oraz jego identyfikator. Metoda sendMessage() służy do wysyłania wiadomości do użytkownika, który jest podpięty do tego gniazda.
W klasie GameServer pojawiły się dwa nowe pola prywatne, tablica sockets przechowująca wszystkie gniazda dostępne dla serwera oraz tablicę clients przechowującą obiekty klasy GameClient.
Ogólne założenia się nie zmieniły — wciąż istnieje pętla nieskończona jednak jej zawartość została zmodyfikowana.
$changedSockets = $this->sockets; socket_select($changedSockets,$write=null,$exceptions=null,null);
Użyliśmy tutaj funkcji socket_select(). W parametrach podajemy referencje(dlatego pierwsza linijka jest konieczna) do tablic gniazd, a funkcja pozostawia w nich jedynie te elementy, w których zaszły pewne zdarzenia. Pierwszy parametr odpowiada czy czytanie, czyli wybrane zostaną jedynie te gniazda, które coś przesyłają do serwera. Drugi i trzeci parametr odpowiadają za pisanie oraz wyjątki lecz nas one nie interesują. Ostatni parametr dotyczy limitu czasu oczekiwania na wybranie poszczególnych gniazd. Null oznacza brat limitu czasowego.
Następnie sprawdzamy każde z gniazd z tablicy $changedSockets. Jeśli gniazdo jest tym samym co gniazdo główne serwera oznacza to, że nowy użytkownik chce ustanowić połączenie. Robimy to tworząc nowe gniazdo i dopisując informacje o nim do tablic $this->sockets oraz $this->clients. W przypadku innych gniazd odczytywane są dane przesyłane do serwera.
$input = socket_read($socket, 1024); $input = trim($input," \t\n\r");
Używam funkcji trim() aby wyczyścić dane przychodzące do serwera z różnych niepotrzebnych białych znaków.
Pozostały kod to pseudo-protokół chatu. W przypadku przesłania komunikatu pustego lub wiadomości exit połączenie z użytkownikiem jest zrywane. Polecenie hello jest czysto demonstracyjne. W przypadku wpisania list pojawia się lista identyfikatorów innych użytkowników dostępnych aktualnie lub komunikat o ich braku. Wysyłanie komunikatów do innych użytkowników odbywa się w następujący sposób:
ID komunikat
Jeżeli ID wynosi zero wiadomość jest przesyłana do wszystkich uczestników chatu.
Serwer ten wciąż nie jest idealny. Nie zabezpiecza on przed połączeniem się zbyt dużej ilości użytkowników. Protokół również jest dosyć dziwny. W zasadzie stworzyłem go jedynie w celach edukacyjnych. Proponowałbym jego wymianę na coś przyjaźniejszego. Co warto dopisać do takiego serwera? Można spróbować uporać się z problemem pokojów dla różnych grup użytkowników. Zapewne te rzeczy opiszę niebawem na blogu lecz tymczasowo zrobię przerwę z php. Już niebawem sporo informacji na temat rysowania w obiekcie Canvas. Może zrobić jakąś fajną planszę w rzucie izometrycznym?
Bardzo ciekawy pomysł z tą grą. Zastanawia mnie czy ty robisz tą grę sam czy w jakimś większym zespole? Bo stworzenie takiej gry w pojedynkę raczej się nie uda, aczkolwiek trzymam kciuki
Komentarz by kalkulator kredytowy — 15 maja 2010, 23:25
Sam, lecz mam kolegę, który myślę, że mi pomoże.Daję sobie miesiąc aby stworzyć pierwszą grywalną wersję(rejestracja, logowanie, rozgrywka z komputerem i innymi graczami). Najwięcej problemów będę miał z grafiką(nie umiem rysować
) oraz z optymalizacją jej renderowania tak aby canvas wyciągał przynajmniej jakieś 25fps
Komentarz by Michał Środek — 16 maja 2010, 0:02