·ÖÏíPHPɨÂëµÇ¼µÀÀí¼°ÊµÏÖ°ì·¨
±¾ÎÄÖ÷Òª½éÉÜÁËɨÂëµÇ¼µÄµÀÀí¼°ÍÅÌåÁ÷³Ì£¬°üÀ¨Á˶þάÂëµÄÉú³É/ÁÔÈ¡¡¢¹ýÆÚʧЧµÄ´¦ÖᢵǼ״̬µÄ¼àÌý¡£
ɨÂëµÇ¼µÄµÀÀí
ÍÅÌåÁ÷³Ì
ΪÀû±ã¶®µÃ£¬ÎÒ¼ò±ã»ÁËÒ»¸ö UML ʱÐòͼ£¬ÓÃÒÔÃèдɨÂëµÇ¼µÄ´óÖÂÁ÷³Ì£¡
×ܽáÏÂÖÐÐÄÁ÷³Ì£º
¿ÒÇóÒµÎñЧÀÍÆ÷ÁÔÈ¡ÓÃÒԵǼµÄ¶þάÂëºÍ UUID¡£
ͨ¹ý websocket Á¬½Ó socket ЧÀÍÆ÷£¬²¢°´Ê±(ʱ¼ä¼ä¸ô¸ù¾ÝЧÀÍÆ÷ÅäÖÃʱ¼äµ÷Õû)·¢ËÍÐÄÌø±£³ÖÁ¬½Ó¡£
»áԱͨ¹ý APP ɨÃè¶þάÂ룬·¢ËÍ¿ÒÇóµ½ÒµÎñЧÀÍÆ÷´¦ÖõǼ¡£°´ÕÕ UUID É趨µÇ¼½á¹û¡£
socket ЧÀÍÆ÷ͨ¹ý¼àÌýÁÔÈ¡µÇ¼½á¹û£¬´´Á¢ session Êý¾Ý£¬°´ÕÕ UUID ÍÆË͵ǼÊý¾Ýµ½»áÔ±ÔĶÁÆ÷¡£
»áÔ±µÇ¼³É¹¦£¬Ð§ÀÍÆ÷Ö÷¶¯½«¸Ã socker Á¬½Ó´ÓÁ¬½Ó³ØÖÐÌÞ³ý£¬¸Ã¶þάÂëʧЧ¡£
¹ØÓÚ¿Í»§¶Ë±êʶ
Ò²¾ÍÊÇ UUID£¬ÕâÊǹᴩÕû¸öÁ÷³ÌµÄŦ´ø£¬Ò»¸ö±Õ»·µÇ¼Àú³Ì£¬Ã¿Ò»²½ÒµÎñ´¦Öö¼ÊÇ»·ÈÆ¸Ã´ÎµÄ UUD Í£Ö¹´¦Öõġ£UUID µÄÉú³ÉÓа´ÕÕ session_id µÄÒ²Óа´ÕÕ¿Í»§¶Ë ip µØÖ·µÄ¡£¸öÈË»¹Êdz«Òéÿ¸ö¶þάÂ붼ÓжÀ×﵀ UUID£¬ÊÊÓô¦¾°¸ü¹ãһЩ£¡
¹ØÓÚÇ°¶ËºÍЧÀÍÆ÷ͨѶ
Ç°¶Ë±Ø¶¨ÊÇÒªºÍЧÀÍÆ÷±£³Ö²»¶ÏͨѶµÄ£¬ÓÃÒÔÁÔÈ¡µÇ¼½á¹ûºÍ¶þάÂë״̬¡£¿´ÁËÏÂÍøÉϵÄһЩʵÏÖ·½°¸£¬¸ù±¾¸÷¸ö·½°¸¶¼ÓÐÓõģºÂÖѯ¡¢³¤ÂÖѯ¡¢³¤Á´½Ó¡¢websocket¡£Ò²²»¿Ë²»¼°±Ø¶¨µÄ˵Äĸö·½°¸ºÃÄĸö·½°¸²»ºÃ£¬Ö»ÄÜ˵Äĸö·½°¸¸üÊÊÓÃÓÚµ±Ç°ÀûÓó¡¾°¡£¸öÈ˱ÈÁ¦³«ÒéʹÓó¤ÂÖѯ¡¢websocket ÕâÖÖ±ÈÁ¦½ÚԼЧÀÍÆ÷»úÄܵķ½°¸¡£
¹ØÓÚƽ°²ÐÔ
ɨÂëµÇ¼µÄºÃ´¦²»ÑÔ¶øÓ÷£¬Ò»ÊÇÈËÐÔ»¯£¬ÔÙ¾ÍÊÇ·ÀÖ¹ÃÜÂë×ß©¡£µ«ÊÇз½Ê½µÄ½ÓÈ룬ÍùÍùÒ²Åã°é×ÅеķçÏÕ¡£ËùÒÔ£¬ºÜÓбØÒªÔÙÍÅÌåÀú³ÌÖвμÓÇ¡µ±µÄƽ°²»úÖÆ¡£ÀýÈ磺
- Ç¿ÆÈ HTTPS ºÍ̸
- ¶ÌÆÚÁîÅÆ
- Êý¾ÝÇ©Ãû
- Êý¾Ý¼ÓÃÜ
ɨÂëµÇ¼µÄÀú³ÌÑÝʾ
´úÂëʵÏÖºÍÔ´ÂëºóÃæ»á¸ø³ö¡£
¿ªÆô Socket ЧÀÍÆ÷
°Ý·ÃµÇ¼ҳÃæ
¿ÉÒÔ¿´µ½»áÔ±¿ÒÇóµÄ¶þάÂë×ÊÔ´£¬²¢ÁÔÈ¡µ½ÁË qid
¡£
ÁÔÈ¡¶þάÂëʱºò»á´´Á¢ÏìÓ¦»º´æ£¬²¢É趨¹ýÆÚʱ¼ä£º
Ö®ºó»áÁ¬½Ó socket ЧÀÍÆ÷£¬°´Ê±·¢ËÍÐÄÌø¡£
´Ëʱ socket ЧÀÍÆ÷»áÓÐÏìÓ¦Á¬½ÓÈÕÖ¾Êä³ö£º
»áԱʹÓà APP ɨÂë²¢ÊÚȨ
ЧÀÍÆ÷ÑéÖ¤²¢´¦ÖõǼ£¬´´Á¢ session£¬´´Á¢¶ÔÓ¦µÄ»º´æ£º
Socket ЧÀÍÆ÷¶ÁÈ¡µ½»º´æ£¬¿ª¶ËÍÆËÍÐÅÏ¢£¬²¢¹Ø±ÕÌÞ³ýÁ¬½Ó£º
Ç°¶ËÁÔÈ¡ÐÅÏ¢£¬´¦ÖõǼ£º
ɨÂëµÇ¼µÄʵÏÖ
ÁôÒ⣺±¾ Demo Ö»ÊǸöÈËѧϰ²âÊÔ£¬ËùÒÔ²¢Î´×öÌ«¶àƽ°²»úÖÆ£¡
Socket ´úÀíЧÀÍÆ÷
ʹÓà Nginx ×÷Ϊ´úÀí socke ЧÀÍÆ÷¡£¿ÉʹÓÃÓòÃû£¬Àû±ã×ö¸ºÔØƽºâ¡£±¾´Î²âÊÔÓòÃû£ºloc.websocket.net
websocker.conf
server { listen 80; server_name loc.websocket.net; root /www/websocket; index index.php index.html index.htm; #charset koi8-r; access_log /dev/null; #access_log /var/log/nginx/nginx.localhost.access.log main; error_log /var/log/nginx/nginx.websocket.error.log warn; #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } location / { proxy_pass http://php-cli:8095/; proxy_http_version 1.1; proxy_connect_timeout 4s; proxy_read_timeout 60s; proxy_send_timeout 12s; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } }
Socket ЧÀÍÆ÷
ʹÓà PHP ¹¹½¨µÄ socket ЧÀÍÆ÷¡£Êµ¼ÊÏîÄ¿Öдó¼Ò¿ÉÒÔ˼¿¼Ê¹ÓõÚÈý·½ÀûÓ㬲»ÂÒÐÔ¸üºÃһЩ£¡
QRServer.php
<?php require_once dirname(dirname(__FILE__)) . '/Config.php'; require_once dirname(dirname(__FILE__)) . '/lib/RedisUtile.php'; require_once dirname(dirname(__FILE__)) . '/lib/Common.php';/** * ɨÂëµÇ½ЧÀÍ¶Ë * Class QRServer * @author BNDong */class QRServer { private $_sock; private $_redis; private $_clients = array(); /** * socketServer constructor. */ public function __construct() { // É趨 timeout set_time_limit(0); // ´´Á¢Ò»¸öÌ×½Ó×Ö£¨Í¨Ñ¶½Úµã£© $this->_sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("Could not create socket" . PHP_EOL); socket_set_option($this->_sock, SOL_SOCKET, SO_REUSEADDR, 1); // °ó¶¨µØÖ· socket_bind($this->_sock, \Config::QRSERVER_HOST, \Config::QRSERVER_PROT) or die("Could not bind to socket" . PHP_EOL); // ¼àÌýÌ×½Ó×ÖÉϵÄÁ¬½Ó socket_listen($this->_sock, 4) or die("Could not set up socket listener" . PHP_EOL); $this->_redis = \lib\RedisUtile::getInstance(); } /** * Æô¶¯Ð§ÀÍ */ public function run() { $this->_clients = array(); $this->_clients[uniqid()] = $this->_sock; while (true){ $changes = $this->_clients; $write = NULL; $except = NULL; socket_select($changes, $write, $except, NULL); foreach ($changes as $key => $_sock) { if($this->_sock == $_sock){ // ÍƶÏÊDz»ÊÇнÓÈëµÄ socket if(($newClient = socket_accept($_sock)) === false){ die('failed to accept socket: '.socket_strerror($_sock)."\n"); } $buffer = trim(socket_read($newClient, 1024)); // ¶ÁÈ¡¿ÒÇó $response = $this->handShake($buffer); socket_write($newClient, $response, strlen($response)); // ·¢ËÍÏìÓ¦ socket_getpeername($newClient, $ip); // ÁÔÈ¡ ip µØÖ· $qid = $this->getHandQid($buffer); $this->log("new clinet: ". $qid); if ($qid) { // ÑéÖ¤¿É·ñ´æÔÚ qid if (isset($this->_clients[$qid])) $this->close($qid, $this->_clients[$qid]); $this->_clients[$qid] = $newClient; } else { $this->close($qid, $newClient); } } else { // Íƶ϶þάÂë¿É·ñ¹ýÆÚ if ($this->_redis->exists(\lib\Common::getQidKey($key))) { $loginKey = \lib\Common::getQidLoginKey($key); if ($this->_redis->exists($loginKey)) { // ÍƶϻáÔ±¿É·ñɨÂë $this->send($key, $this->_redis->get($loginKey)); $this->close($key, $_sock); } $res = socket_recv($_sock, $buffer, 2048, 0); if (false === $res) { $this->close($key, $_sock); } else { $res && $this->log("{$key} clinet msg: " . $this->message($buffer)); } } else { $this->close($key, $this->_clients[$key]); } } } sleep(1); } } /** * ¹¹½¨ÏìÓ¦ * @param string $buf * @return string */ private function handShake($buf){ $buf = substr($buf,strpos($buf,'Sec-WebSocket-Key:') + 18); $key = trim(substr($buf, 0, strpos($buf,"\r\n"))); $newKey = base64_encode(sha1($key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true)); $newMessage = "HTTP/1.1 101 Switching Protocols\r\n"; $newMessage .= "Upgrade: websocket\r\n"; $newMessage .= "Sec-WebSocket-Version: 13\r\n"; $newMessage .= "Connection: Upgrade\r\n"; $newMessage .= "Sec-WebSocket-Accept: " . $newKey . "\r\n\r\n"; return $newMessage; } /** * ÁÔÈ¡ qid * @param string $buf * @return mixed|string */ private function getHandQid($buf) { preg_match("/^[\s\n]?GET\s+\/\?qid\=([a-z0-9]+)\s+HTTP.*/", $buf, $matches); $qid = isset($matches[1]) ? $matches[1] : ''; return $qid; } /** * ±àÒë·¢ËÍÊý¾Ý * @param string $s * @return string */ private function frame($s) { $a = str_split($s, 125); if (count($a) == 1) { return "\x81" . chr(strlen($a[0])) . $a[0]; } $ns = ""; foreach ($a as $o) { $ns .= "\x81" . chr(strlen($o)) . $o; } return $ns; } /** * ½âÎö½ÓÊÕÊý¾Ý * @param resource $buffer * @return null|string */ private function message($buffer){ $masks = $data = $decoded = null; $len = ord($buffer[1]) & 127; if ($len === 126) { $masks = substr($buffer, 4, 4); $data = substr($buffer, 8); } else if ($len === 127) { $masks = substr($buffer, 10, 4); $data = substr($buffer, 14); } else { $masks = substr($buffer, 2, 4); $data = substr($buffer, 6); } for ($index = 0; $index < strlen($data); $index++) { $decoded .= $data[$index] ^ $masks[$index % 4]; } return $decoded; } /** * ·¢ËÍÐÂÎÅ * @param string $qid * @param string $msg */ private function send($qid, $msg) { $frameMsg = $this->frame($msg); socket_write($this->_clients[$qid], $frameMsg, strlen($frameMsg)); $this->log("{$qid} clinet send: " . $msg); } /** * ¹Ø±Õ socket * @param string $qid * @param resource $socket */ private function close($qid, $socket) { socket_close($socket); if (array_key_exists($qid, $this->_clients)) unset($this->_clients[$qid]); $this->_redis->del(\lib\Common::getQidKey($qid)); $this->_redis->del(\lib\Common::getQidLoginKey($qid)); $this->log("{$qid} clinet close"); } /** * ÈÕÖ¾¼ÇÔØ * @param string $msg */ private function log($msg) { echo '['. date('Y-m-d H:i:s') .'] ' . $msg . "\n"; } } $server = new QRServer(); $server->run();
µÇ¼ҳÃæ
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>ɨÂëµÇ¼ - ²âÊÔÒ³Ãæ</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" type="text/css" href="./public/css/main.css"> </head> <body translate="no"> <p class='box'> <p class='box-form'> <p class='box-login-tab'></p> <p class='box-login-title'> <p class='i i-login'></p><h2>µÇ¼</h2> </p> <p class='box-login'> <p class='fieldset-body' id='login_form'> <button onclick="openLoginInfo();" class='b b-form i i-more' title='Mais Informa??es'></button> <p class='field'> <label for='user'>»áÔ±ÕË»§</label> <input type='text' id='user' name='user' title='Username' placeholder="ÇëÊäÈë»áÔ±ÕË»§/ÓÊÏäµØÖ·" /> </p> <p class='field'> <label for='pass'>»áÔ±ÃÜÂë</label> <input type='password' id='pass' name='pass' title='Password' placeholder="ÇéÊäÈëÕË»§ÃÜÂë" /> </p> <label class='checkbox'> <input type='checkbox' value='TRUE' title='Keep me Signed in' /> ¼ÇסÎÒ </label> <input type='submit' id='do_login' value='µÇ¼' title='µÇ¼' /> </p> </p> </p> <p class='box-info'> <p><button onclick="closeLoginInfo();" class='b b-info i i-left' title='Back to Sign In'></button><h3>ɨÂëµÇ¼</h3> </p> <p class='line-wh'></p> <p style="position: relative;"> <input type="hidden" id="qid" value=""> <p id="qrcode-exp">¶þάÂëÒÑʧЧ<br>µã»÷´ÓÐÂÁÔÈ¡</p> <img id="qrcode" src="" /> </p> </p> </p> <script src='./public/js/jquery.min.js'></script> <script src='./public/js/modernizr.min.js'></script> <script id="rendered-js"> $(document).ready(function () { restQRCode(); openLoginInfo(); $('#qrcode-exp').click(function () { restQRCode(); $(this).hide(); }); }); /** * ·¿ª¶þάÂë */ function openLoginInfo() { $(document).ready(function () { $('.b-form').css("opacity", "0.01"); $('.box-form').css("left", "-100px"); $('.box-info').css("right", "-100px"); }); } /** * ¹Ø±Õ¶þάÂë */ function closeLoginInfo() { $(document).ready(function () { $('.b-form').css("opacity", "1"); $('.box-form').css("left", "0px"); $('.box-info').css("right", "-5px"); }); } /** * ˢжþάÂë */ var ws, wsTid = null; function restQRCode() { $.ajax({ url: 'http://localhost/qrcode/code.php', type:'post', dataType: "json", async: false, success:function (result) { $('#qrcode').attr('src', result.img); $('#qid').val(result.qid); } }); if ("WebSocket" in window) { if (typeof ws != 'undefined'){ ws.close(); null != wsTid && window.clearInterval(wsTid); } ws = new WebSocket("ws://loc.websocket.net?qid=" + $('#qid').val()); ws.onopen = function() { console.log('websocket ÒÑÁ¬½ÓÉÏ£¡'); }; ws.onmessage = function(e) { // todo: ±¾º¯Êý×öµÇ¼´¦Ö㬵ǼÍƶϣ¬´´Á¢»º´æÐÅÏ¢£¡ console.log(e.data); var result = JSON.parse(e.data); console.log(result); alert('µÇ¼³É¹¦£º' + result.name); }; ws.onclose = function() { console.log('websocket Á¬½ÓÒѹرգ¡'); $('#qrcode-exp').show(); null != wsTid && window.clearInterval(wsTid); }; // ·¢ËÍÐÄÌø wsTid = window.setInterval( function () { if (typeof ws != 'undefined') ws.send('1'); }, 50000 ); } else { // todo: ²»Ö§³Å WebSocket µÄ£¬¿ÉÒÔʹÓà js ÂÖѯ´¦Öã¬ÕâÀï²»×÷¸Ã¹¦Ð§ÊµÏÖ£¡ alert('ÄúµÄÔĶÁÆ÷²»Ö§³Å WebSocket£¡'); } }</script> </body> </html>
µÇ¼´¦ÖÃ
²âÊÔʹÓã¬Ä£ÄâµÇ¼´¦Öã¬Î´×öƽ°²ÈÏÖ¤£¡£¡
<?php require_once dirname(__FILE__) . '/lib/RedisUtile.php'; require_once dirname(__FILE__) . '/lib/Common.php';/** * ------- µÇ¼Â߼ģÄâ -------- * Çë°´ÕÕʵ¼Ê±àдµÇ¼Âß¼²¢´¦ÖÃƽ°²ÑéÖ¤ */$qid = $_GET['qid']; $uid = $_GET['uid']; $data = array();switch ($uid) { case '1': $data['uid'] = 1; $data['name'] = 'ÕÅÈý'; break; case '2': $data['uid'] = 2; $data['name'] = 'ÀîËÄ'; break; } $data = json_encode($data); $redis = \lib\RedisUtile::getInstance(); $redis->setex(\lib\Common::getQidLoginKey($qid), 1800, $data);
¸ü¶àÏà¹Ø֪ʶ£¬Çë°Ý·ÃPHPÖÐÎÄÍø£¡
ÒÔÉϾÍÊÇ·ÖÏíPHPɨÂëµÇ¼µÀÀí¼°ÊµÏÖ°ì·¨µÄ¾ßÌåÄÚÈÝ£¬¸ü¶àÇë¹Ø×¢°Ù·Ö°ÙÔ´ÂëÍøÆäËüÏà¹ØÎÄÕ£¡
¸ÐлÄúµÄÖ§³Ö£¬ÎÒ»á¼ÌÐøŬÁ¦µÄ!
´ò¿ªÖ§¸¶±¦É¨Ò»É¨£¬¼´¿É½øÐÐɨÂë´òÉÍŶ
°Ù·Ö°ÙÔ´ÂëÍø ½¨Òé´òÉÍ1¡«10Ôª£¬ÍÁºÀËæÒ⣬¸ÐлÄúµÄÔĶÁ£¡