首页 技术 正文
技术 2022年11月14日
0 收藏 452 点赞 5,087 浏览 29111 个字

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

node.js和socket.io实现im

socket.io

Node.js中使用socket的一个包。使用它可以很方便地建立服务器到客户端的sockets连接,发送事件与接收特定事件

安装

npm install socket.io

安装后在node_modules文件夹下新生成了一个socket.io文件夹,其中可以找到一个socket.io.js文件

将它引入到HTML页面,这样我们就可以在前端使用socket.io与服务器进行通信了

通过socket.emit()来激发一个事件

通过socket.on()来侦听和处理对应事件

这两个事件通过传递的参数进行通信

查看安装的模块

node.js和socket.io实现im

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');

node.js和socket.io实现im

node.js和socket.io实现im

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');

node.js和socket.io实现im

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" rel="external nofollow" >
<link rel="shortcut icon" href="favicon.ico" rel="external nofollow" rel="external nofollow" type="image/x-icon">
<link rel="icon" href="favicon.ico" rel="external nofollow" rel="external nofollow" 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" rel="external nofollow" >GitHub</a> | <a href="mailto:liuwayong@gmail.com" rel="external nofollow" >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 + '" rel="external nofollow" rel="external nofollow" rel="external nofollow" 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');

node.js和socket.io实现im

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 + '" rel="external nofollow" rel="external nofollow" rel="external nofollow" 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');

node.js和socket.io实现im

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 + '" rel="external nofollow" rel="external nofollow" rel="external nofollow" 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和socket.io实现im

部署上线

先添加一个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 

这样一个简单的聊天就写好了,这是广播的形式发送消息,其他的还有待完善

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,116
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,588
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,433
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,204
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,840
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,925