モジュールシンプル設計——sockete.ioチャットコード

16257 ワード

インターネット上の各種教程とコードを参照して、簡単なオンラインチャットを実現し、指向性の発信をサポートします。
サーバ端
app.js(expressを使ってスタティックサービスを構築し、socket.ioでサーバー側のモニターを設立する)
 
var app = require('express')()
, express = require('express')
, server = require('http').createServer(app)
, io = require('socket.io').listen(server);

var zTool = require("./zTool");
var onlineUserMap = new zTool.SimpleMap();
var historyContent = new zTool.CircleList(100);

EVENT_TYPE = {
	"LOGIN":"LOGIN",
	"LOGOUT":"LOGOUT",
	"CHAT":"CHAT",
	"ERROR":"ERROR"
};

server.listen(80);

app.use('/', express.static(__dirname + '/'));

io.sockets.on('connection', function (socket){
		//
		// exception from clients
		//
		socket.on("disconnect", function (){
			var user = onlineUserMap.get(socket.id);
			var data = JSON.stringify({
				"event":EVENT_TYPE.LOGOUT,
				"values":[user]
				});
			onlineUserMap.remove(socket.id);
			io.sockets.emit("message",data);//broadcast
			});
		//
		// normal messages(login | logout | chat)
		//
		socket.on('message', function (message){
			//json decode
			var msgData = JSON.parse(message);

			if(msgData && msgData.event){
			switch(msgData.event) {
			//
			//user login
			//
			case EVENT_TYPE.LOGIN:
			var newUser = {"uid":socket.id, "nick":msgData.values[0]};
			onlineUserMap.put(socket.id, newUser);

			var data = JSON.stringify({
				"user":onlineUserMap.get(socket.id),
				"event":EVENT_TYPE.LOGIN,
				"values":[newUser],
				"users":onlineUserMap.values(),
				"historyContent":historyContent.values()
				});
			io.sockets.emit("message",data);//broadcast
			break;
			//
			//user chat
			//
			case EVENT_TYPE.CHAT:
			var content = msgData.values[0];
			var data = JSON.stringify({
					"user":onlineUserMap.get(socket.id),
					"event":EVENT_TYPE.CHAT,
					"values":[content]
					});

			var to = msgData.to;
			if(to != 0){//private chat
				io.sockets.emit(socket.id,data); //to sender
				io.sockets.emit(to,data);//to receiver
			}
			else{//public chat
				io.sockets.emit("message",data);//broadcast
				historyContent.add({
						"user":onlineUserMap.get(socket.id),
						"content":content,
						"time":new Date().getTime()
						});
			}
			break;
			//
			//user logout
			//
			case EVENT_TYPE.LOGOUT:
			var user = msgData.values[0];
			onlineUserMap.remove(user.uid);
			var data = JSON.stringify({
					"event":EVENT_TYPE.LOGOUT,
					"values":[user]
					});
			io.sockets.emit("message",data);//broadcast
			break; 
			}
			} else {
				console.log("invalid message , userId:"+socket.id+" , message:"+message);
				var data = JSON.stringify({
						"uid":socket.id,
						"event":EVENT_TYPE.ERROR
						});
				socket.emit(socket.id, data);
			}
		});
});
ウェブ端末index.
 
<html>
<head>
	<title>chatroom</title>
	<meta charset="utf-8" />
	<script src="jquery.js"></script>
	<script src="zTool.js"></script>
	<script src="DateUtil.js"></script>
	<script src="chat.js"></script>
	<script src="http://127.0.0.1:80/socket.io/socket.io.js"></script>
	<link href="style.css" rel="stylesheet" media="all">
</head>
<body>

<div id="errorPage" class="page" style="display:none">
	<h1>        WebSocket,        ,  chrome 4.0.249.0 +</h1>
</div>

<div id="prePage" class="page" style="text-align:center;">
	<input id = "nickInput" type="text" class="itext" placeholder="    " style="margin-top:150px;" />
	<input id = "enter" type="button" class="ibutton" value="    " />
</div>
<div id="mainPage" class="page" style="display:none">
	<div>
		<div class="talkLeft">
			<div id="talkFrame" class="talkHistory">
			</div>
		</div>
		<div class="talkRight">
			<div id="onlineUsers"></div>
		</div>
	</div>
	<div id="inputDiv" style="margin-top:50px">
		<input placeholder="    " id="message" class="itext" />
		<input type="button" class="ibutton" value="  " id="send" />
		<input type="button" class="ibutton" value="  " id="logout" />
		<select style="float:right" id="chatWith" class="iselect">
			<option>   </option>
		</select>
	</div>
