angular+nodejs+socket.io 聊天功能的实现
来源:互联网 发布:女装批发的软件 编辑:程序博客网 时间:2024/05/16 09:41
如题:本文部分是转载,部分是自己在实际联系中的收获
功能
着手开发之前,首先明确一下需要实现的功能:
新用户登入,广播通知其他用户
用户下线,广播通知其他用户
可显示在线人数及列表
可群聊,可私信
用户若发送群消息,广播通知其他所有用户
用户若发送私信,单独通知收方界面
因为自己是个审美渣,所以全靠bootstrap了,另外还模仿了下微信聊天记录里的气泡设计。
界面分左右两个板块,分别用于显示在线列表和聊天内容。
在左侧的在线列表中,点击不同项可以切换右侧板块的聊天对象。
右侧显示与当前聊天对象的对话记录,不过仅显示最近的30条。每一条聊天记录内容包括发送人的昵称及头像、发送时间、消息内容。关于头像,这里做简单处理,用填充了随机色的方块代替。另外,自己发出去的消息与收到的消息样式自然要做不同设计,
服务端:首先本地D盘创建文件夹,取名为:chat
首先是实现服务端:
打开chat文件夹:
在根目录下执行npm init
根据提示,生成一个package.json文件。打开并配置依赖项:
{
"name": "c",
"version": "1.0.0",
"description": {
"express": "^4.14.0",
"socket.io": "^1.5.1"
},
}
之后执行 npm install 安装依赖模块。
接下来,我们在根目录下新建app.js,在其中写Server端代码。再新建public文件夹,存放client端代码
app.js(/**/里面的是注释):
关于express的学习,推荐 http://www.tuicool.com/articles/YRRRNbv
var express = require('express');var app = require('express')();
var http = require('http').createServer(app);
/*
Express将app初始化成一个函数处理器,
你可以将其提供给一个HTTP服务器(看第三行)我们定义一个路由处理器,当我们访问网站主页时,这个处理器就会被调用。
我们让这个http服务器监听3012端口。
Socket.IO由两部分构成:
socket.io:一个服务器,集成了Node.JS的HTTP服务器
socket.io-client:一个客户端库,在浏览器端加载
在开发期间,socket.io会自动为我们提供客户端,所以现在我们只需安装一个模块:
npm install --save socket.io
注意,接下来我传递了一个http(HTTP Server)对象来创建一个新的socket.io实例,
然后,为即将来到的套接字监听connection事件,我会将其打印到控制台中。*/
var io=require('socket.io')(http);
/*
其中,app.use是express“调用中间件的方法”。所谓“中间件(middleware),就是处理HTTP请求的函数,用来完成各种特定的任务,比如检查用户是否登录、分析数据、以及其他在需要最终将数据发送给用户之前完成的任务。”。这是阮一峰文章的原话。
简而言之,app.use() 里面使用的参数,主要是函数。但这个使用,并不是函数调用,而是使能的意思。当用户在浏览器发出请求的时候,这部分函数才会启用,进行过滤、处理。
express的路由,众所周知,是app.get(),app.post(),app.all(),。。。,但其实,它们都是app.use的别名,呵呵。怪不得,我说为什么看上去,app.use 跟它们那么像:app.use的调用方式,除了类似 app.use(匿名函数或函数名),也可以是类似 app.use("/",匿名函数或函数名)。
通过 Express 内置的 express.static
可以方便地托管静态文件,例如图片、CSS、JavaScript 文件等。
将静态资源文件所在的目录作为参数传递给 express.static
中间件就可以提供静态资源文件的访问了。例如,假设在 public
目录放置了图片、CSS 和 JavaScript 文件,你就可以:
app.use(express.static('public'));
现在,public
目录下面的文件就可以访问了。
http://localhost:3000/images/kitten.jpghttp://localhost:3000/css/style.csshttp://localhost:3000/js/app.jshttp://localhost:3000/images/bg.pnghttp://localhost:3000/hello.html
*/
app.use(express.static((__dirname + '/public')));app.get('/',function(req,res){
res.sendfile('index.html');
});
var connectedSockets={};
//初始值即包含了群聊,用“”表示nickname
var allUsers=[{nickname:"",color:"#000"}];
/*用来监听连接*/
io.on('connection',function(socket){socket.on('addUser',function(data){
//该事件由客户端输入昵称后触发,服务端收到后判断,昵称已被占用
if(connectedSockets[data.nickname]){
socket.emit('userAddingResult',{result:false});
}else{
//反之通知客户端昵称有效以及当前所有已经连接的用户信息,并广播
socket.emit('userAddingResult',{result:true});
socket.nickname=data.nickname;
/* 保存每个socket实例,发私信需要用
发给特定用户,如果消息是发给特定用户A,那么就需要获取A对应的socket实例,
然后调用起emit方法,所以没当一个客户端连接到server时,
我们得把socket实例保存起来,以备后续之需 */
connectedSockets[socket.nickname]=socket;
allUsers.push(data);
//广播欢迎新用户,除新用户外都可以看到
socket.broadcast.emit('userAdded',data);
//将所有在线用户发给新用户
socket.emit('allUser',allUsers);
}
});
socket.on('addMessage',function(data){
if(data.to){
console.log(data);
//需要发私信时,取出socket实例做操作即可:
connectedSockets[data.to].emit('messageAdded',data);
}else{
//群发,广播消息,除原发送外其他的都可以看到
socket.broadcast.emit('messageAdded',data);
}
});
socket.on('disconnect',function(){
//有用户退出聊天室
//广播有用户退出
socket.broadcast.emit('userRemoved',{
nickname: socket.nickname
});
for(var i=0;i<allUsers.length;i++){
if(allUsers[i].nickname==socket.nickname){
allUsers.splice(i,1);
}
}
//删除对应的socket实例
delete connectedSockets[socket.nickname];
});
});
http.listen(3012,function(){
console.log('hello listening on * 3012');
});
前端app.js
app.factory('socket',function($rootScope){
var socket=io();
return {
on: function(eventName, callback) {
socket.on(eventName, function() {
var args = arguments;
$rootScope.$apply(function() { //手动执行脏检查
callback.apply(socket, args);
});
});
},
emit:function(eventName,data,callback){
socket.emit(eventName,data,function(){
var args=arguments;
$rootScope.$apply(function(){
if(callback)
{
callback.apply(socket,args);
}
});
});
}
};
});
app.factory('randomColor', function($rootScope) {
return {
newColor: function() {
return '#'+('00000'+(Math.random()*0x1000000<<0).toString(16)).slice(-6);
}
};
});
app.factory('userService', function($rootScope) {
return {
get: function(users,nickname) {
if(users instanceof Array){
for(var i=0;i<users.length;i++){
if(users[i].nickname===nickname){
return users[i];
}
}
}else{
return null;
}
}
};
});
app.controller("chatCtrl",['$scope','socket','randomColor','userService',function($scope,socket,randomColor,userService){
var messageWrapper= $('.message-wrapper');
$scope.hasLogined=false;
$scope.receiver="";//默认是群聊
$scope.publicMessages=[];//群聊消息
$scope.privateMessages={};//私信消息
$scope.messages=$scope.publicMessages;//默认显示群聊
$scope.users=[];//
$scope.color=randomColor.newColor();//当前用户头像颜色
$scope.login=function(){ //登录进入聊天室
socket.emit("addUser",{nickname:$scope.nickname,color:$scope.color});
}
$scope.scrollToBottom=function(){
messageWrapper.scrollTop(messageWrapper[0].scrollHeight);
}
$scope.postMessage=function(){
var msg={text:$scope.words,type:"normal",color:$scope.color,from:$scope.nickname,to:$scope.receiver};
var rec=$scope.receiver;
if(rec){
//私信
if(!$scope.privateMessages[rec]){
$scope.privateMessages[rec]=[];
}
$scope.privateMessages[rec].push(msg);
}else{
//群聊
$scope.publicMessages.push(msg);
}
$scope.words="";
if(rec!=$scope.nickname){
//排除给自己发的情况
socket.emit("addMessage",msg);
}
}
$scope.setReceiver=function(receiver){
$scope.receiver=receiver;
if(receiver){
//私信用户
if(!$scope.privateMessages[receiver]){
$scope.privateMessages[receiver]=[];
}
$scope.messages=$scope.privateMessages[receiver];
}else{
//广播
$scope.messages=$scope.publicMessages;
}
var user=userService.get($scope.users,receiver);
if(user){
user.hasNewMessage=false;
}
}
//收到登录结果
socket.on('userAddingResult',function(data){
if(data.result){
$scope.userExisted=false;
$scope.hasLogined=true;
}
else{
//昵称被占用
$scope.userExisted=true;
}
});
//接收到欢迎新用户的消息
socket.on('userAdded',function(data){
if(!$scope.hasLogined)return;
$scope.publicMessages.push({text:data.nickname,type:"welcome"});
$scope.users.push(data);
});
//接收到在线用户的信息
socket.on('allUser',function(data){
if(!$scope.hasLogined) return;
$scope.users=data;
});
//接收到用户退出消息
socket.on('userRemoved',function(data){
if(!$scope.hasLogined) return;
alert(data.nickname);
$scope.publicMessages.push({text:data.nickname,type:"bye"});
for(var i=0;i<$scope.users.length;i++){
if($scope.users[i].nickname==data.nickname){
$scope.users.splice(i,1);
return;
}
}
});
//接收到新消息
socket.on('messageAdded',function(data){
if(!$scope.hasLogined) return;
if(data.to){
//私信
if(!$scope.privateMessages[data.from]){
$scope.privateMessages[data.from]=[];
}
$scope.privateMessages[data.from].push(data);
}else{
//群发
$scope.publicMessages.push(data);
}
var fromUser=userService.get($scope.users,data.from);
var toUser=userService.get($scope.users,data.to);
if($scope.receiver!==data.from){
//与来信方不是正在聊天当中才提示新消息
if(fromUser&&toUser.nickname){
fromUser.hasNewMessage=true;//私信
}else{
toUser.hasNewMessage = true;//群发
}
}
});
}]);
app.directive('message',['$timeout',function($timeout){
return {
//E 表示该指令是一个element; A 表示该指令是attribute; C 表示该指令是class; M 表示该指令是注视
restrict:'E',
templateUrl:'message.html',
scope:{
info:"=",
self:"=",
scrolltothis:"&"
},
link:function(scope,elem,attrs){
scope.time=new Date();
$timeout(scope.scrolltothis);
$timeout(function(){
elem.find('.avatar').css('background',scope.info.color);
});
}
};
}])
.directive('user',['$timeout',function($timeout){
return {
restrict:'E',
templateUrl: 'user.html',
scope:{
info:"=",
iscurrentreceiver:"=",
setreceiver:"&"
},
link:function(scope, elem, attrs,chatCtrl){
$timeout(function(){
elem.find('.avatar').css('background',scope.info.color);
});
}
};
}]);
前端index.html
<html>
<head lang="en">
<meta charset="utf-8">
<title>在线聊天室</title>
<link href="http://cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="./assets/style/app.css"/>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
<script src="//cdn.bootcss.com/angular.js/1.4.3/angular.min.js"></script>
<script src="./assets/js/app.js"></script>
</head>
<body ng-app="chatRoom" ng-controller="chatCtrl">
<!-- chat room -->
<div class="chat-room-wrapper" ng-show="hasLogined">
<div class="online panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">在线<span class="user-number">
{{users.length-1}}
</span></h3>
</div>
<div class="user-wrapper panel-body">
<user iscurrentreceiver="receiver===user.nickname" info="user"
ng-click="setReceiver(user.nickname)" ng-repeat="user in users">
</user>
</div>
</div>
<div class="chat-room panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">
{{receiver?receiver:"群聊"}}
</h3>
</div>
<div class="message-wrapper panel-body">
<message self="nickname" scrolltothis="scrollToBottom()"
info="message" ng-repeat="message in messages">
</message>
</div>
<div class="panel-footer">
<form class="post-form form-inline" novalidate name="postform" ng-submit="postMessage()">
<input type="text" class="form-control" ng-model="words" placeholder="说点什么呗" required>
<button type="submit" class="btn btn-success" ng-disabled="postform.$invalid">发送</button>
</form>
</div>
</div>
</div>
<!-- end of chat room -->
<!-- login form -->
<div class="userform-wrapper" ng-show="!hasLogined">
<form class="form-inline login" novalidate name="userform" ng-submit="login()">
<div class="form-group">
<label for="nickname" class="sr-only"></label>
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-user"></span></div>
<input type="text" class="form-control" id="nickname" placeholder="Your Nickname" ng-model="nickname" required/>
</div>
</div>
<button type="submit" class="btn btn-primary" ng-disabled="userform.$invalid">LOG IN</button>
<p ng-show="userExisted" class="help-block">A user with this nickname already exists.</p>
</form>
</div>
<!-- end of login form -->
</body>
</html>
前端message.html
<div ng-switch on="info.type">
<!-- 欢迎消息 -->
<div class="system-notification" ng-switch-when="welcome">
<strong>系统: </strong>
<span>{{info.text}}</span>来啦我们欢迎~
</div>
<!-- 退出消息 -->
<div class="system-notification" ng-switch-when="bye"><strong>系统: </strong>byebye,
<span>{{info.text}}</span></div>
<!-- 普通消息 -->
<div class="normal-message" ng-class="{others:self!==info.from,self:self===info.from}" ng-switch-when="normal">
<div class="name-wrapper">
<span>{{info.from}} @ </span>
<span>{{time | date: 'HH:mm:ss' }}</span>
</div>
<div class="content-wrapper">
<span class="content">{{info.text}}</span>
<span class="avatar"></span>
</div>
</div>
</div>
前端user.html
<meta charset="utf-8">
<span class="avatar"></span><span class="nickname">{{info.nickname?info.nickname:"群聊"}}</span>
<span class="unread" ng-show="info.hasNewMessage&&!iscurrentreceiver">[未读]</span>
</div>
通过 Express 内置的 express.static
可以方便地托管静态文件,例如图片、CSS、JavaScript 文件等。
将静态资源文件所在的目录作为参数传递给 express.static
中间件就可以提供静态资源文件的访问了。例如,假设在 public
目录放置了图片、CSS 和 JavaScript 文件,你就可以:
app.use(express.static('public'));
现在,public
目录下面的文件就可以访问了。
http://localhost:3000/images/kitten.jpghttp://localhost:3000/css/style.csshttp://localhost:3000/js/app.jshttp://localhost:3000/images/bg.pnghttp://localhost:3000/hello.html
- angular+nodejs+socket.io 聊天功能的实现
- 基于Socket.IO实现Android聊天功能
- nodejs死亡笔记之socket.io那些事(附可用来撩妹的聊天功能)
- nodejs+socket.io实现数据推送功能
- NodeJS+Socket.io+ExpressJS实现web聊天应用
- 基于Vue、Nodejs、Socket.io的聊天应用
- nodejs+socket.io即时聊天实例
- nodejs+socket.io即时聊天实例
- nodejs+socket.io 即时聊天笔记
- Socket实现聊天功能
- Socket 实现聊天功能
- 用nodejs实现聊天功能
- socket.io 实现网页聊天
- socket之聊天功能实现
- 使用socket.io+express实现网页聊天的实践
- 基于Node.js + socket.io实现WebSocket的聊天DEMO
- 使用Express + Socket.io + MongoDB实现简单的聊天
- nodeJs+jquery实现聊天插入表情功能
- JDBC连接数据库的代码及步骤
- iOS apache开启命令
- 神经网络的直观解释
- [机器学习] Coursera笔记 - 机器学习应用的建议-Part2
- 定时向数组前面添加一个随机数
- angular+nodejs+socket.io 聊天功能的实现
- Java 泛型
- 线性回归--最小二乘法笔记
- 二叉树的遍历(283)
- Swing中实现增删查改项目后获得的经验
- Python 爬虫 —— BeautifulSoup
- 飞哥教你微信公众号开发-2-自定义菜单
- Codeforces 303A Lucky Permutation Triple【思维】
- 11月5日 数组