本文将使用 Node.js 加 web socket 协议打造一个网页即时聊天程序,其中将会使用到 express 和 socket.io 两个包模块,下面会有介绍。
首先放出代码链接【github-wchat】
1. node 的安装与使用 #
关于如何安装 node,可以参考我的上篇文章【使用 node 更新 google hosts】,那篇文章比较详细的介绍了如何安装 node。
2. 聊天系统使用到的模块 #
node 的火爆得益于其丰富的模块管理系统(npm),我们能够从这里获取到任何我们想要的模块,同时我们也能上传自己的模块。关于这个聊天项目,我们使用到了两个模块:express 和 socket.io,使用 npm 安装这两个模块即可
npm install express
npm install socket.io
安装成功之后,就需要使用 express 构建我们的 web 开发框架
var express = require("express"), //引入express模块
app = express(),
server = require("http").createServer(app),
io = require("socket.io").listen(server);
app.use("/", express.static(__dirname + "/www")); //指定静态HTML文件的位置
server.listen(80);
这样,我们把所有的前端文件放到 www 目录里就行,比如 html, css, js 和 images 等。更多关于 express 的信息,可以参考这里:express api
3. 简要介绍 node 的事件机制 #
其实在 node 中关于事件机制有很多的内容,这里主要讲解 emit 和 on 的使用:emit
是事件产生器,用来产生事件的,on
是事件接收器,用来接收事件的,那么这两个正好组成了一组。一个在前端产生,另一个在后端接收,或者相反。这样事件就能在前后端进行传递了,同时也能进行参数的传递。
前端:
// wchat.js
socket.emit("login", { nickname: nickname, img: $img.attr("src") });
后端:
// server.js
socket.on("login", function (obj) {
if (User.hasExisted(obj.nickname)) {
socket.emit("nickExisted");
} else {
obj.uid = socket.id;
User.info[socket.id] = obj;
// 通知用户登录成功
socket.emit("loginSuccess", obj.uid);
var s = {
nickname: obj.nickname,
user: User.info,
len: User.getLength(),
type: "login",
};
// 通知其他人有新用户进入
io.sockets.emit("system", s);
}
});
4. web 即时聊天的思路 #
4.1 用户的登录与退出: #
因为暂时没有使用数据库,用户只能临时输入用户名,只要跟之前的用户不重复就能使用。同时在本系统中,还增加了一个可选择自己头像的功能,然后与用户名一起提交到 server.js,server.js 中有一个数组专门来存储所有登陆的用户。后续会添加上数据库的功能,来验证用户,不必再使用数组保存用户。
// 登陆
login : function(){
var nickname = $("#nickname").val();
if(nickname===""){
return;
}
var $img = $(".portrait .selected").find("img"); // 获取头像地址
socket.emit("login", {nickname:nickname, img:$img.attr("src")});
return false;
}
server.js 收到用户的登录信息后,先判断用户名是否重复,如果没有重复,则返回登陆成功的信息,同时提示其他用户有新用户加入,并更新在线用户列表。新用户验证成功后,在wchat
中的 userid 字段中保存从服务器返回的用户编号,获取所有的用户列表,然后就可以聊天了。
socket.on("login", function (obj) {
if (User.hasExisted(obj.nickname)) {
socket.emit("nickExisted");
} else {
obj.uid = socket.id;
User.info[socket.id] = obj;
socket.emit("loginSuccess", obj.uid);
// 更新在线用户列表
var s = {
nickname: obj.nickname,
user: User.info,
len: User.getLength(),
type: "login",
};
io.sockets.emit("system", s);
console.log(obj.nickname + " 已接入");
}
});
4.2 用户发送信息给其他用户 #
用户登录成功后就可以发送消息了:
// user_msg是用户要发送的消息
socket.emit("message", {msg:user_msg}, userid);
当然这之前还需要进行一些其他的检验,比如判断 wchat.userid 是否为空,若为空表示用户还没有登陆,不能进行发送;若输入框内没有内容,直接点击发送按钮,也不能向服务器发送请求。当所有的验证都通过了,就可以发送请求了。
server.js 接收到message
发送过来的请求后,首先验证是否有 userid 这个用户,如果没有则返回提示“该请求非法”,否则就使用msg
向所有人发出这个消息,这个消息里具体的字段有:
{
"status": "success",
"info": {
"userid": "123",
"nickname": "wenzi",
"image": "xx.png",
"msg": "hello, my name is wenzi"
}
}
前端接收到 msg 的请求后,判断 status 的值是否等于 success,若等于,则解析 info 字段,将这些信息追加到输入框中:
if (result.status == "success") {
var info = result.info;
var $content = $(this.content);
this.userid == info.uid && $content.addClass("louzhu");
$content.find("img").attr("src", info.img);
$content.find(".name").html(info.nickname);
$content.find(".timer").html(info.time);
$content.find(".msg").html(info.msg);
$(".record").find(".list").append($content);
this.scroll(); // 保证滚动条在最底部
$("#msg").val("").focus();
} else {
this.warning("认证失败");
}
到这里,最基本的聊天功能已经完成了。
我们还以给这个聊天系统弄一些锦上添花的功能,比如可以修改文字的颜色和大小,发送图片等功能。
4.3 修改文字的颜色和大小 #
在 html 中插入颜色选择器和文字大小修改控件,让用户进行选择。 颜色选择器可以使用 html5 中提供的 input:type=color 标签,文字大小修改控件我采用的是 select 标签让用户进行选择。
<div class="fontlist clearfix">
<div class="fontcolor cc">
<input type="color" id="fontcolor" />
</div>
<div class="fontsize cc">
<select id="fontsize">
<option value="12px">12px</option>
<option value="14px" selected="true">14px</option>
<option value="16px">16px</option>
<option value="18px">18px</option>
<option value="20px">20px</option>
<option value="22px">22px</option>
</select>
</div>
</div>
当然用户也可以不选择,这两个都有默认值,文字颜色默认是#000000
,文字大小默认是14px
,每次发送消息时,都获取一次这两个控件的值:
socket.emit(
"message",
{ msg: msg, color: $("#fontcolor").val(), size: $("#fontsize").val() },
userid
);
服务器接收到这些值后,可以先拼接好 html 后再返回到前端,或者直接把这些属性发送到前端,让前端进行拼接。
4.4 发送图片 #
上面已经实现了基本的聊天功能了,进一步,如果我们还想让用户可以发送图片,那程序便更加完美了。
图片不同于文字,但通过将图片转化为字符串形式后,便可以像发送普通文本消息一样发送图片了,只是在显示的时候将它还原为图片。
在这之前,我们已经将图片按钮在页面放好了,其实是一个文件类型的 input,下面只需在它身上做功夫便可。
用户点击图片按钮后,弹出文件选择窗口供用户选择图片。之后我们可以在 JavaScript 代码中使用FileReader
来将图片读取为 base64 格式的字符串形式进行发送。而 base64 格式的图片直接可以指定为图片的 src,这样就可以将图片用 img 标签显示在页面了。
为此我们监听图片按钮的 change 事件,一但用户选择了图片,便显示到自己的屏幕上同时读取为文本发送到服务器。
$("#fileupload").on("change", function () {
//检查是否有文件被选中
var $this = $(this),
files = $this[0].files;
if (files.length != 0) {
//获取文件并用FileReader进行读取
var file = files[0],
reader = new FileReader();
if (!reader) {
Chat.warning("您的浏览器不支持FileReader");
$this.val("");
return;
}
reader.onload = function (e) {
//读取成功,显示到页面并发送到服务器
$this.val("");
var $img = $('<img src="' + e.target.result + '" alt="img">'),
img = $img[0];
socket.emit(
"message",
{
msg:
'<a href="javascript:;" class="msg_img"><img src="' +
e.target.result +
'" alt="img" data-width="' +
img.width +
'" data-height="' +
img.height +
'"></a>',
color: $("#fontcolor").val(),
size: $("#fontsize").val(),
},
Chat.userid
);
// that._displayImage('me', e.target.result);
};
reader.readAsDataURL(file);
}
});
我们使用上面发送文字的事件来发送图片,然后再通过服务器分发到各个用户的聊天面板上。
有时候我们发送会很大的图片,产生横向的滚动条,破坏掉布局,因此我们限制图片的高度:
.chat .info_msg .msg img {
max-width: 174px;
}
之前不知道怎么实现点击查看原图,后来才发现,能够根据e.target.result
构造出一个 jQuery 对象:$img,然后顺便得出 DOM 对象:var img = $img[0]。通过这个 img 能够获取图片原生的高度和宽度,然后将这个高度和宽度作为属性存储到 img 标签中。用户点击被等比例缩小的图片时,获取这个存储的宽度和高度,展示出来。
5. 总结 #
到这里,基本上所有的东西已经完成了,当然,还有很多改进的地方,会在以后慢慢补上。比如有:发送语音,添加数据库,使用 CSS3 进行窗口抖动,可以一对一聊天等。
其实一对一聊天已经实现了个大概了,在shaobings_one
的分支上,只是目前只实现了前端的功能,还不能真正的发送消息。
如果你有什么建议和意见,欢迎留言。