/** * mime类型的 map * @ author Cheng Liufeng * @ date 2014/8/30 * 当请求静态服务器文件的类型 html, css, gif, jpg, png, javascript, json, plain text, 我们会在此文件进行映射 */exports.types = { "css": "text/css", "gif": "image/gif", "html": "text/html", "ico": "image/x-icon", "jpeg": "image/jpeg", "jpg": "image/jpeg", "js": "text/javascript", "json": "application/json", "pdf": "application/pdf", "png": "image/png", "svg": "image/svg+xml", "swf": "application/x-shockwave-flash", "tiff": "image/tiff", "txt": "text/plain", "wav": "audio/x-wav", "wma": "audio/x-ms-wma", "wmv": "video/x-ms-wmv", "xml": "text/xml"};/** * 聊天室服务端 * 功能:实现了Node版的静态服务器 * 实现了缓存,gzip压缩等 * @ author Cheng Liufeng * @ date 2014/8/30 */ // 设置端口号var PORT = 3000;// 引入模块var http = require("http");var url = require("url");var fs = require("fs");var path = require("path");var zlib = require("zlib");// 引入文件var mime = require("./mime").types;var config = require("./config");var chatServer = require("./utils/chat_server");var server = http.createServer(function (req, res) { res.setHeader("Server","Node/V8"); // 获取文件路径 var pathName = url.parse(req.url).pathname; if(pathName.slice(-1) === "/"){ pathName = pathName + "index.html"; //默认取当前默认下的index.html } // 安全处理(当使用Linux 的 curl命令访问时,存在安全隐患) var realPath = path.join("client", path.normalize(pathName.replace(/../g, ""))); // 检查文件路径是否存在 path.exists(realPath, function(exists) { // 当文件不存在时的情况, 输出一个404错误 if (!exists) {res.writeHead(404, "Not Found", {"Content-Type": "text/plain"});res.write("The request url" + pathName +" is not found!");res.end(); } else { // 当文件存在时的处理逻辑fs.stat(realPath, function(err, stat) {// 获取文件扩展名var ext = path.extname(realPath);ext = ext ? ext.slice(1) : "unknown";var contentType = mime[ext] || "text/plain";// 设置 Content-Typeres.setHeader("Content-Type", contentType);var lastModified = stat.mtime.toUTCString();var ifModifiedSince = "If-Modified-Since".toLowerCase();res.setHeader("Last-Modified", lastModified);if (ext.match(config.Expires.fileMatch)) { var expires = new Date(); expires.setTime(expires.getTime() + config.Expires.maxAge * 1000); res.setHeader("Expires", expires.toUTCString()); res.setHeader("Cache-Control", "max-age=" + config.Expires.maxAge);}if (req.headers[ifModifiedSince] && lastModified == req.headers[ifModifiedSince]) { res.writeHead(304, "Not Modified"); res.end();} else { // 使用流的方式去读取文件 var raw = fs.createReadStream(realPath); var acceptEncoding = req.headers["accept-encoding"] || ""; var matched = ext.match(config.Compress.match); if (matched && acceptEncoding.match(/gzip/)) { res.writeHead(200, "Ok", {"Content-Encoding": "gzip"}); raw.pipe(zlib.createGzip()).pipe(res); } else if (matched && acceptEncoding.match(/deflate/)) { res.writeHead(200, "Ok", {"Content-Encoding": "deflate"}); raw.pipe(zlib.createDeflate()).pipe(res); } else { res.writeHead(200, "Ok"); raw.pipe(res); }//下面是普通的读取文件的方式,不推荐// fs.readFile(realPath, "binary", function(err, data) {// if(err) {//// file exists, but have some error while read//res.writeHead(500, {"Content-Type": "text/plain"});//res.end(err);// } else {//// file exists, can success return//res.writeHead(200, {"Content-Type": contentType});//res.write(data, "binary");//res.end();// }// });}}); } });});//监听3000端口server.listen(PORT, function() { console.log("Server is listening on port " + PORT + "!");});chatServer.listen(server);二,服务端利用WebSocket构建聊天室服务端
/** * 聊天服务。 */var socketio = require("socket.io");var io;var guestNumber = 1; //初始用户名编号var nickNames = {}; // 昵称列表var namesUsed = []; //使用过的用户名var currentRoom = {}; //当前聊天室function assignGuestName(socket, guestNumber, nickNames, namesUsed) { var name = "Guest" + guestNumber; nickNames[socket.id] = name; socket.emit("nameResult", { success: true, name: name }); namesUsed.push(name); return guestNumber + 1;}function joinRoom(socket, room) { socket.join(room); currentRoom[socket.id] = room; socket.emit("joinResult", {room: room}); socket.broadcast.to(room).emit("message", { text: nickNames[socket.id] + "has joined " + room + "." });}function handleMessageBroadcasting(socket) { socket.on("message", function(message) { socket.broadcast.to(message.room).emit("message", {text: nickNames[socket.id] + ":" + message.text }); });}exports.listen = function(server) { io = socketio.listen(server); io.set("log level", 1); // 定义每个用户的连接处理 io.sockets.on("connection", function(socket) { // 分配一个用户名 guestNumber = assignGuestName(socket, guestNumber, nickNames, namesUsed); // 将用户加入聊天室Lobby里 joinRoom(socket, "Lobby"); //处理聊天信息 handleMessageBroadcasting(socket, nickNames); //handleNameChangeAttempts(socket, nickNames, namesUsed); //handleRoomJoining(socket); //handleClientDisconnection(socket, nickNames, namesUsed); });};三,利用Angular搭建聊天室客户端<!DOCTYPE html><html ng-app="chatApp"><head> <meta name="viewport" content="width=device-width, user-scalable=no"></head><body ng-controller="InitCtrl"> <div ng-view></div> <script src="lib/angular.js"></script> <script src="lib/angular-route.js"></script> <script src="lib/socket.io.js"></script> <script src="app.js"></script> <script src="controllers/InitCtrl.js"></script></body></html>怎样构建一个单页应用?单页应用的原理?
/** * 客户端(目前只支持浏览器,将来会扩展到移动端)程序入口文件 * 创建一个模块,并且命名为chatApp * 配置路由,实现单页应用(single page application) */ var chatApp = angular.module("chatApp", ["ngRoute"]); // 路由配置 chatApp.config(function($routeProvider) { $routeProvider.when("/", {templateUrl : "views/init.html",controller: "InitCtrl" }) .when("/init", {templateUrl : "views/init.html",controller: "InitCtrl" }); });客户端聊天界面的代码逻辑如下/** * # InitCtrl */angular.module("chatApp").controller("InitCtrl", function($scope) { var socket = io.connect("http://127.0.0.1:3000"); socket.on("nameResult", function(result) { var message; if (result.success) {message = "you are now known as " + result.name + "."; console.log("message=", message);document.getElementById("guestname").innerHTML = message; } else {message = result.message; } }); socket.on("joinResult", function(result) { document.getElementById("room").innerHTML = result.room; }); $scope.sendMessage = function() { var message = {room: "Lobby",text: document.getElementById("user_input").value }; socket.emit("message", message); }; socket.on("message", function(message) { var p = document.createElement("p"); p.innerHTML = message.text; document.getElementById("message").appendChild(p); });});基于node.js和socket.io搭建多人聊天室<script src="/socket.io/socket.io.js"></script>
<script src="http://ip:port/socket.io/socket.io.js"></script>
var io = require("socket.io").listen(80);io.sockets.on("connection", function (socket) { socket.broadcast.emit("user connected"); socket.broadcast.json.send({ a: "message" });});io.sockets.emit("users_count", clients);




五、源码下载
Nodejs多人聊天室(点击此处下载源码)
ps:
1、在命令行运行
node main.js然后在浏览器中打开index.html,如果浏览器(ff、Chrome)不支持,请升级到支持WebSocket的版本.