node.js和socket.io实现im
im——Instant Messaging 即时通讯
基本技术原理
(1)通过IM服务器登陆或注销
(2)用户A通过列表找到B,用户B获得消息并与之交谈
(3)通过IM服务器指引建立与B单独的通讯通道
通讯方式
(1)在线直接通讯
直接通过服务器发送过来的用户B的IP地址、TCP端口号等信息,直接向用户B的PC机发出聊天信息,即时文字消息就不再IM服务器中转,直接通过网络进行点对点的通讯
(2)在线代理通讯
点对点通讯由于防火墙、网络速度等原因难以建立或者速度很慢,IM服务器将会主动提供消息中转服务
(3)离线代理通讯
不能同时在线的时候,如此时A向B发送消息,IM服务器可以主动寄存A用户的消息,到B用户下一次登陆的时候,自动将消息转发 给B
(4)扩展方式通讯
用户A可以通过IM服务器将信息以扩展的方式传递给B,如短信、email等
用nodejs实现im使用到express和socket.io两个包模块
expres
是node.js中管理路由响应请求的模块,根据请求的URL返回相应的HTML页面
安装
npm install express
不用express需要将HTML代码与后台JavaScript代码写在一起进行请求的响应
var http = require('http');
server = http.createServer(function(req,res){
res.writeHead(200,{
'Content-Type':'text/html'
});
res.write('<h1>hello world!</h1><div style="color:blue">baby</div>');
res.end();
});
server.listen(8080);
console.log('server started');
使用express,在当前目录下建www用来存放我们的网页文件,包括图片以及前端的js文件等
server.js修改为
var express = require('express'),
app = express(),
server = require('http').createServer(app);
app.use('/',express.static(__dirname+'/www/'));
server.listen(8080);
console.log('server started');
www下见文件index.html
<!doctype html>
<html>
<head>
<title>mychat</title>
</head>
<body>
<div class="wrapper">
<div class="banner">
<h1>My Chat</h1>
<span id="status"></span>
</div>
<div id="historyMsg">
</div>
<div class="controls" >
<textarea id="messageInput" placeHolder="enter to send"></textarea>
<input id="sendBtn" type="button" value="SEND">
</div>
</div>
</body>
</html>
重新运行server.js
socket.io
Node.js中使用socket的一个包。使用它可以很方便地建立服务器到客户端的sockets连接,发送事件与接收特定事件
安装
npm install socket.io
安装后在node_modules文件夹下新生成了一个socket.io文件夹,其中可以找到一个socket.io.js文件
将它引入到HTML页面,这样我们就可以在前端使用socket.io与服务器进行通信了
通过socket.emit()来激发一个事件
通过socket.on()来侦听和处理对应事件
这两个事件通过传递的参数进行通信
查看安装的模块
eg:
index.html
<!doctype html>
<html>
<head>
<title>mychat</title>
</head>
<body>
<div class="wrapper">
<div class="banner">
<h1>My Chat</h1>
<span id="status"></span>
</div>
<div id="historyMsg">
</div>
<div class="controls" >
<textarea id="messageInput" placeHolder="enter to send"></textarea>
<input id="sendBtn" type="button" value="SEND">
</div>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="js/jquery.js"></script>
<script type="text/javascript"> var socket=io.connect(),//与服务器进行连接
button=document.getElementById('sendBtn');
button.onclick=function(){
var msg= $("#messageInput").val();
socket.emit('foo', msg);//发送一个名为foo的事件,并且传递一个字符串数据‘hello’
}
</script>
</body>
</html>
首先建立与服务器的连接,然后得到一个socket实例
之后如果页面上面一个ID为sendBtn的按钮被点击的话,通过这个socket实例发起一个名为foo的事件,同时传递一个hello字符串信息到服务器
在服务器端写相应的代码来处理这个foo事件并接收传递来的数据
var express = require('express'),
app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server);
app.use('/',express.static(__dirname+'/www/'));
server.listen(8080);
io.on('connection',function(socket){
socket.on('foo',function(data){
console.log(data);
})
});
console.log('server started');
1.设置昵称
在后台server.js中,创建一个名叫users的全局数组变量,当一个用户设置好昵称发送到服务器的时候,将昵称压入users数组。如果用户断线离开了,也要相应地从users数组中移除
index.html
<!doctype html>
<html>
<head>
<title>mychat</title>
</head>
<body>
<div class="wrapper">
<div class="banner">
<h1>My Chat</h1>
<span id="status"></span>
</div>
<div id="historyMsg">
</div>
<div class="controls" >
<textarea id="messageInput" placeHolder="enter to send"></textarea>
<input id="sendBtn" type="button" value="SEND">
</div>
</div>
<div id="loginWrapper">
<p id="info">connecting to server...</p>
<div id="nickWrapper" style="display:none;">
<input type="text" placeHolder="nickname" id="nicknameInput" />
<input type="button" value="OK" id="loginBtn" />
</div>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="js/jquery.js"></script>
<script src="js/chat.js"></script>
</body>
</html>
chat.js
var chat;
$(function(){
chat = new Chat();
chat.init();
$("#loginWrapper").show();
$(".wrapper").hide();
$("#loginBtn").click(function(){
var nickName = $('#nicknameInput').val();
if (nickName.trim().length != 0) {
chat.socket.emit('login', nickName);
} else {
$('#nicknameInput').focus();
};
});
});
//定义chat类
var Chat = function() {
this.socket = null;
};
//向原型添加业务方法
Chat.prototype = {
init: function() {//初始化
var that = this;
//建立到服务器的socket连接
this.socket = io.connect();
//监听socket的connect事件,此事件表示连接已经建立
this.socket.on('connect', function() {
//连接到服务器后,显示昵称输入框
$('#info').html('get yourself a nickname');
$('#nickWrapper').show();
$('#nicknameInput').focus();
});
this.socket.on('nickExisted', function() {
$('#info').html('nickname is taken, choose another please'); //显示昵称被占用的提示
});
this.socket.on('loginSuccess', function() {
$(document).attr("title",'Chat | ' + $('#nicknameInput').val());
$('#loginWrapper').hide();//隐藏遮罩层显聊天界面
$(".wrapper").show();
$('#messageInput').focus();//让消息输入框获得焦点
});
}
};
server.js
var express = require('express'),
app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server),
users =[];
app.use('/',express.static(__dirname+'/www/'));
server.listen(8080);
io.on('connection',function(socket){
socket.on('login',function(nickname){
if(users.indexOf(nickname)>-1){
socket.emit('nickExisted');
}else{
socket.userIndex = users.length;
socket.nickname = nickname;
users.push(nickname);
socket.emit('loginSuccess');
io.sockets.emit('system', nickname, users.length, 'login');
}
});
});
console.log('server started');
2.在线统计
通过io.sockets.emit 向所有用户发送了一个system事件,传递了刚登入或离开用户的昵称
chat.js
var chat;
$(function(){
chat = new Chat();
chat.init();
$("#loginWrapper").show();
$(".wrapper").hide();
$("#loginBtn").click(function(){
var nickName = $('#nicknameInput').val();
if (nickName.trim().length != 0) {
chat.socket.emit('login', nickName);
} else {
$('#nicknameInput').focus();
};
}); }); //定义chat类
var Chat = function() {
this.socket = null;
}; //向原型添加业务方法
Chat.prototype = {
init: function() {//初始化
var that = this;
//建立到服务器的socket连接
this.socket = io.connect();
//监听socket的connect事件,此事件表示连接已经建立
this.socket.on('connect', function() {
//连接到服务器后,显示昵称输入框
$('#info').html('get yourself a nickname');
$('#nickWrapper').show();
$('#nicknameInput').focus();
});
this.socket.on('nickExisted', function() {
$('#info').html('nickname is taken, choose another please'); //显示昵称被占用的提示
});
this.socket.on('loginSuccess', function() {
$(document).attr("title",'Chat | ' + $('#nicknameInput').val());
$('#loginWrapper').hide();//隐藏遮罩层显聊天界面
$(".wrapper").show();
$('#messageInput').focus();//让消息输入框获得焦点
});
this.socket.on('system', function(nickName, userCount, type) {
//判断用户是连接还是离开以显示不同的信息
var msg = nickName + (type == 'login' ? ' join in' : 'left');
$('#historyMsg').append("<p>"+msg+"</p>");
chat._displayNewMsg('system ', msg, 'red');
//将在线人数显示到页面顶部
$('#status').html(userCount + (userCount > 1 ? ' users' : ' user') + ' online');
}); }
};
server.js
var express = require('express'),
app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server),
users =[];
app.use('/',express.static(__dirname+'/www/'));
server.listen(8080);
io.on('connection',function(socket){
socket.on('login',function(nickname){
if(users.indexOf(nickname)>-1){
socket.emit('nickExisted');
}else{
socket.userIndex = users.length;
socket.nickname = nickname;
users.push(nickname);
socket.emit('loginSuccess');
io.sockets.emit('system', nickname, users.length, 'login');
}
});
socket.on('disconnect', function() {
users.splice(socket.userIndex, 1);
socket.broadcast.emit('system', socket.nickname, users.length, 'logout');
});
});
console.log('server started');
用户登录和离开显示提示
3.发送消息
chat.js
var chat;
$(function(){
chat = new Chat();
chat.init();
$("#loginWrapper").show();
$(".wrapper").hide();
$("#loginBtn").click(function(){
var nickName = $('#nicknameInput').val();
if (nickName.trim().length != 0) {
chat.socket.emit('login', nickName);
} else {
$('#nicknameInput').focus();
};
});
$('#sendBtn').click(function() {
var messageInput = $('#messageInput'),
msg = messageInput.val();
messageInput.val('');
messageInput.focus();
if (msg.trim().length != 0) {
chat.socket.emit('postMsg', msg); //把消息发送到服务器
chat._displayNewMsg('me', msg); //把自己的消息显示到自己的窗口中
};
});
}); //定义chat类
var Chat = function() {
this.socket = null;
}; //向原型添加业务方法
Chat.prototype = {
init: function() {//初始化
var that = this;
//建立到服务器的socket连接
this.socket = io.connect();
//监听socket的connect事件,此事件表示连接已经建立
this.socket.on('connect', function() {
//连接到服务器后,显示昵称输入框
$('#info').html('get yourself a nickname');
$('#nickWrapper').show();
$('#nicknameInput').focus();
});
this.socket.on('nickExisted', function() {
$('#info').html('nickname is taken, choose another please'); //显示昵称被占用的提示
});
this.socket.on('loginSuccess', function() {
$(document).attr("title",'Chat | ' + $('#nicknameInput').val());
$('#loginWrapper').hide();//隐藏遮罩层显聊天界面
$(".wrapper").show();
$('#messageInput').focus();//让消息输入框获得焦点
});
this.socket.on('system', function(nickName, userCount, type) {
//判断用户是连接还是离开以显示不同的信息
var msg = nickName + (type == 'login' ? ' join in' : 'left');
$('#historyMsg').append("<p>"+msg+"</p>");
chat._displayNewMsg('system ', msg, 'red');
//将在线人数显示到页面顶部
$('#status').html(userCount + (userCount > 1 ? ' users' : ' user') + ' online');
});
this.socket.on('newMsg', function(user, msg) {
chat._displayNewMsg(user, msg);
});
},
_displayNewMsg: function(user, msg, color) {//显示消息
var container = $('#historyMsg'),
msgToDisplay = '',
date = new Date().toTimeString().substr(0, 8),
default_color = color || '#000';
msgToDisplay = '<p>'+user + '<span class="timespan">(' + date + '): </span>' + msg+'</p>';
container.append(msgToDisplay);
container.scrollTop = container.scrollHeight;
}
};
server.js
var express = require('express'),
app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server),
users =[];
app.use('/',express.static(__dirname+'/www/'));
server.listen(8080);
io.on('connection',function(socket){
socket.on('login',function(nickname){
if(users.indexOf(nickname)>-1){
socket.emit('nickExisted');
}else{
socket.userIndex = users.length;
socket.nickname = nickname;
users.push(nickname);
socket.emit('loginSuccess');
io.sockets.emit('system', nickname, users.length, 'login');
}
});
socket.on('disconnect', function() {
users.splice(socket.userIndex, 1);
socket.broadcast.emit('system', socket.nickname, users.length, 'logout');
});
socket.on('postMsg', function(msg) {
//将消息发送到除自己外的所有用户
socket.broadcast.emit('newMsg', socket.nickname, msg);
});
});
console.log('server started');
4.发送图片
图片不同于文字,但通过将图片转化为字符串形式后,便可以像发送普通文本消息一样发送图片了,只是在显示的时候将它还原为图片
文件类型的input,用户点击图片按钮后,弹出文件选择窗口选择图片。然后在JavaScript代码中使用FileReader来将图片读取为base64格式的字符串形式进行发送
而base64格式的图片直接可以指定为图片的src,这样就可以将图片用img标签显示在页面了
index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="author" content="Wayou">
<meta name="description" content="hichat | a simple chat application built with node.js and websocket">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>hichat</title>
<link rel="stylesheet" href="styles/main.css">
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
<link rel="icon" href="favicon.ico" type="image/x-icon">
</head>
<body>
<div class="wrapper">
<div class="banner">
<h1>HiChat :)</h1>
<span id="status"></span>
</div>
<div id="historyMsg">
</div>
<div class="controls" >
<div class="items">
<input id="colorStyle" type="color" placeHolder='#000' title="font color" />
<input id="emoji" type="button" value="emoji" title="emoji" />
<label for="sendImage" class="imageLable">
<input type="button" value="image" />
<input id="sendImage" type="file" value="image"/>
</label>
<input id="clearBtn" type="button" value="clear" title="clear screen" />
</div>
<textarea id="messageInput" placeHolder="enter to send"></textarea>
<input id="sendBtn" type="button" value="SEND">
<div id="emojiWrapper">
</div>
</div>
</div>
<div id="loginWrapper">
<p id="info">connecting to server...</p>
<div id="nickWrapper">
<input type="text" placeHolder="nickname" id="nicknameInput" />
<input type="button" value="OK" id="loginBtn" />
</div>
</div>
<footer>
<small>view on <a href="https://github.com/Wayou/HiChat">GitHub</a> | <a href="mailto:liuwayong@gmail.com">contact me</a></small>
</footer>
<script src="/socket.io/socket.io.js"></script>
<script src="scripts/hichat.js"></script>
<script>
/**REMOVE ME IF YOU CANT ACCESS GOOGLE SERVICE**/
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-46794744-7', 'hichat.herokuapp.com');
ga('send', 'pageview');
/**REMOVE END**/
</script>
</body>
</html>
chat.js
var chat;
$(function(){
chat = new Chat();
chat.init();
$("#loginWrapper").show();
$(".wrapper").hide();
$("#loginBtn").click(function(){
var nickName = $('#nicknameInput').val();
if (nickName.trim().length != 0) {
chat.socket.emit('login', nickName);
} else {
$('#nicknameInput').focus();
};
});
$('#sendBtn').click(function() {
var messageInput = $('#messageInput'),
msg = messageInput.val();
messageInput.val('');
messageInput.focus();
if (msg.trim().length != 0) {
chat.socket.emit('postMsg', msg); //把消息发送到服务器
chat._displayNewMsg('me', msg); //把自己的消息显示到自己的窗口中
};
});
$('#sendImage').change(function() {
//检查是否有文件被选中
if (this.files.length != 0) {
//获取文件并用FileReader进行读取
var file = this.files[0],
reader = new FileReader();
if (!reader) {
chat._displayNewMsg('system', '!your browser doesn\'t support fileReader', 'red');
this.value = '';
return;
};
reader.onload = function(e) {
//读取成功,显示到页面并发送到服务器
this.value = '';
chat.socket.emit('img', e.target.result);
chat._displayImage('me', e.target.result);
};
reader.readAsDataURL(file);
}
});
}); //定义chat类
var Chat = function() {
this.socket = null;
}; //向原型添加业务方法
Chat.prototype = {
init: function() {//初始化
var that = this;
//建立到服务器的socket连接
this.socket = io.connect();
//监听socket的connect事件,此事件表示连接已经建立
this.socket.on('connect', function() {
//连接到服务器后,显示昵称输入框
$('#info').html('get yourself a nickname');
$('#nickWrapper').show();
$('#nicknameInput').focus();
});
this.socket.on('nickExisted', function() {
$('#info').html('nickname is taken, choose another please'); //显示昵称被占用的提示
});
this.socket.on('loginSuccess', function() {
$(document).attr("title",'Chat | ' + $('#nicknameInput').val());
$('#loginWrapper').hide();//隐藏遮罩层显聊天界面
$(".wrapper").show();
$('#messageInput').focus();//让消息输入框获得焦点
});
this.socket.on('system', function(nickName, userCount, type) {
//判断用户是连接还是离开以显示不同的信息
var msg = nickName + (type == 'login' ? ' join in' : 'left');
$('#historyMsg').append("<p>"+msg+"</p>");
chat._displayNewMsg('system ', msg, 'red');
//将在线人数显示到页面顶部
$('#status').html(userCount + (userCount > 1 ? ' users' : ' user') + ' online');
});
this.socket.on('newMsg', function(user, msg) {
chat._displayNewMsg(user, msg);
});
this.socket.on('newImg', function(user, img) {
chat._displayImage(user, img);
});
},
_displayNewMsg: function(user, msg, color) {//显示消息
var container = $('#historyMsg'),
msgToDisplay = '',
date = new Date().toTimeString().substr(0, 8),
default_color = color || '#000';
msgToDisplay = '<p style="color:'+color+'">'+user + '<span class="timespan">(' + date + '): </span>' + msg+'</p>';
container.append(msgToDisplay);
container.scrollTop = container.scrollHeight;
},
_displayImage: function(user, imgData, color) {
var container = $('#historyMsg'),
msgToDisplay = '',
date = new Date().toTimeString().substr(0, 8),
mycolor = color || '#000';
msgToDisplay = '<p style="color:'+color+'">'+ user + '<span class="timespan">(' + date + '): </span> <br/>' + '<a href="' + imgData + '" target="_blank"><img src="' + imgData + '"/></a></p>';
container.append(msgToDisplay);
container.scrollTop = container.scrollHeight;
}
};
server.js
var express = require('express'),
app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server),
users =[];
app.use('/',express.static(__dirname+'/www/'));
server.listen(8080);
io.on('connection',function(socket){
socket.on('login',function(nickname){
if(users.indexOf(nickname)>-1){
socket.emit('nickExisted');
}else{
socket.userIndex = users.length;
socket.nickname = nickname;
users.push(nickname);
socket.emit('loginSuccess');
io.sockets.emit('system', nickname, users.length, 'login');
}
});
socket.on('disconnect', function() {
users.splice(socket.userIndex, 1);
socket.broadcast.emit('system', socket.nickname, users.length, 'logout');
});
socket.on('postMsg', function(msg) {
//将消息发送到除自己外的所有用户
socket.broadcast.emit('newMsg', socket.nickname, msg);
});
//接收用户发来的图片
socket.on('img', function(imgData) {
//通过一个newImg事件分发到除自己外的每个用户
socket.broadcast.emit('newImg', socket.nickname, imgData);
});
});
console.log('server started');
5.发表情
聊天程序是把表情转为符号,然后数据传输过程中其实转输的是一个冒号加右括号的组合,当每个客户端接收到消息后,从文字当中将这些表情符号提取出来,再用gif图片替换,这样呈现到页面我们就 看到了表情加文字的混排了
表情的格式[emoji:xx]
xx表示某个gif图片的编号
index.html
<!doctype html>
<html>
<head>
<title>mychat</title>
<style>
/*custom the file input*/
.imageLable {
position: relative;
}
#sendImage {
position: absolute;
width: 52px;
left: 0;
opacity: 0;
overflow: hidden;
}
#historyMsg img {
max-width: 99%;
}
#emojiWrapper {
display: none;
width: 500px;
bottom: 105px;
position: absolute;
background-color: #aaa;
box-shadow: 0 0 10px #555;
}
#emojiWrapper img {
margin: 2px;
padding: 2px;
width: 25px;
height: 25px;
}
#emojiWrapper img:hover {
background-color: blue;
}
.emoji{
display: inline;
}
/*end custom file input*/
</style>
</head>
<body>
<div class="wrapper">
<div class="banner">
<h1>My Chat</h1>
<span id="status"></span>
</div>
<div id="historyMsg">
</div>
<div class="controls" >
<div class="items">
<input id="colorStyle" type="color" placeHolder='#000' title="font color" />
<input id="emoji" type="button" value="emoji" title="emoji" />
<label for="sendImage" class="imageLable">
<input type="button" value="image" />
<input id="sendImage" type="file" value="image"/>
</label>
<input id="clearBtn" type="button" value="clear" title="clear screen" />
</div>
<textarea id="messageInput" placeHolder="enter to send"></textarea>
<input id="sendBtn" type="button" value="SEND">
<div id="emojiWrapper">
</div>
</div>
</div>
<div id="loginWrapper">
<p id="info">connecting to server...</p>
<div id="nickWrapper" style="display:none;">
<input type="text" placeHolder="nickname" id="nicknameInput" />
<input type="button" value="OK" id="loginBtn" />
</div>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="js/jquery.js"></script>
<script src="js/chat.js"></script>
</body>
</html>
chat.js
var chat;
$(function(){
chat = new Chat();
chat.init();
$("#loginWrapper").show();
$(".wrapper").hide();
$("#loginBtn").click(function(){
var nickName = $('#nicknameInput').val();
if (nickName.trim().length != 0) {
chat.socket.emit('login', nickName);
} else {
$('#nicknameInput').focus();
};
});
$('#sendBtn').click(function() {
var messageInput = $('#messageInput'),
msg = messageInput.val();
messageInput.val('');
messageInput.focus();
if (msg.trim().length != 0) {
chat.socket.emit('postMsg', msg); //把消息发送到服务器
chat._displayNewMsg('me', msg); //把自己的消息显示到自己的窗口中
};
});
$('#sendImage').change(function() {
//检查是否有文件被选中
if (this.files.length != 0) {
//获取文件并用FileReader进行读取
var file = this.files[0],
reader = new FileReader();
if (!reader) {
chat._displayNewMsg('system', '!your browser doesn\'t support fileReader', 'red');
this.value = '';
return;
};
reader.onload = function(e) {
//读取成功,显示到页面并发送到服务器
this.value = '';
chat.socket.emit('img', e.target.result);
chat._displayImage('me', e.target.result);
};
reader.readAsDataURL(file);
}
});
$('#emoji').click(function(e) {
var emojiwrapper = $('#emojiWrapper');
emojiwrapper.show();
e.stopPropagation();
});
$("body").click(function(e) {
var emojiwrapper = $('#emojiWrapper');
if (e.target != emojiwrapper) {
emojiwrapper.hide();
};
});
$('#emojiWrapper').click(function(e) {
//获取被点击的表情
var target = e.target;
if (target.nodeName.toLowerCase() == 'img') {
var messageInput = $('#messageInput');
messageInput.focus();
messageInput.val(messageInput.val() + '[emoji:' + target.title + ']');
};
});
}); //定义chat类
var Chat = function() {
this.socket = null;
}; //向原型添加业务方法
Chat.prototype = {
init: function() {//初始化
var that = this;
//建立到服务器的socket连接
this.socket = io.connect();
//监听socket的connect事件,此事件表示连接已经建立
this.socket.on('connect', function() {
//连接到服务器后,显示昵称输入框
$('#info').html('get yourself a nickname');
$('#nickWrapper').show();
$('#nicknameInput').focus();
});
this.socket.on('nickExisted', function() {
$('#info').html('nickname is taken, choose another please'); //显示昵称被占用的提示
});
this.socket.on('loginSuccess', function() {
$(document).attr("title",'Chat | ' + $('#nicknameInput').val());
$('#loginWrapper').hide();//隐藏遮罩层显聊天界面
$(".wrapper").show();
$('#messageInput').focus();//让消息输入框获得焦点
});
this.socket.on('system', function(nickName, userCount, type) {
//判断用户是连接还是离开以显示不同的信息
var msg = nickName + (type == 'login' ? ' join in' : 'left');
$('#historyMsg').append("<p>"+msg+"</p>");
chat._displayNewMsg('system ', msg, 'red');
//将在线人数显示到页面顶部
$('#status').html(userCount + (userCount > 1 ? ' users' : ' user') + ' online');
});
this.socket.on('newMsg', function(user, msg) {
chat._displayNewMsg(user, msg);
});
this.socket.on('newImg', function(user, img) {
chat._displayImage(user, img);
});
this._initialEmoji();
},
_displayNewMsg: function(user, msg, color) {//显示消息
var container = $('#historyMsg'),
msgToDisplay = '',
date = new Date().toTimeString().substr(0, 8),
default_color = color || '#000';
msg = this._showEmoji(msg);
msgToDisplay = '<p style="color:'+color+'">'+user + '<span class="timespan">(' + date + '): </span>' + msg+'</p>';
container.append(msgToDisplay);
container.scrollTop = container.scrollHeight;
},
_displayImage: function(user, imgData, color) {
var container = $('#historyMsg'),
msgToDisplay = '',
date = new Date().toTimeString().substr(0, 8),
mycolor = color || '#000';
msgToDisplay = '<p style="color:'+color+'">'+ user + '<span class="timespan">(' + date + '): </span> <br/>' + '<a href="' + imgData + '" target="_blank"><img src="' + imgData + '"/></a></p>';
container.append(msgToDisplay);
container.scrollTop = container.scrollHeight;
},
_initialEmoji: function() {
var emojiContainer = $('#emojiWrapper'),
docFragment = '';
for (var i = 69; i > 0; i--) {
var emojiItem = '<img ';
emojiItem += ' src="content/emoji/' + i + '.gif" ';
emojiItem += 'title="'+i+'" >';
docFragment+=emojiItem;
};
emojiContainer.append(docFragment);
},
_showEmoji: function(msg) {
var match, result = msg,
reg = /\[emoji:\d+\]/g,
emojiIndex,
totalEmojiNum = $('#emojiWrapper').children().length;
while (match = reg.exec(msg)) { emojiIndex = match[0].slice(7, -1);
if (emojiIndex > totalEmojiNum) {
result = result.replace(match[0], '[X]');
} else {
result = result.replace(match[0], '<img class="emoji" src="content/emoji/' + emojiIndex + '.gif" />');
};
};
return result;
}
};
server.js
var express = require('express'),
app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server),
users =[];
app.use('/',express.static(__dirname+'/www/'));
server.listen(8080);
io.on('connection',function(socket){
socket.on('login',function(nickname){
if(users.indexOf(nickname)>-1){
socket.emit('nickExisted');
}else{
socket.userIndex = users.length;
socket.nickname = nickname;
users.push(nickname);
socket.emit('loginSuccess');
io.sockets.emit('system', nickname, users.length, 'login');
}
});
socket.on('disconnect', function() {
users.splice(socket.userIndex, 1);
socket.broadcast.emit('system', socket.nickname, users.length, 'logout');
});
socket.on('postMsg', function(msg) {
//将消息发送到除自己外的所有用户
socket.broadcast.emit('newMsg', socket.nickname, msg);
console.log('server started'+msg);
});
//接收用户发来的图片
socket.on('img', function(imgData) {
//通过一个newImg事件分发到除自己外的每个用户
socket.broadcast.emit('newImg', socket.nickname, imgData);
});
});
console.log('server started');
6.文字颜色
HTML5新增了一个专门用于颜色选取的input标签
每次发送消息到服务器的时候,多加一个color参数就可以了,同时,在显示消息时调用_displayNewMsg的时候将这个color传递过去
增加输入昵称和发送消息的回车事件
index.html不变
chat.js
var chat;
$(function(){
chat = new Chat();
chat.init();
$("#loginWrapper").show();
$(".wrapper").hide();
$("#loginBtn").click(function(){
var nickName = $('#nicknameInput').val();
if (nickName.trim().length != 0) {
chat.socket.emit('login', nickName);
} else {
$('#nicknameInput').focus();
};
});
$('#sendBtn').click(function() {
var messageInput = $('#messageInput'),
msg = messageInput.val(),
color = $('#colorStyle').val();
messageInput.val('');
messageInput.focus();
if (msg.trim().length != 0) {
chat.socket.emit('postMsg', msg,color); //把消息发送到服务器
chat._displayNewMsg('me', msg,color); //把自己的消息显示到自己的窗口中
};
});
$('#sendImage').change(function() {
//检查是否有文件被选中
if (this.files.length != 0) {
//获取文件并用FileReader进行读取
var file = this.files[0],
reader = new FileReader();
if (!reader) {
chat._displayNewMsg('system', '!your browser doesn\'t support fileReader', 'red');
this.value = '';
return;
};
reader.onload = function(e) {
//读取成功,显示到页面并发送到服务器
this.value = '';
chat.socket.emit('img', e.target.result);
chat._displayImage('me', e.target.result);
};
reader.readAsDataURL(file);
}
});
$('#emoji').click(function(e) {
var emojiwrapper = $('#emojiWrapper');
emojiwrapper.show();
e.stopPropagation();
});
$("body").click(function(e) {
var emojiwrapper = $('#emojiWrapper');
if (e.target != emojiwrapper) {
emojiwrapper.hide();
};
});
$('#emojiWrapper').click(function(e) {
//获取被点击的表情
var target = e.target;
if (target.nodeName.toLowerCase() == 'img') {
var messageInput = $('#messageInput');
messageInput.focus();
messageInput.val(messageInput.val() + '[emoji:' + target.title + ']');
};
});
$('#nicknameInput').keydown(function(e) {
if (e.keyCode == 13) {
var nickName = $('#nicknameInput').val();
if (nickName.trim().length != 0) {
chat.socket.emit('login', nickName);
}
};
});
$('#messageInput').keydown(function(e) {
var messageInput = $('#messageInput'),
msg = messageInput.val(),
color = $('#colorStyle').val();
if (e.keyCode == 13 && msg.trim().length != 0) {
messageInput.val('');
chat.socket.emit('postMsg', msg, color);
chat._displayNewMsg('me', msg, color);
}
});
}); //定义chat类
var Chat = function() {
this.socket = null;
}; //向原型添加业务方法
Chat.prototype = {
init: function() {//初始化
var that = this;
//建立到服务器的socket连接
this.socket = io.connect();
//监听socket的connect事件,此事件表示连接已经建立
this.socket.on('connect', function() {
//连接到服务器后,显示昵称输入框
$('#info').html('get yourself a nickname');
$('#nickWrapper').show();
$('#nicknameInput').focus();
});
this.socket.on('nickExisted', function() {
$('#info').html('nickname is taken, choose another please'); //显示昵称被占用的提示
});
this.socket.on('loginSuccess', function() {
$(document).attr("title",'Chat | ' + $('#nicknameInput').val());
$('#loginWrapper').hide();//隐藏遮罩层显聊天界面
$(".wrapper").show();
$('#messageInput').focus();//让消息输入框获得焦点
});
this.socket.on('system', function(nickName, userCount, type) {
//判断用户是连接还是离开以显示不同的信息
var msg = nickName + (type == 'login' ? ' join in' : 'left');
$('#historyMsg').append("<p>"+msg+"</p>");
chat._displayNewMsg('system ', msg, 'red');
//将在线人数显示到页面顶部
$('#status').html(userCount + (userCount > 1 ? ' users' : ' user') + ' online');
});
this.socket.on('newMsg', function(user, msg,color) {
chat._displayNewMsg(user, msg,color);
});
this.socket.on('newImg', function(user, img) {
chat._displayImage(user, img);
});
this._initialEmoji();
},
_displayNewMsg: function(user, msg, color) {//显示消息
var container = $('#historyMsg'),
msgToDisplay = '',
date = new Date().toTimeString().substr(0, 8),
default_color = color || '#000';
msg = this._showEmoji(msg);
msgToDisplay = '<p style="color:'+color+'">'+user + '<span class="timespan">(' + date + '): </span>' + msg+'</p>';
container.append(msgToDisplay);
container.scrollTop = container.scrollHeight;
},
_displayImage: function(user, imgData, color) {
var container = $('#historyMsg'),
msgToDisplay = '',
date = new Date().toTimeString().substr(0, 8),
mycolor = color || '#000';
msgToDisplay = '<p style="color:'+color+'">'+ user + '<span class="timespan">(' + date + '): </span> <br/>' + '<a href="' + imgData + '" target="_blank"><img src="' + imgData + '"/></a></p>';
container.append(msgToDisplay);
container.scrollTop = container.scrollHeight;
},
_initialEmoji: function() {
var emojiContainer = $('#emojiWrapper'),
docFragment = '';
for (var i = 69; i > 0; i--) {
var emojiItem = '<img ';
emojiItem += ' src="content/emoji/' + i + '.gif" ';
emojiItem += 'title="'+i+'" >';
docFragment+=emojiItem;
};
emojiContainer.append(docFragment);
},
_showEmoji: function(msg) {
var match, result = msg,
reg = /\[emoji:\d+\]/g,
emojiIndex,
totalEmojiNum = $('#emojiWrapper').children().length;
while (match = reg.exec(msg)) { emojiIndex = match[0].slice(7, -1);
if (emojiIndex > totalEmojiNum) {
result = result.replace(match[0], '[X]');
} else {
result = result.replace(match[0], '<img class="emoji" src="content/emoji/' + emojiIndex + '.gif" />');
};
};
return result;
}
};
server.js
var express = require('express'),
app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server),
users =[];
app.use('/',express.static(__dirname+'/www/'));
server.listen(8080);
io.on('connection',function(socket){
socket.on('login',function(nickname){
if(users.indexOf(nickname)>-1){
socket.emit('nickExisted');
}else{
socket.userIndex = users.length;
socket.nickname = nickname;
users.push(nickname);
socket.emit('loginSuccess');
io.sockets.emit('system', nickname, users.length, 'login');
}
});
socket.on('disconnect', function() {
users.splice(socket.userIndex, 1);
socket.broadcast.emit('system', socket.nickname, users.length, 'logout');
});
socket.on('postMsg', function(msg,color) {
//将消息发送到除自己外的所有用户
socket.broadcast.emit('newMsg', socket.nickname, msg,color);
});
//接收用户发来的图片
socket.on('img', function(imgData) {
//通过一个newImg事件分发到除自己外的每个用户
socket.broadcast.emit('newImg', socket.nickname, imgData);
});
});
console.log('server started');
部署上线
先添加一个node.js程序通用的package.json文件
指定程序使用了哪些模块,其他人在获取到代码后,只需通过npm install命令就可以自己下载安装程序中需要的模块了
package.json
{
"name": "hichat",
"description": "a realtime chat web application",
"version": "0.4.0",
"main": "server.js",
"dependencies": {
"express": "4.15.x",
"socket.io": "1.7.x"
},
"engines": {
"node": "0.12.x",
"npm": "2.15.x"
}
}
注:
由于是onchange事件时,发送图片,所以连续发同一张图的话就没法发送了
方法一:
$("#file").val("");
方法二:
var file = $("#file");
file.after(file.clone().val(""));
file.remove();
但是如果是直接在onchang事件里写的话
$('#sendImage').change(function() {
//检查是否有文件被选中
if (this.files.length != 0) {
//获取文件并用FileReader进行读取
var file = this.files[0],
reader = new FileReader();
if (!reader) {
chat._displayNewMsg('system', '!your browser doesn\'t support fileReader', 'red');
this.value = '';
return;
};
reader.onload = function(e) {
//读取成功,显示到页面并发送到服务器
this.value = '';
chat.socket.emit('img', e.target.result);
chat._displayImage('me', e.target.result);
};
reader.readAsDataURL(file);
$("#sendImage").val("");
//var file_obj = $("#sendImage");
// file_obj.after(file_obj.clone().val(""));
//file_obj.remove();
}
});
如果直接这样写,方法二是不好使的,方法一没问题
可以将index.html此处修改
<input id="sendImage" type="file" value="image" onchange="changeImg(this)"/>
chat.js去掉$('#sendImage').change
增加
function changeImg(e){
//检查是否有文件被选中
if (e.files.length != 0) {
//获取文件并用FileReader进行读取
var file = e.files[0],
reader = new FileReader();
if (!reader) {
chat._displayNewMsg('system', '!your browser doesn\'t support fileReader', 'red');
e.value = '';
return;
};
reader.onload = function(e) {
//读取成功,显示到页面并发送到服务器
e.value = '';
chat.socket.emit('img', e.target.result);
chat._displayImage('me', e.target.result); };
reader.readAsDataURL(file);
}
$("#sendImage").val("");
/*
var file_obj = $("#sendImage");
file_obj.after(file_obj.clone().val(""));
file_obj.remove();
*/
}
这样一个简单的聊天就写好了,这是广播的形式发送消息,其他的还有待完善
node.js和socket.io实现im的更多相关文章
- Node.js 和Socket.IO 实现chat WEBIM
socket官方: http://socket.io/ 需求:实现WEB IM功能,数据从服务器PUSH 不是PULL websocket是基于HTML5的新特性,不兼容IE6,7,8 .. ...
- 使用Node.js的socket.io模块开发实时web程序
首发:个人博客,更新&纠错&回复 今天的思维漫游如下:从.net的windows程序开发,摸到nodejs的桌面程序开发,又熟悉了一下nodejs,对“异步”的理解有了上上周对操作系统 ...
- node.js和socket.io纯js实现的即时通讯实例分享
在这个例子中,其实node.js并没有真正起到服务器的作用,因为我们这里可以直接运行client.html文件,而不用输入url请求,当 然,要想输入url请求页面内容还需要加入请求静态文件的代码.这 ...
- [Node.js] 基于Socket.IO 的私聊
原文地址:http://www.moye.me/2015/01/02/node_socket-io/ 引子 最近听到这么一个问题:Socket.IO 怎么实现私聊?换个提法:怎么定位到人(端),或者说 ...
- Node.js + Web Socket 打造即时聊天程序嗨聊
前端一直是一块充满惊喜的土地,不仅是那些富有创造性的页面,还有那些惊赞的效果及不断推出的新技术.像node.js这样的后端开拓者直接将前端人员的能力扩大到了后端.瞬间就有了一统天下的感觉,来往穿梭于前 ...
- Node+Express+MongoDB + Socket.io搭建实时聊天应用
Node+Express+MongoDB + Socket.io搭建实时聊天应用 前言 本来开始写博客的时候只是想写一下关于MongoDB的使用总结的,后来觉得还不如干脆写一个node项目实战教程实战 ...
- Node+Express+MongoDB + Socket.io搭建实时聊天应用实战教程(二)--node解析与环境搭建
前言 本来开始写博客的时候只是想写一下关于MongoDB的使用总结的,后来觉得还不如干脆写一个node项目实战教程实战.写教程一方面在自己写的过程中需要考虑更多的东西,另一方面希望能对node入门者有 ...
- Node+Express+MongoDB+Socket.io搭建实时聊天应用实战教程(一)--MongoDB入门
前言 本文并不是网上流传的多少天学会MongoDB那种全面的教程,而意在总结这几天使用MongoDB的心得,给出一个完整的Node+Express+MongoDB+Socket.io搭建实时聊天应用实 ...
- 【译】深入理解python3.4中Asyncio库与Node.js的异步IO机制
转载自http://xidui.github.io/2015/10/29/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3python3-4-Asyncio%E5%BA%93% ...
随机推荐
- ResolveUrl的用法
<script src='<%=ResolveUrl("~/UserControls/M3/Validate.js") %>' type="text/j ...
- java泛型中<? super String>和<? extends String> 的区别
(1)<? super String> is any class which is a superclass of String (including String itself). (I ...
- [转]uboot中SPI Flash Booting配置
转自:https://e2echina.ti.com/question_answer/dsp_arm/sitara_arm/f/25/t/124834 最近和人一起调试SPI FLASH的配置问题,做 ...
- Mac 添加ll命令
执行 vim ~/.bash_profile 该文件有可能不存在,直接编辑即可. 在文件中加入: alias ll='ls -alF' 再执行 source ~/.bash_profile
- WinForm中播放视频示例(含源码)
1背景 这几天一老友要求我做个小软件,在WinForm播放视频.印象中微软有个WM控件直接可以使用,晚上研究下 2实现方式 2.1微软草根 最简单的方式,是直接使用微软的WM控件,也是通过COM方式集 ...
- Opengl绘制我们的小屋(二)第一人称漫游
这章我们先讲第一人称漫游的实现.在openTK里,我们用函数Matrix4.LookAt(caram.Eye,caram.Target,Vector3.UnitY)来放置摄像机,其中三个参数分别与摄像 ...
- matlab中生成随机数的相关知识
randperm()函数: 功能:用于生成从1到N的随机整数,并且没有重复,它本质上是一个随机排序的函数: 用法:1. randperm(n) 随机生成从1到n的不重复的整数: 2. ran ...
- python numpy logic_and
>>> import numpy as np >>> np.logical_and(True, False) False >>> np.logic ...
- SpringMVC系列(十五)Spring MVC与Spring整合时实例被创建两次的解决方案以及Spring 的 IOC 容器和 SpringMVC 的 IOC 容器的关系
一.Spring MVC与Spring整合时实例被创建两次的解决方案 1.问题产生的原因 Spring MVC的配置文件和Spring的配置文件里面都使用了扫描注解<context:compon ...
- e617. Determining the Opposite Component of a Focus Event
The opposite component is the other component affected in a focus event. Specifically, in a focus-lo ...