</div>
</body>
</html>
 ウェブ端末のchat.js(各種ニュースを傍受し、サーバの異常切断など)
 
EVENT_TYPE = {
	"LOGIN":"LOGIN",
	"LOGOUT":"LOGOUT",
	"CHAT":"CHAT",
	"ERROR":"ERROR"
};

$(document).ready(function() {

		var socket = null;
		var onlineUserMap = new zTool.SimpleMap();
		var currentUser = null;
		var currentUserNick = null;

		if (typeof WebSocket === 'undefined') {
		$("#prePage").hide();
		$("#errorPage").show();
		}

		function appendMessage(msg) {
		$("#talkFrame").append("<div>" + msg + "</div>");
		document.getElementById("talkFrame").scrollTop = document.getElementById("talkFrame").scrollHeight;
		}	

		function formatUserTalkString(user) {
			return user.nick + "  " + new Date().format("hh:mm:ss") + " ";
		}

		function formatUserTalkHisString(user, time) {
			return user.nick + "  " + new Date(time).format("yyyy-MM-dd hh:mm:ss") + " ";
		}

		function reset() {
			if (socket) {
				socket.close();
			}
			socket = null;
			onlineUserMap = null;
			currentUser = null;
			$("#onlineUsers").html("");
			$("#talkFrame").html("");
			$("#nickInput").val("");
			$("#chatWith").val("<option>   </option>");
		}

		function setChatWith(uid){
			$("#chatWith").find(uid).attr("SELECTED","SELECTED");
		}

		function updateOnlineUser() {
			var html = ["<div>    (" + onlineUserMap.size() + ")</div>"];
			if (onlineUserMap.size() > 0) {
				var users = onlineUserMap.values();
				var number = users.length;
				for ( var i=0;i<number;i++) {
					html.push("<div>");
					if (users[i].uid == currentUser.uid) {
						html.push("<b>" + users[i].nick + "( )</b>");
					} else {
						html.push(users[i].nick);
					}
					html.push("</div>");
				}
			}

			$("#onlineUsers").html(html.join(''));
		}

		function updateChatWith() {
			var html = ["<option value=\"0\">   </option>"];
			if (onlineUserMap.size() > 0) {
				var users = onlineUserMap.values();
				var number = users.length;
				for ( var i=0;i<number;i++) {
					if (users[i].uid == currentUser.uid) {
					} else {
						html.push("<option value=\""+users[i].uid+"\">");
						html.push(users[i].nick);
						html.push("</option>");
					}
				}
			}

			$("#chatWith").html(html.join(''));
		}

		//enter chatroom
		$("#enter").click(function(event) {
				currentUserNick = $.trim($("#nickInput").val());
				if ('' == currentUserNick) {
				alert('      ');
				return;
				}

				$("#prePage").hide();
				$("#mainPage").show();
				reset();

				socket = io.connect('http://127.0.0.1');
				onlineUserMap = new zTool.SimpleMap();
				socket.on('connect', function () {
					socket.emit('message', JSON.stringify({
							'event' : EVENT_TYPE.LOGIN,
							'values' : [currentUserNick]
							}));
					});

				socket.on("disconnect",function(message){
					$("#prePage").show();
					$("#mainPage").hide();
					close();
					});

				socket.on("message",function(message){
						var mData = JSON.parse(message);
						if (mData && mData.event) {
						switch (mData.event) {
						//
						// user login
						//
						case EVENT_TYPE.LOGIN:
						var user = mData.values[0];
						var users = mData.users;
						if (users && users.length) {
						var number = users.length;
						for (var i=0;i<number;i++) {
						onlineUserMap.put(users[i].uid, users[i]);
						if (mData.user.uid == users[i].uid && currentUser == null) {
						currentUser = users[i];
						//
						// listen on private chat
						//
						socket.on(currentUser.uid,function(pmessage){
							var pmData = JSON.parse(pmessage);
							if (pmData && pmData.event) {
							switch (pmData.event) {
							case EVENT_TYPE.CHAT:
							var content = pmData.values[0];
							appendMessage(formatUserTalkString(pmData.user));
							appendMessage("<span>&nbsp;&nbsp;</span>" + content);
							break;
							}
							}
							});
						}
						}
						}
						//
						// get history message
						//
						var data = mData.historyContent;

						if (data && data.length) {
							var number = data.length;
							for ( var i=0;i<number;i++) {
								appendMessage(formatUserTalkHisString(data[i].user, data[i].time));
								appendMessage("<span>&nbsp;&nbsp;</span>" + data[i].content);
							}
							appendMessage("<span class='gray'>==================          ==================</span>");
						}

						updateOnlineUser();
						updateChatWith();
						appendMessage(formatUserTalkString(user) + "[    ]");
						break;

						//
						// user logout
						//
						case EVENT_TYPE.LOGOUT: 
						var user = mData.values[0];
						onlineUserMap.remove(user.uid);
						updateOnlineUser();
						updateChatWith();
						appendMessage(formatUserTalkString(user) + "[    ]");
						break;

						//
						// user public chat
						//
						case EVENT_TYPE.CHAT:
						var content = mData.values[0];
						appendMessage(formatUserTalkString(mData.user));
						appendMessage("<span>&nbsp;&nbsp;</span>" + content);
						break;

						case EVENT_TYPE.ERROR:
						appendMessage("[error state...]");
						break;

						default:
						break;
						}

						}

				});

				socket.on("error",function(){
						appendMessage("[server encounts an error...]");
						});


				socket.on("close",function(){
						appendMessage("[server is closed...]");
						close();
						});

		});


		//send a message
		$("#send").click(function(event) {
				var value = $.trim($("#message").val());
				var to = $.trim($("#chatWith").val());
				if (value) {
				$("#message").val('');
				var data = JSON.stringify({
					'event' : EVENT_TYPE.CHAT,
					'to' : to,
					'values' : [value]
					});
				socket.emit('message',data);
				}
				});

		//logout
		$("#logout").click(function(event){
				var data = JSON.stringify({
					'event' : EVENT_TYPE.LOGOUT,
					'values' : [currentUser]
					});
				socket.emit('message',data);
				$("#prePage").show();
				$("#mainPage").hide();
				});
});
 運転方式sudo node ap.js
