iT邦幫忙

DAY 4
5

且戰且走HTML5系列 第 4

且戰且走HTML5(4) 基本的Chat應用

大致上了解了Socket.IO的使用方式,接下來就來試試看,怎樣用它做出簡單的Chat功能。
基本的Chat應用,在伺服器端其實跟單純的Echo差不多。只是原本伺服器中connection事件取得的socket物件,原本只能傳送訊息給自己,這樣就無法做到像Chat的群播功能。

不過像這樣的基本需求,Socket.IO已經考慮好了,所以只要加上broadcast,把原本的 socket.emit('事件') 改成 socket.broadcast.emit('事件') ,就能把訊息傳送給自己之外的所有人。

除了訊息之外,使用Chat的時候,至少要知道是誰發的,所以額外加上一個簡單的設定nickname的功能,並且在有重複的nickname時自動加上遞增的數字來區別。

伺服器的程式很簡單:

 var fs = require('fs');
 var app = require('http').createServer(function(req, res) {
 	fs.readFile('test830.html', function(err, data) {
 		if(err) {
 			res.writeHead(500);
 			return res.end('Error reading default index.');
 		} else {
 			res.writeHead(200);
 			res.end(data);
 		}
 	});
 });
 var nickname = {};
 var io = require('socket.io').listen(app);
 io.sockets.on('connection', function(socket) {
	// setting nickname
 	socket.on('setnickname', function(m) {
 		if(typeof nickname[m] === 'undefined') {
 			nickname[m] = {count: 0};
 			socket.emit('nicknamesuccess', m);
 		} else {
 			nickname[m].count++;
 			var t = m + '' + nickname[m].count;
 			socket.emit('nicknamefail', t);
 		}
 	});
	// broadcast received message
 	socket.on('post', function(m) {
 		console.log(m);
 		socket.broadcast.emit('msg', m);
 	})
 });
 
 app.listen(80);

訊息傳送的部份很簡單,網頁的處理反而比較複雜XD

為了節省時間,顯示訊息的部份就用簡單的textarea來做。簡單的事件說明如下:

  1. setnickname:設定nickname
  2. nicknamesuccess:設定nickname成功
  3. nicknamefail:nickname已經有人用了,把使用的nickname自動改成設定的nickname加上遞增的數字
  4. post:把訊息送到伺服器,並且更新本地端的訊息顯示
  5. msg:接收伺服器送來的廣播訊息,並且更新到本地端的訊息顯示

使用流程規劃成一定要先輸入nickname,設定好nickname才能發送訊息。

來看看網頁端:

 <meta charset='utf-8'>
 
 <style>
 .container {
 	font-size: 12px;
 	border-radius: 10px;
 	border: solid 1px #336699;
 	padding: 15px 15px 15px 15px;
 	line-height: 20px;
 	width: 400px;
 }
 .disabled {
 	color: gray;
 }
 .enabled {
 	color: true;
 }
 </style>
 <script src='/socket.io/socket.io.js'></script>
 <script src='http://code.jquery.com/jquery-1.8.2.min.js'></script>
 <script>
 $(document).ready(function() {
 	var socket = io.connect();
 	var nickname = '';
 	$('#form1').submit(function(e) {
 		e.preventDefault();
 		socket.emit('setnickname', $('#nickname').val());
 	});
 	socket.on('nicknamesuccess', function(m) {
 		nickname = m;
 		$('#nickname').prop('disabled', true);
 		$('#sendnickname').prop('disabled', true);
 		$('#msg').prop('disabled', false).focus();
 		$('#send').prop('disabled', false);
 		$('#msglabel').prop('className', 'enabled');
 	});
 	socket.on('nicknamefail', function(m) {
 		alert('Nickname conflict. Your nickname will be changed to "'+m+'"');
 		nickname = m;
 		$('#nickname').val(m);
 		$('#nickname').prop('disabled', true);
 		$('#sendnickname').prop('disabled', true);
 		$('#msg').prop('disabled', false).focus();
 		$('#send').prop('disabled', false);
 		$('#msglabel').prop('className', 'enabled');
 	});
 	$('#form2').submit(function(e) {
 		e.preventDefault();
 		var m = $('#msg').val();
 		socket.emit('post', {nickname: nickname, msg: m});
 		$('#msg').val('');
 		updateMsg({nickname:nickname,msg:m});
 	})
 	socket.on('msg', function(m) {
 		console.log('got msg',m);
 		updateMsg(m);
 	});
 	function updateMsg(msg) {
 		var ta = $("#panel");
 		var t = new Date();
 		var s = t.getHours() + ':' + t.getMinutes() + ':' + t.getSeconds();
 		var m = '[ ' + msg.nickname + ' (' + s + ')]: ' + msg.msg;
 		ta.val(ta.val()+'\n'+m);
 		setTimeout(function(){
 			ta.scrollTop(ta[0].scrollHeight - ta.innerHeight());
 		},10);
 	}
 	$('#msg').focus();
 });
 </script>
 
 
 <div class="container">
 	<textarea cols='54' rows='24' id='panel' readonly></textarea><br>
 	<form id='form1' name='form1'>
 		<label id='nicknamelabel' class='enabled'>Your Nickname: </label><input type='text' size='20' id='nickname'><input type='submit' value='send' id='sendnickname'>
 	</form>
 	<form id='form2' name='form2'>
 		<label id='msglabel' class='disabled'>Message: </label><input type='text' size='54' id='msg' disabled><input type='submit' value='send' id='send' disabled>
 	</form>
 </div>
 
 

由於不論是在送出訊息後更新Chat顯示,或是收到廣播訊息後更新Chat顯示,其實作法都差不多,所以統一交給updateMsg函數來處理。由於在這個例子中,Socket跟Http共用相同的網址跟port,所以在連線時甚至不需要輸入網址。

執行畫面看起來會像這樣:

其實要做一個比較完整的Chat,還要考量更多的需求。不過這些到明天再說吧。


上一篇
且戰且走HTML5(3) 使用Socket.io
下一篇
且戰且走HTML5(5) 更深入Socket.IO
系列文
且戰且走HTML530

尚未有邦友留言

立即登入留言