标签:
1.只出现在用户访问聊天程序网站时:Web浏览器->Http请求->Node服务器->Http响应->Web浏览器
2.在用户聊天时持续发生:Web浏览器->WebSocket数据发送->Node服务器->WebSocket数据接收->Web浏览器
1.创建程序文档结构(如下图所示)
2.指明依赖项
程序的依赖项是在package.json文件中指明的。这个文件总是被放在程序的根目录下。package。json文件用于描述你的应用程序,它包含一些JSON表达式,并遵循CommonJS包描述标准。在package.json文件中可以定义很多事情,但最重要的是程序的名称、版本号、对程序的描述,以及程序的依赖项。
包描述文件
{ "name": "chatrooms", "version": "0.0.1", "description": "Minimalist multiroom chat server", "dependencies": { "socket.io": "~0.9.6", "mime": "~1.2.7" } }
3.安装依赖项
定义好package.json文件之后,安装程序的依赖项就是小菜一碟了。Node包管理器(npm)是Node自带的工具,他有很多功能,可以轻松安装第三方Node模块,可以把你自己创建的任何Node模块向全球发布。它用一行命令就能从package.json文件中读出依赖项,把他们都装好。
在根目录下(E:\nodeTest\chatrooms)输入如下命令
npm install
在看这个目录,你应该能看到node_modules目录,这个目录中存放的就是程序的依赖项。
4.创建静态文件服务器 server.js
(1).发送文件数据及错误响应
//(1)请求的文件不存在时发送404错误 function send404(response){ response.writeHead(404,{‘Content-Type‘:‘text/plain‘}); response.write(‘Error 404 :resource not found.‘); response.end(); } //(2)辅助函数提供文件数据服务。这个函数先写出正确的HTTP头,然后发送文件的内容。 function sendFile(response,filePath,fileContents){ response.writeHead( 200, {"Content-Type":mime.lookup(path.basename(filePath))} ); response.end(fileContents); } //(3)访问内存(RAM)要比访谈我呢文件系统快得多,所以Node程序通常会把常用的数据缓存到内存里。 //我们的聊天程序就要把静态文件缓存到内存中,只有第一次访问的时候才会从文件系统中读取。 //下一个辅助函数会确定文件是否缓存了,如果是,就返回它。如果文件还没被缓存,它会从硬盘中 //读取并返回它。如果文件不存在,则返回一个HTTP 404错误作为响应。 function serveStatic(response,cache,absPath){ if(cache[abspath]){//检查文件是否缓存在内存中 sendFile(response,absPath,cache[absPath]); //从内存中返回文件 }else{ fs.exists(absPath,function(exists){//检查文件是否存在 if(exists){ fs.readFile(absPath,function(err,data){//从硬盘中读取文件 if(err){ send404(response); }else{ cache[absPath]=data; sendFile(response,abspath,data);//从硬盘中读取文件并返回 } }); }else{ send404(response); } }); } }
(2).创建HTTP服务器
在创建HTTP服务期时,需要给createServer传入一个匿名函数作为回调函数,由它来处理每个HTTP请求。这个回调函数接受两个参数:request和response。在这个 回调函数执行时,HTTP服务器会分别组装这两个参数对象,以便你可以对请求的细节进行处理,并返回一个响应。
//创建HTTP服务器,用匿名函数定义对每个请求的处理行为 var server = http.createServer(function(request,response){ var filePath = false; if(request.url == ‘/‘){ filePath = ‘public/index.html‘; //确定返回的默认HTML文件 }else{ filePath = ‘public‘ + request.url; //将url路径转换成文件的相对路径 } var absPath = ‘./‘ + filePath; serveStatic(response,cache,absPath); //返回静态文件 })
(3).启动HTTP服务器
server.listen(3000,function(){ console.log("服务器已启动 端口号3000"); });
5.创建html和css
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>聊天室</title> <link rel=‘stylesheet‘ href=‘/css/style.css‘></link> </head> <body> <div id=‘content‘> <div id="room"></div> <div id=‘room-list‘></div> <div id=‘messages‘></div> <form id=‘send-form‘> <input id=‘send-message‘ /> <input id=‘send-button‘ type=‘submit‘ value=‘发送‘ /> <div id=‘help‘> 聊天室操作 <ul> <li>昵称:<code>/nick[username]</code></li> <li>进入/创建房间:<code>/join [room name]</code></li> </ul> </div> </form> </div> <script src=‘/socket.io/socket.io.js‘ type="text/javascript"></script> <script src=‘http://code.jquery.com/jquery.min.js‘ type=‘text/javascript‘></script> <script src=‘/js/chat.js‘ type=‘text/javascript‘></script> <script src=‘/js/chat_ui.js‘ type=‘text/javascript‘></script> </body> </html>
body{ padding:50px; font:14px "Lucida Grande", Helvetica ,Arial, sans-serif; } a{ color:#00B7FF; } #content{ width:800px; margin-left:auto; margin-right:auto; } #room{ background-color:#ddd; margin-bottom:1em; } #messages{ width:690px; height:300px; overflow:auto; background-color:#eee; margin-bottom:1em; margin-right:10px; } #room-list{ float: right; width:100px; height:300px; overflow:auto; } #room-list div{ border-bottom:1px solid #eee; } #room-list div:hover{ background-color:#ddd; } #send-message{ width:700px; margin-bottom:1em; margin-right:1em; } #help{ font:10px "Lucida grande", Helvetica, Arial ,sans-serif; }
6.用Socket,IO处理与聊天相关的消息
Socket.IO为Node及客户端JavaScript提供了基于WebSocket以及其他传输方式的封装,它提供了一个抽象层。如果浏览器没有实现WebSocket,Socket.IO会自动启用一个备选方案,而对外提供的API还是一样的。
Socket.IO提供了开箱即用的虚拟通道,所以程序不用把每条消息都向已经连接的用户广播,而只向那些预订了某个通道的用户广播。用这个功能实现程序里的聊天室非常简单,很快你就能看到。
Socket.IO还是事件发射器(Event Emitter)的好例子。事件发射器本质上是组织异步逻辑的一种很方便的设计模式。
事件发射器是跟某种资源相关联的,它能向这个资源发送消息,也能从这个资源接收消息。资源可以链接远程服务器,或者更抽象的东西。
(1)设置Socket.IO服务器
//一.设置Socket.IO服务器 var socketio = require(‘socket.io‘); var io; var guestNumber = 1; var nickNames= {}; var namesUsed = []; var currentRoom = {}; exports.listen = function(server){ //启动Socket.IO服务器,允许它搭载在已有的HTTP服务器上 io = socketio.listen(server); io.set(‘log level‘,1); //定义每个用户链接的处理逻辑 io.sockets.on(‘connection‘,function(socket){ console.log("a new user connection!"); //在用户链接上来时,赋予其一个访客名 guestNumber = assignGuestName(socket,guestNumber,nickNames,namesUsed); //在用户连接上来时把它放入聊天室Lobby里 joinRoom(socket,‘Lobby‘); handleMessageBroadcasting(socket,nickNames); handleNameChangeAttempts(socket,nickNames,namesUsed); handleRoomJoining(socket); //用户发出请求时,向其提供已经被占用的聊天室的列表 socket.on(‘rooms‘,function(){ socket.emit(‘rooms‘,io.sockets.manager.rooms); }); //定义用户断开连接后的清除逻辑 handleClientDisconnection(socket,nickNames,namesUsed); }); } //二.处理程序场景及事件 //1.分配用户昵称 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; } //2.进入聊天室 function joinRoom(socket,room){ console.log(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 + ‘.‘ }); var usersInRoom = io.sockets.clients(room); //如果不止一个用户在这个房间里,汇总一下都是谁 if(usersInRoom.length>1){ var usersInRoomSummary = ‘Users currently in ‘ + room + ‘:‘; for(var index in usersInRoom){ var userSocketId = usersInRoom[index].id; if(userSocketId !=socket.id){ if(index>0){ usersInRoomSummary +=‘, ‘; } usersInRoomSummary +=nickNames[userSocketId]; } } usersInRoomSummary +=‘.‘; //将房间里其他用户的汇总发送给这个用户 socket.emit(‘message‘,{text:usersInRoomSummary}); } } //3.处理昵称变更请求 function handleNameChangeAttempts(socket,nickNames,namesUsed){ socket.on(‘nameAttempt‘,function(name){ //昵称不能以Guest开头 if(name.indexOf(‘Guest‘) ==0){ socket.emit(‘nameResult‘,{ success: false, message: ‘Name cannot begin with "Guest".‘ }); }else{ //如果昵称还没注册就注册上 if(namesUsed.indexOf(name) == -1){ var previousName = nickNames[socket.id]; var previousNameIndex = namesUsed.indexOf(previousName); namesUsed.push(name); nickNames[socket.id] = name; //删掉之前用的昵称,让其他用户可以使用 delete namesUsed[previousNameIndex]; socket.emit(‘nameResult‘,{ success:true, name:name }); socket.broadcast.to(currentRoom[socket.id]).emit(‘message‘,{ text:previousName + ‘is now konwn as ‘ + name + ‘.‘ }); }else{ //如果用户名已经被占用,给客户端发送错误消息 socket.emit(‘nameResult‘,{ success:false, message:‘That name is aleady in use.‘ }); } } }); } //4.发送聊天消息 Socket.IO的broadcase函数是用来转发消息的. function handleMessageBroadcasting(socket){ socket.on(‘message‘,function(message){ socket.broadcast.to(message.room).emit(‘message‘,{ text:nickNames[socket.id] + ‘: ‘ + message.text }); }); } //5.创建房间 function handleRoomJoining(socket){ socket.on(‘join‘,function(room){ //console.log(room.newRoom); //console.log(currentRoom[socket.id]); socket.leave(currentRoom[socket.id]); joinRoom(socket,room.newRoom); }); } //6.用户断开连接 function handleClientDisconnection(socket){ socket.on(‘disconnect‘,function(){ var nameIndex = namesUsed.indexOf(nickNames[socket.id]); delete namesUsed[nameIndex]; delete nickNames[socket.id]; }); }
7.在程序的用户界面上使用客户端JavaScript
(1).向服务器发送用户的消息和昵称/房间变更请求;(在js目录中新建chat.js)
(2).显示其他用户的消息,以及可用房间的列表(在js目录中新建chat_ui.js)
chat.js
var Chat = function(socket){ this.socket = socket; } //发送聊天消息 Chat.prototype.sendMessage = function(room,text){ var message = { room: room, text: text }; this.socket.emit(‘message‘,message); }; // Chat.prototype.changeRoom = function(room){ this.socket.emit(‘join‘,{ newRoom: room }); }; //处理聊天命令 Chat.prototype.processCommand = function(command){ var words = command.split(‘ ‘); var command = words[0] .substring(1,words[0].length) .toLowerCase(); var message = false; console.log(command); switch(command){ case ‘join‘: words.shift();//前出 var room = words.join(‘ ‘); this.changeRoom(room); break; case ‘nick‘: words.shift(); var name = words.join(‘ ‘); this.socket.emit(‘nameAttempt‘,name); break; default: message = ‘Unrecongnized command‘; break; } return message; }
chat_ui.js
//显示可疑的文本数据 function divEscapedContentElement(message){ return $(‘<div></div>‘).text(message); } //显示系统创建的受信内容 function divSystemContentElement(message){ return $(‘<div></div>‘).html(‘<i>‘+ message + ‘</i>‘); } //处理原始的用户输入 function processUserInput(chatApp,socket){ var message = $(‘#send-message‘).val(); var systemMessage; //如果用户输入的内容以斜杠(/)开头,将其作为聊天命令 if(message.charAt(0)==‘/‘){ systemMessage = chatApp.processCommand(message); if(systemMessage){ $(‘#messages‘).append(divSystemContentElement(systemMessage)); } }else{ //将非命令输入广播给其他用户 chatApp.sendMessage($(‘#room‘).text(),message); $(‘#messages‘).append(divEscapedContentElement(message)); $(‘#messages‘).scrollTop($(‘#messages‘).prop(‘scrollHeight‘)); } $(‘#send-message‘).val(‘‘); } //用户的浏览器加载完页面后执行 对客户端Socket.IO事件处理进行初始化 var socket = io.connect(); $(document).ready(function(){ var chatApp = new Chat(socket); //显示更名尝试的结果 socket.on(‘nameResult‘,function(result){ var message; if(result.success){ message = ‘You are now konw as ‘ + result.name + ‘.‘; }else{ message = result.message; } $(‘#messages‘).append(divSystemContentElement); }); //显示房间变更结果 socket.on(‘joinResult‘,function(result){ $(‘#room‘).text(result.room); $(‘#messages‘).append(divSystemContentElement(‘Room changed.‘)); }); //显示接收到的消息 socket.on(‘message‘,function (message){ var newElement = $(‘<div></div>‘).text(message.text); $(‘#messages‘).append(newElement); }); //显示可用房间列表 socket.on(‘rooms‘,function (rooms){ $(‘#room-list‘).empty(); console.log(rooms); for(var room in rooms){ //console.log(rooms.size); room = room.substring(1,room.length); console.log(room); if(room !=‘‘){ $(‘#room-list‘).append(divEscapedContentElement(room)); } } $(‘#room-list div‘).click(function(){ chatApp.processCommand(‘/join‘ + $(this).text()); $(‘#send-message‘).focus(); }); }); setInterval(function(){ socket.emit(‘rooms‘); },1000); $(‘#send-message‘).focus(); $(‘#send-form‘).submit(function(){ processUserInput(chatApp,socket); return false; }); });
最后效果图:
标签:
原文地址:http://www.cnblogs.com/demongao/p/5760751.html