https://chat.nkf.freewebhostmost.com/chat.php
https://www.cnblogs.com/zjfree
<?php // 系統入口 date_default_timezone_set("PRC"); error_reporting(E_ALL & ~E_NOTICE); set_time_limit(30); $room = $_REQUEST['room'] ?? 'default'; $type = $_REQUEST['type'] ?? 'enter'; $type = strtolower($type); //使用PHP做的單頁面線上聊天。 // //基本功能: // //1. 多人聊天 //2. 多房間 //3. 傳輸資訊加密,基於base64+字元替換實現 //4. 基於長連線讀取(ngnix使用PHP sleep有問題) //5. 支援暱稱自定義,並使用瀏覽器儲存。 //6. 需要在程式目錄建立chat_data資料夾,用來儲存歷史聊天資料(僅保留最近1分鐘) // 建立新房間 function newRoom($room) { $room_file = './chat_data/' . $room . '.txt'; $key_list = array_merge(range(48, 57), range(65, 90), range(97, 122), [43, 47, 61]); $key1_list = $key_list; shuffle($key1_list); $room_data = [ 'name' => $room, 'encode' => array_combine($key_list, $key1_list), 'list' => [], 'time' => date('Y-m-d H:i:s'), ]; file_put_contents($room_file, json_encode($room_data)); } // 獲取訊息列表 function getMsg($room, $last_id) { $room_file = './chat_data/' . $room . '.txt'; $msg_list = []; $room_data = json_decode(file_get_contents($room_file), true); $list = $room_data['list']; // 清楚1分鐘前訊息 $cur_list = []; $del_time = date('Y-m-d H:i:s', time() - 60); foreach ($list as $r) { if ($r['time'] > $del_time) { $cur_list[] = $r; } } if (count($cur_list) != count($list) && count($list) > 0) { $room_data['list'] = $cur_list; file_put_contents($room_file, json_encode($room_data)); } // 查詢最新訊息 foreach ($list as $r) { if ($r['id'] > $last_id) { $msg_list[] = $r; } } return $msg_list; } $room_file = './chat_data/' . $room . '.txt'; switch ($type) { case 'enter': // 進入房間 break; case 'get': // 獲取訊息 $last_id = $_REQUEST['last_id']; $msg_list = []; if (strpos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false) { $msg_list = getMsg($room, $last_id); } else { // nginx 使用sleep將會把整個網站卡死 for ($i=0; $i<20; $i++) { $msg_list = getMsg($room, $last_id); if (!empty($msg_list)) { break; } usleep(500000); } } echo json_encode(['result' => 'ok', 'list' => $msg_list]); break; case 'send': // 傳送訊息 $item = [ 'id' => round(microtime(true) * 1000), 'user' => $_REQUEST['user'], 'content' => $_REQUEST['content'], 'time' => date('Y-m-d H:i:s'), ]; $room_data = json_decode(file_get_contents($room_file), true); $room_data['list'][] = $item; file_put_contents($room_file, json_encode($room_data)); echo json_encode(['result' => 'ok']); break; case 'new': // 新建房間 mt_srand(); $room = strtoupper(md5(uniqid(mt_rand(), true))); $room = substr($room, 0, 10); newRoom($room); header('Location:chat.php?room=' . $room); break; default: echo 'ERROR:no type!'; break; } if ($type != 'enter') { exit; } if (!file_exists($room_file)) { if ($room == 'default') { newRoom($room); } else { echo 'ERROR:room not exists!'; exit; } } $room_data = json_decode(file_get_contents($room_file), true); unset($room_data['list']); $user = 'User' . str_pad((time() % 99 + 1), 2, '0', STR_PAD_LEFT); ?> <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="renderer" content="webkit"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>PHP 線上聊天</title> <link href="https://lib.baomitu.com/normalize/latest/normalize.min.css" rel="stylesheet"> <style> /* css style */ body{ padding:0 10px; } .divMain{ font-size:14px; line-height: 2; } #divList span{ color:gray; } </style> <script src="https://lib.baomitu.com/jquery/3.4.1/jquery.min.js"></script> </head> <body> <h1>PHP 線上聊天</h1> <div class="divMain"> <?php if ($room != 'default'){?> <div>複製網頁連結發給朋友一起聊天!</div> <?php } ?> 暱稱:<input id="txtUser" type="text" maxlength="50" value="<?=$user?>" /> <button onclick="$('#divList').html('');">清空</button> <a href="chat.php?type=new">新房間</a> <br> 內容:<input id="txtContent" type="text" value="" maxlength="100" style="width: 300px;" /> <button onclick="sendMsg();">傳送</button> <hr> <div id="divList"></div> </div> <!--使用worker獲取訊息資料,注意ngnix會阻塞整個程序--> <script id="worker" type="app/worker"> var room = '<?=$room_data['name']?>'; var isBusy = false; var lastId = -1; var urlBase = ''; addEventListener('message', function (evt) { urlBase = evt.data; }, false); setInterval(function(){ if (isBusy) return; isBusy = true; let url = new URL( 'chat.php?type=get&room=' + room + '&last_id=' + lastId, urlBase ); fetch(url) .then(res=>res.json()) .then(function(res){ isBusy = false; if (res.list.length > 0) { lastId = res.list[res.list.length-1].id; } self.postMessage(res); }) .catch(function(err){ isBusy = false; }); }, 1000); </script> <script> var blob = new Blob([document.querySelector('#worker').textContent]); var url = window.URL.createObjectURL(blob); var worker = new Worker(url); worker.onmessage = function (e) { let res = e.data; let html = ''; for (let k in res.list) { let r = res.list[k]; html = '<div><span>' + r.time + '</span> <b>' + r.user + ':</b> ' + decodeContent(r.content) + '</div>' + html; } $('#divList').prepend(html); }; worker.postMessage(document.baseURI); </script> <script> var room = <?=json_encode($room_data)?>; room['decode'] = {}; for (let k in room.encode) { room['decode'][room.encode[k]] = k; } // 傳送訊息 var lastSendTime = 0; function sendMsg() { let user = $('#txtUser').val().trim(); let content = $('#txtContent').val().trim(); if (content == '') { return; } if (user == '') { alert('暱稱不能為空'); return; } window.localStorage.setItem('r_' + room.name, user); // 限制0.5秒內僅允許傳送1條訊息 let curTime = new Date().getTime(); if (curTime - lastSendTime < 500) { return; } lastSendTime = curTime; $.ajax({ url:'chat.php?type=send', data:{room:room.name, user:user, content:encodeContent(content)}, type:'POST', dataType:'json', success:function(){ $('#txtContent').val(''); $('#txtContent').focus(); }, }); } // 訊息加密 function encodeContent(content) { content = encodeURIComponent(content); content = window.btoa(content); let str = ''; for (let i=0; i<content.length; i++) { str += String.fromCharCode(room.encode[content.charCodeAt(i)]); } return str; } // 訊息解密 function decodeContent(content) { let str = ''; for (let i=0; i<content.length; i++) { str += String.fromCharCode(room.decode[content.charCodeAt(i)]); } str = window.atob(str); str = decodeURIComponent(str); return str; } $(function(){ let userName = window.localStorage.getItem('r_' + room.name); if (userName) { $('#txtUser').val(userName); } $('#txtContent').keydown(function(e){ if(e.keyCode==13){ event.preventDefault(); sendMsg(); } }); $('#txtContent').val('😀 我來了!'); sendMsg(); }); </script> </body> </html>