From 11e1d2033997fd741916945f1cdc1ad3912b6f79 Mon Sep 17 00:00:00 2001 From: basten Date: Mon, 7 May 2012 13:21:44 +0200 Subject: [PATCH] initial --- client/C64_Pro_Mono_v1.0-STYLE.eot | Bin 0 -> 8064 bytes client/C64_Pro_Mono_v1.0-STYLE.ttf | Bin 0 -> 30600 bytes client/C64_Pro_Mono_v1.0-STYLE.woff | Bin 0 -> 9180 bytes client/c64.css | 25 +++ client/index.html | 97 ++++++++ server/chatbot.demo.php | 24 ++ server/client.html | 67 ++++++ server/server.php | 166 ++++++++++++++ server/websocket.class.php | 333 ++++++++++++++++++++++++++++ server/websocket.demo.php | 8 + 10 files changed, 720 insertions(+) create mode 100644 client/C64_Pro_Mono_v1.0-STYLE.eot create mode 100644 client/C64_Pro_Mono_v1.0-STYLE.ttf create mode 100644 client/C64_Pro_Mono_v1.0-STYLE.woff create mode 100644 client/c64.css create mode 100644 client/index.html create mode 100644 server/chatbot.demo.php create mode 100644 server/client.html create mode 100644 server/server.php create mode 100644 server/websocket.class.php create mode 100644 server/websocket.demo.php diff --git a/client/C64_Pro_Mono_v1.0-STYLE.eot b/client/C64_Pro_Mono_v1.0-STYLE.eot new file mode 100644 index 0000000000000000000000000000000000000000..512eca79160f1bbc69fd604a501e1966b7517643 GIT binary patch literal 8064 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 X1bVbUsOaut(9HF4Kezn9Pv`#wA+d@2 literal 0 HcmV?d00001 diff --git a/client/C64_Pro_Mono_v1.0-STYLE.ttf b/client/C64_Pro_Mono_v1.0-STYLE.ttf new file mode 100644 index 0000000000000000000000000000000000000000..6160a54024293cf0442cb592b73b60fd2ce27c2c GIT binary patch literal 30600 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 literal 0 HcmV?d00001 diff --git a/client/C64_Pro_Mono_v1.0-STYLE.woff b/client/C64_Pro_Mono_v1.0-STYLE.woff new file mode 100644 index 0000000000000000000000000000000000000000..4defca4b6834a2ca9534f89bae455defa7e7c73b GIT binary patch literal 9180 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 bGRdVez~B+`|LKHOUe%ZU?pX + + + + +C64 + + + + + + + +
+
+ + \ No newline at end of file diff --git a/server/chatbot.demo.php b/server/chatbot.demo.php new file mode 100644 index 0000000..cce2459 --- /dev/null +++ b/server/chatbot.demo.php @@ -0,0 +1,24 @@ +#!/php -q + php -q chatbot.demo.php +include "websocket.class.php"; + +// Extended basic WebSocket as ChatBot +class ChatBot extends WebSocket{ + function process($user,$msg){ + $this->say("< ".$msg); + switch($msg){ + case "hello" : $this->send($user->socket,"hello human"); break; + case "hi" : $this->send($user->socket,"zup human"); break; + case "name" : $this->send($user->socket,"my name is Multivac, silly I know"); break; + case "age" : $this->send($user->socket,"I am older than time itself"); break; + case "date" : $this->send($user->socket,"today is ".date("Y.m.d")); break; + case "time" : $this->send($user->socket,"server time is ".date("H:i:s")); break; + case "thanks": $this->send($user->socket,"you're welcome"); break; + case "bye" : $this->send($user->socket,"bye"); break; + default : $this->send($user->socket,$msg." not understood"); break; + } + } +} + +$master = new ChatBot("localhost",12345); diff --git a/server/client.html b/server/client.html new file mode 100644 index 0000000..e17b0a8 --- /dev/null +++ b/server/client.html @@ -0,0 +1,67 @@ + + +WebSocket + + + + + + + +

WebSocket v2.00

+
+ + + +
Commands: hello, hi, name, age, date, time, thanks, bye
+ + diff --git a/server/server.php b/server/server.php new file mode 100644 index 0000000..8eca8b4 --- /dev/null +++ b/server/server.php @@ -0,0 +1,166 @@ +#!/php -q +php -q server.php */ + +error_reporting(E_ALL & !E_WARNING); +set_time_limit(0); +ob_implicit_flush(); + +$master = WebSocket("localhost",12346); +$sockets = array($master); +$users = array(); +$debug = true; + +while(true){ + $changed = $sockets; + socket_select($changed,$write=NULL,$except=NULL,NULL); + foreach($changed as $socket){ + if($socket==$master){ + $client=socket_accept($master); + if($client<0){ console("socket_accept() failed"); continue; } + else{ connect($client); } + } + else{ + $bytes = @socket_recv($socket,$buffer,2048,0); + if($bytes==0){ disconnect($socket); } + else{ + $user = getuserbysocket($socket); + if(!$user->handshake){ dohandshake($user,$buffer); } + else{ process($user,$buffer); } + } + } + } +} + +//--------------------------------------------------------------- +function process($user,$msg){ + $action = unwrap($msg); + say("< ".$action); + switch($action){ + case "hello" : send($user->socket,"hello human"); break; + case "hi" : send($user->socket,"zup human"); break; + case "name" : send($user->socket,"my name is Multivac, silly I know"); break; + case "age" : send($user->socket,"I am older than time itself"); break; + case "date" : send($user->socket,"today is ".date("Y.m.d")); break; + case "time" : send($user->socket,"server time is ".date("H:i:s")); break; + case "thanks": send($user->socket,"you're welcome"); break; + case "bye" : send($user->socket,"bye"); break; + default : send($user->socket,$action." not understood"); break; + } +} + +function send($client,$msg){ + say("> ".$msg); + $msg = wrap($msg); + socket_write($client,$msg,strlen($msg)); +} + +function WebSocket($address,$port){ + $master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() failed"); + socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1) or die("socket_option() failed"); + socket_bind($master, $address, $port) or die("socket_bind() failed"); + socket_listen($master,20) or die("socket_listen() failed"); + echo "Server Started : ".date('Y-m-d H:i:s')."\n"; + echo "Master socket : ".$master."\n"; + echo "Listening on : ".$address." port ".$port."\n\n"; + return $master; +} + +function connect($socket){ + global $sockets,$users; + $user = new User(); + $user->id = uniqid(); + $user->socket = $socket; + array_push($users,$user); + array_push($sockets,$socket); + console($socket." CONNECTED!"); +} + +function disconnect($socket){ + global $sockets,$users; + $found=null; + $n=count($users); + for($i=0;$i<$n;$i++){ + if($users[$i]->socket==$socket){ $found=$i; break; } + } + if(!is_null($found)){ array_splice($users,$found,1); } + $index = array_search($socket,$sockets); + socket_close($socket); + console($socket." DISCONNECTED!"); + if($index>=0){ array_splice($sockets,$index,1); } +} + +function dohandshake($user,$buffer){ + console("\nRequesting handshake..."); + console($buffer); + list($resource,$host,$origin,$strkey1,$strkey2,$data) = getheaders($buffer); + console("Handshaking..."); + + $pattern = '/[^\d]*/'; + $replacement = ''; + $numkey1 = preg_replace($pattern, $replacement, $strkey1); + $numkey2 = preg_replace($pattern, $replacement, $strkey2); + + $pattern = '/[^ ]*/'; + $replacement = ''; + $spaces1 = strlen(preg_replace($pattern, $replacement, $strkey1)); + $spaces2 = strlen(preg_replace($pattern, $replacement, $strkey2)); + + if ($spaces1 == 0 || $spaces2 == 0 || $numkey1 % $spaces1 != 0 || $numkey2 % $spaces2 != 0) { + socket_close($user->socket); + console('failed'); + return false; + } + + $ctx = hash_init('md5'); + hash_update($ctx, pack("N", $numkey1/$spaces1)); + hash_update($ctx, pack("N", $numkey2/$spaces2)); + hash_update($ctx, $data); + $hash_data = hash_final($ctx,true); + + $upgrade = "HTTP/1.1 101 WebSocket Protocol Handshake\r\n" . + "Upgrade: WebSocket\r\n" . + "Connection: Upgrade\r\n" . + "Sec-WebSocket-Origin: " . $origin . "\r\n" . + "Sec-WebSocket-Location: ws://" . $host . $resource . "\r\n" . + "\r\n" . + $hash_data; + + socket_write($user->socket,$upgrade.chr(0),strlen($upgrade.chr(0))); + $user->handshake=true; + console($upgrade); + console("Done handshaking..."); + return true; +} + +function getheaders($req){ + $r=$h=$o=null; + if(preg_match("/GET (.*) HTTP/" ,$req,$match)){ $r=$match[1]; } + if(preg_match("/Host: (.*)\r\n/" ,$req,$match)){ $h=$match[1]; } + if(preg_match("/Origin: (.*)\r\n/",$req,$match)){ $o=$match[1]; } + if(preg_match("/Sec-WebSocket-Key: (.*)\r\n/",$req,$match)){ $key2=$match[1]; } + if(preg_match("/Sec-WebSocket-Key: (.*)\r\n/",$req,$match)){ $key1=$match[1]; } + if(preg_match("/\r\n(.*?)\$/",$req,$match)){ $data=$match[1]; } + return array($r,$h,$o,$key1,$key2,$data); +} + +function getuserbysocket($socket){ + global $users; + $found=null; + foreach($users as $user){ + if($user->socket==$socket){ $found=$user; break; } + } + return $found; +} + +function say($msg=""){ echo $msg."\n"; } +function wrap($msg=""){ return chr(0).$msg.chr(255); } +function unwrap($msg=""){ return substr($msg,1,strlen($msg)-2); } +function console($msg=""){ global $debug; if($debug){ echo $msg."\n"; } } + +class User{ + var $id; + var $socket; + var $handshake; +} + +?> diff --git a/server/websocket.class.php b/server/websocket.class.php new file mode 100644 index 0000000..89ff451 --- /dev/null +++ b/server/websocket.class.php @@ -0,0 +1,333 @@ +. + * + * @package WebSocket + * @author George Nava + * @author Vincenzo Ferrari + * @copyright 2010-2011 + * @license http://www.gnu.org/licenses/gpl.txt GNU GPLv3 + * @version 1.1.0 + * @link http://code.google.com/p/phpwebsocket/ + */ + + /** + * @usage $master = new WebSocket ("localhost", 12345); + */ + class WebSocket { + var $master; + var $sockets = array (); + var $users = array (); + // true to debug + var $debug = false; + // frame mask + var $masks; + // initial frames + var $initFrame; + + function __construct ($address, $port) { + error_reporting (E_ALL); + set_time_limit (0); + ob_implicit_flush (); + + // Socket creation + $this->master = socket_create (AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() failed"); + socket_set_option ($this->master, SOL_SOCKET, SO_REUSEADDR, 1) or die("socket_option() failed"); + socket_bind ($this->master, $address, $port) or die("socket_bind() failed"); + socket_listen ($this->master, 20) or die("socket_listen() failed"); + $this->sockets[] = $this->master; + $this->say ("Server Started : " . date ('Y-m-d H:i:s')); + $this->say ("Listening on : {$address} {$port}"); + $this->say ("Master socket : {$this->master}\n"); + + // Main loop + while (true) { + $changed = $this->sockets; + socket_select ($changed, $write = NULL, $except = NULL, NULL); + + foreach ($changed as $socket) { + if ($socket == $this->master) { + $client = socket_accept ($this->master); + + if ($client < 0) { + $this->log ("socket_accept() failed"); + continue; + } + else { + // Connects the socket + $this->connect ($client); + } + } + else { + $bytes = @socket_recv ($socket, $buffer, 2048, 0); + if ($bytes == 0) { + // On socket.close (); + $this->disconnect ($socket); + } + else { + // Retrieve the user from his socket + $user = $this->getuserbysocket ($socket); + + if (!$user->handshake) { + $this->dohandshake ($user, $buffer); + } + else { + $this->process ($user, $this->decode ($buffer)); + } + } + } + } + } + } + + /** + * @brief Echo incoming messages back to the client + * @note Extend and modify this method to suit your needs + * @param $user {User} : owner of the message + * @param $msg {String} : the message to echo + * @return void + */ + function process ($user, $msg) { + foreach($this->users as $user) + { + $this->send ($user->socket, $msg); + } + } + + /** + * @brief Send a message to a client + * @param $client {Socket} : socket to send the message + * @param $msg {String} : the message to send + * @return void + */ + function send ($client, $msg) { + $this->say ("> {$msg}"); + $msg = $this->encode ($msg); + socket_write ($client, $msg, strlen ($msg)); + } + + /** + * @brief Connect a new client (socket) + * @param $socket {Socket} : socket to connect + * @return void + */ + function connect ($socket) { + $user = new User (); + $user->id = uniqid (); + $user->socket = $socket; + + array_push ($this->users, $user); + array_push ($this->sockets, $socket); + + $this->log ("{$socket} CONNECTED!"); + $this->log (date ("d/n/Y ") . "at " . date ("H:i:s T")); + } + + /** + * @brief Disconnect a client (socket) + * @param $socket {Socket} : socket to disconnect + * @return void + */ + function disconnect ($socket) { + $found = null; + $n = count ($this->users); + + // Finds the right user index from the given socket + for ($i = 0; $i < $n; $i++) { + if ($this->users[$i]->socket == $socket) { + $found = $i; + break; + } + } + + if (!is_null ($found)) { + array_splice ($this->users, $found, 1); + } + + $index = array_search ($socket, $this->sockets); + socket_close ($socket); + $this->log ("{$socket} DISCONNECTED!"); + + if ($index >= 0) { + array_splice ($this->sockets, $index, 1); + } + } + + /** + * @brief Do the handshake between server and client + * @param $user {User} : user to handshake + * @param $buffer {String} : user's request + * @return Boolean + */ + function dohandshake ($user, $buffer) { + $this->log ("\nRequesting handshake..."); + $this->log ($buffer); + + list ($resource, $host, $connection, $version, $origin, $key, $upgrade) = $this->getheaders ($buffer); + + $this->log ("Handshaking..."); + $reply = + "HTTP/1.1 101 Switching Protocols\r\n" . + "Upgrade: {$upgrade}\r\n" . + "Connection: {$connection}\r\n" . + "Sec-WebSocket-Version: {$version}\r\n" . + "Sec-WebSocket-Origin: {$origin}\r\n" . + "Sec-WebSocket-Location: ws://{$host}{$resource}\r\n" . + "Sec-WebSocket-Accept: " . $this->calcKey ($key) . "\r\n" . + "\r\n"; + + // Closes the handshake + socket_write ($user->socket, $reply, strlen ($reply)); + + $user->handshake = true; + $this->log ($reply); + $this->log ("Done handshaking..."); + + return true; + } + + /** + * @brief Calculate Sec-WebSocket-Accept + * @note For more info look at: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 + * @param $key {String} : key to calculate + * @return Calculated key + */ + function calcKey ($key) { + // Constant string as specified in the ietf-hybi-17 draft + $key .= "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + $key = sha1 ($key); + $key = pack ('H*', $key); + $key = base64_encode ($key); + + return $key; + } + + /** + * @brief Get the client request headers + * @param $buffer {String} : buffer from which to draw the headers. + * @return Array + */ + function getheaders ($buffer) { + $resource = $host = $connection = $version = $origin = $key = $upgrade = null; + + preg_match ('#GET (.*?) HTTP#', $buffer, $match) && $resource = $match[1]; + preg_match ("#Host: (.*?)\r\n#", $buffer, $match) && $host = $match[1]; + preg_match ("#Connection: (.*?)\r\n#", $buffer, $match) && $connection = $match[1]; + preg_match ("#Sec-WebSocket-Version: (.*?)\r\n#", $buffer, $match) && $version = $match[1]; + preg_match ("#Sec-WebSocket-Origin: (.*?)\r\n#", $buffer, $match) && $origin = $match[1]; + preg_match ("#Sec-WebSocket-Key: (.*?)\r\n#", $buffer, $match) && $key = $match[1]; + preg_match ("#Upgrade: (.*?)\r\n#", $buffer, $match) && $upgrade = $match[1]; + + return array ($resource, $host, $connection, $version, $origin, $key, $upgrade); + } + + /** + * @brief Retrieve an user from his socket + * @param $socket {Socket} : socket of the user to search + * @return User or null + */ + function getuserbysocket ($socket) { + $found = null; + + foreach ($this->users as $user) { + if ($user->socket == $socket) { + $found = $user; + break; + } + } + + return $found; + } + + /** + * @brief Decode messages as specified in the ietf-hybi-17 draft + * @param $msg {String} : message to decode + * @return Message decoded + */ + function decode ($msg) { + $len = $data = $decoded = $index = null; + $len = $msg[1] & 127; + + if ($len === 126) { + $this->masks = substr ($msg, 4, 4); + $data = substr ($msg, 8); + $this->initFrame = substr ($msg, 0, 4); + } + else if ($len === 127) { + $this->masks = substr ($msg, 10, 4); + $data = substr ($msg, 14); + $this->initFrame = substr ($msg, 0, 10); + } + else { + $this->masks = substr ($msg, 2, 4); + $data = substr ($msg, 6); + $this->initFrame = substr ($msg, 0, 2); + } + + for ($index = 0; $index < strlen ($data); $index++) { + $decoded .= $data[$index] ^ $this->masks[$index % 4]; + } + + return $decoded; + } + + /** + * @brief Encode messages + * @param $msg {String} : message to encode + * @return Message encoded + */ + function encode ($msg) { + $index = $encoded = null; + + for ($index = 0; $index < strlen ($msg); $index++) { + $encoded .= $msg[$index] ^ $this->masks[$index % 4]; + } + + $encoded = $this->initFrame . $this->masks . $encoded; + + return $encoded; + } + + /** + * @brief Local echo messages + * @param $msg {String} : message to echo + * @return void + */ + function say ($msg = "") { + echo "{$msg}\n"; + } + + /** + * @brief Log function + * @param $msg {String} : message to log + * @return void + */ + function log ($msg = "") { + if ($this->debug) { + echo "{$msg}\n"; + } + } + } + + class User { + var $id; + var $socket; + var $handshake; + } +?> diff --git a/server/websocket.demo.php b/server/websocket.demo.php new file mode 100644 index 0000000..0a2b58b --- /dev/null +++ b/server/websocket.demo.php @@ -0,0 +1,8 @@ +#!/php -q + php -q websocket.demo.php + + // Basic WebSocket demo echoes msg back to client + include "websocket.class.php"; + $master = new WebSocket ("CONTENT6818", 12346); +?>