添付:zTool.jsは以下の通りです。
(function (exports) {
	var SimpleMap = exports.SimpleMap = function() {
		this.map = {};
		this.mapSize = 0;
	};

	SimpleMap.prototype.put = function(key, value) {
		var oldValue = this.map[key];
		this.map[key] = value;
		if (!oldValue) {
			this.mapSize++;
		}
		return (oldValue || value);
	};

	SimpleMap.prototype.get = function(key) {
		return this.map[key];
	};

	SimpleMap.prototype.remove = function(key) {
		var v = this.map[key];
		if (v) {
			delete this.map[key];
			this.mapSize--;
		};
		return v;
	};

	SimpleMap.prototype.size = function() {
		return this.mapSize;
	};

	SimpleMap.prototype.clear = function() {
		this.map = {};
		this.mapSize = 0;
	};

	SimpleMap.prototype.keySet = function() {
		var theKeySet = [];
		for (var i in this.map) {
			theKeySet.push(i);
		}
		return theKeySet;
	};

	SimpleMap.prototype.values = function() {
		var theValue = [];
		for (var i in this.map) {
			theValue.push(this.map[i]);
		}
		return theValue;
	};

	var CircleList = exports.CircleList = function(maxSize) {
		this.maxSize = (maxSize || 10);
		this.list = [];
		this.index = null;
	};

	CircleList.prototype.clear = function() {
		this.list = [];
		this.index = null;
	};

	CircleList.prototype.add = function(value) {
		if (null == this.index) {
			this.index = 0;
		}

		this.list[this.index++] = value;
		
		if (this.index == this.maxSize) {
			this.index = 0;
		}
	};

	CircleList.prototype.values = function() {
		var theValue = [];
		if (null != this.index) {
			if (this.list.length == this.maxSize) {
				for (var i = this.index; i < this.maxSize; i++) {
					theValue.push(this.list[i]);
				}
			}

			for (var i = 0; i < this.index; i++) {
				theValue.push(this.list[i]);
			}
		}
		return theValue;
	};

})( (function(){
    if(typeof exports === 'undefined') {
        window.zTool = {};
        return window.zTool;
    } else {
        return exports;
    }
})() );