Socket.io在线聊天室

从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮助我们迅速建立web站点,比起PHP的开发效率更高,而且学习曲线更低。非常适合小型网站,个性化网站,我们自己的Geek网站!!

关于作者

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/nodejs-socketio-chat/

socketio

前言

网络聊天室在web1.0的时代就出现了,但当时技术支持比较有限,大都是通过浏览器插件BHO,JavaApplet,Flash实现的。如今HTML5技术风起云涌,通过websocket实现的网络聊天室,被定义为websocket的第一个实验,就像Hello World一样的简单。

今天我也动手完成了这个实验,感觉真的很爽!

目录

  1. socket.io介绍
  2. 服务器端和客户端通信设计
  3. 服务器端实现
  4. 客户端实现
  5. 完整案例代码

1. socket.io介绍

socket.io一个是基于Nodejs架构体系的,支持websocket的协议用于时时通信的一个软件包。socket.io 给跨浏览器构建实时应用提供了完整的封装,socket.io完全由javascript实现。

基于Nodejs实现webscoket其他的框架,请参考文章:Nodejs实现websocket的4种方式

2. 服务器端和客户端通信设计

chat

上图中client1 和 server 描述通信过程,client2描述对其他的客户端,通过广播进行消息通信。

  1. client1向server发起连接请求
  2. server接受client的连接
  3. client1输入登陆用户名
  4. server返回欢迎语
  5. server通过广播告诉其他在线的用户,client1已登陆
  6. client1发送聊天信息
  7. server返回聊天信息(可省略)
  8. server通过广播告诉其他在线的用户,client1的聊天消息
  9. client1关闭连接,退出登陆
  10. server通过广播告诉其他在线的用户,client1已退出

我们看一下测试截图:
chat2

  1. 左边: aaa 登陆
  2. 右边: bbb 登陆
  3. 左边: aaa 收到 bbb登陆欢迎消息
  4. aaa 和 bbb 实现对话
  5. 右边: bbb 刷新浏览自动退出
  6. 左边: aaa 收到 bbb的退出消息
  7. 右边: CCC 登陆
  8. 左边: aaa 收到 CCC 登陆欢迎消息
  9. aaa 和 CCC 实现对话

3. 服务器端实现

我们这里使用socket.io和express3的混合模式,让HTTP请求和WebSocket请求都使用3000端口。

服务器端实现,只有一个核心文件app.js。


//引入程序包
var express = require('express')
  , path = require('path')
  , app = express()
  , server = require('http').createServer(app)
  , io = require('socket.io').listen(server);

//设置日志级别
io.set('log level', 1); 

//WebSocket连接监听
io.on('connection', function (socket) {
  socket.emit('open');//通知客户端已连接

  // 打印握手信息
  // console.log(socket.handshake);

  // 构造客户端对象
  var client = {
    socket:socket,
    name:false,
    color:getColor()
  }

  // 对message事件的监听
  socket.on('message', function(msg){
    var obj = {time:getTime(),color:client.color};

    // 判断是不是第一次连接,以第一条消息作为用户名
    if(!client.name){
        client.name = msg;
        obj['text']=client.name;
        obj['author']='System';
        obj['type']='welcome';
        console.log(client.name + ' login');

        //返回欢迎语
        socket.emit('system',obj);
        //广播新用户已登陆
        socket.broadcast.emit('system',obj);
     }else{

        //如果不是第一次的连接,正常的聊天消息
        obj['text']=msg;
        obj['author']=client.name;      
        obj['type']='message';
        console.log(client.name + ' say: ' + msg);

        // 返回消息(可以省略)
        socket.emit('message',obj);
        // 广播向其他用户发消息
        socket.broadcast.emit('message',obj);
      }
    });

    //监听出退事件
    socket.on('disconnect', function () {  
      var obj = {
        time:getTime(),
        color:client.color,
        author:'System',
        text:client.name,
        type:'disconnect'
      };

      // 广播用户已退出
      socket.broadcast.emit('system',obj);
      console.log(client.name + 'Disconnect');
    });

});

//express基本配置
app.configure(function(){
  app.set('port', process.env.PORT || 3000);
  app.set('views', __dirname + '/views');
  app.use(express.favicon());
  app.use(express.logger('dev'));
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(path.join(__dirname, 'public')));
});

app.configure('development', function(){
  app.use(express.errorHandler());
});

// 指定webscoket的客户端的html文件
app.get('/', function(req, res){
  res.sendfile('views/chat.html');
});

server.listen(app.get('port'), function(){
  console.log("Express server listening on port " + app.get('port'));
});

var getTime=function(){
  var date = new Date();
  return date.getHours()+":"+date.getMinutes()+":"+date.getSeconds();
}

var getColor=function(){
  var colors = ['aliceblue','antiquewhite','aqua','aquamarine','pink','red','green',
                'orange','blue','blueviolet','brown','burlywood','cadetblue'];
  return colors[Math.round(Math.random() * 10000 % colors.length)];
}

4. 客户端实现

这里我们需要定义几个文件:chat.html, chat.js, jquery.min.js, main.css

1). views/chat.html


<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Socket.io - Simple chat</title>
<link rel="stylesheet" type="text/css" href="css/main.css">
<script src="javascripts/jquery.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script src="javascripts/chat.js"></script>
</head>
<body>
<h1>Socket.io - Simple chat room</h1>
<div>
<span id="status">Connecting...</span>
<input type="text" id="input"/>
</div>
<div id="content"></div>
</body>
</html>

2). public/javascript/chat.js


$(function () {
var content = $('#content');
var status = $('#status');
var input = $('#input');
var myName = false;

//建立websocket连接
socket = io.connect('http://localhost:3000');
//收到server的连接确认
socket.on('open',function(){
status.text('Choose a name:');
});

//监听system事件,判断welcome或者disconnect,打印系统消息信息
socket.on('system',function(json){
var p = '';
if (json.type === 'welcome'){
if(myName==json.text) status.text(myName + ': ').css('color', json.color);
p = '<p style="background:'+json.color+'">system @ '+ json.time+ ' : Welcome ' + json.text +'</p>';
}else if(json.type == 'disconnect'){
p = '<p style="background:'+json.color+'">system @ '+ json.time+ ' : Bye ' + json.text +'</p>';
}
content.prepend(p);
});

//监听message事件,打印消息信息
socket.on('message',function(json){
var p = '<p><span style="color:'+json.color+';">' + json.author+'</span> @ '+ json.time+ ' : '+json.text+'</p>';
content.prepend(p);
});

//通过“回车”提交聊天信息
input.keydown(function(e) {
if (e.keyCode === 13) {
var msg = $(this).val();
if (!msg) return;
socket.send(msg);
$(this).val('');
if (myName === false) {
myName = msg;
}
}
});
});

3). public/javascript/jquery.min.js
从jquery官方下载: http://jquery.com/

4). public/css/main.css


* {padding:0px; margin:0px;}
body{font-family:tahoma; font-size:12px;margin:10px;}
p {line-height:18px;padding:2px;}
div {width:500px;}
#content { 
    padding:5px; 
    background:#ddd; 
    border-radius:5px;
    border:1px solid #CCC; 
    margin-top:10px; 
}
#input { 
    border-radius:2px; 
    border:1px solid #ccc;
    margin-top:10px; 
    padding:5px; 
    width:380px;  
}
#status { 
    width:100px; 
    display:block; 
    float:left; 
    margin-top:15px; 
}

5. 完整案例代码

项目已经上传到github: https://github.com/bsspirit/chat-websocket

下载,安装,启动服务器


git clone https://github.com/bsspirit/chat-websocket.git
npm install
node app.js

打开浏览器
可以多打开几个窗口,模拟不同用户有的对话。
http://localhost:3000

做完实验,感觉棒极了。技术创新的革命!!

转载请注明出处:
http://blog.fens.me/nodejs-socketio-chat/

打赏作者

This entry was posted in Javascript语言实践

0 0 votes
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

84 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
kiki

這可以轉成App 在手機上使用嗎?

Conan Zhang

可以的。用手机的浏览器访问网页,同样可以实现这个功能。

kiki

我昨天把天變成一個.apk檔 在手機上卻無法做使用耶 ….

Conan Zhang

需要chrome,safari,firefox浏览器支持!最好是v8内核的。IE要11以上!
apk要用java实现websocket调用。

Meteor

写的很好,谢谢.

Conan Zhang

过奖了!

elppa

收了!

[…] Socket.io在线聊天室 […]

思 林

为什么我运行app.js报错,说找不到express,是什么原因呢

Conan Zhang

没装express吧

思 林

是啊刚装好 cmd的命令是npm install express,现在可以运行了,需要的可以找我拿安装教程,对了,你这个可以一对一聊天吗

Conan Zhang

可以运行就好!这里留言回复是最快的,如果有大段的描述,可以给我发邮件。:-)

春遥 刘

用来做手机应用的服务器端可行吗?

Conan Zhang

可以,node主要用来解决IO密集型的问题。

A.L.

刚开始学Node.js 教材太老了 …. 学起来好累 , 我研究下你的代码吧

fgjun

socket.id获取不到呢,还是获取错误

Conan Zhang

socket.io

yang wang

请问:我CMD中node app.js之后有info socket.io started,浏览器访问localhost时CMD也有相关的GET信息,但就是在聊天文本框中键入aaa之后回车没有任何反应。。是什么情况呢

Conan Zhang

浏览器,必须支持HTML5。我的测试都在chrome下进行的,你是否用的IE?

yang wang

也是chrome下进行的,win7

yang wang

我的文件目录和运行情况如图

Conan Zhang

没有看到websocket连接的信息,注释这边
io.set(‘log level’, 1);

Conan Zhang

另外,你是否自己改了代码?

yang wang

Could you give me your IM account~ thanks for your help

Conan Zhang

不常用QQ,你可以把更详细的操作过程,发邮件给我。

yang wang

代码里的这句看到了,我没有修改过代码,新手,几乎是按你这个一步一步来的

Charmtiger

博主,我也遇到了这样的问题,我在mac 上node app。js ,成功运行,可以聊天,但是在 服务器 centos 6.5 里面,确跟 yang wang 一样,一直在 connecting….,怀疑是 express 的问题

Conan Zhang

这我也不知道如何解释,自己的环境还是要自己调。

OC

之前用SignalR做实时这一块儿,不过觉得ASP.net灵活性欠缺,于是接触到Node.js。可现在用socket.io担以前SignalR的戏份,发现前者在用户验证和指定目标发送上都好麻烦,有没有什么好的解决办法?

Conan Zhang

用户验证之类,主要是自己去实现的。有一些辅助的包,可以帮助处理用户验证。

看这篇文章:

Express结合Passport实现登陆认证
http://blog.fens.me/nodejs-express-passport/

amberchobit

请问 为甚么我从非本机上 就会一直卡在connecting

Conan Zhang

是不是两台机器的IP不通,或者端口不通,被访火墙屏蔽了?

rf

博主的 . public/javascript/chat.js
例子中是连接的localhost,
//建立websocket连接
socket = io.connect(‘http://localhost:3000’);
如果要测试非本机的,可以修改下ip。

斌 范

请问socket.io.js到底应该放在哪里呢?我的直接是静态文件啊。。会报错“Uncaught TypeError: Object # has no method ‘connect’”

Conan Zhang

你把源代码下载下来。

Steven

服务端运行会出错 是什么原因

Conan Zhang

是不是代码写错了?

kevine

是下载的代码啊,怎么会代码错误?

maouzha

这里也有一样的问题,express是4.8.6

Conan Zhang

文中express是3.x的,express4会有一些不兼容的代码,需要自己修改,或者选择文章中的版本。

kevine

是下载的代码啊,怎么会代码错误?

Conan Zhang

你确认一下express的版本,是不是3?

Servlet

博主, 你好。 请问你有没有用socket.io.js 做过性能测试吗? 它的性能如何?

Conan Zhang

没有做过性能测试,应用时没有发现明显的性能问题。

Servlet

你用过它做什么应用? 并发有多大?

Conan Zhang

单节点nodejs+socket.io,并发10,数据量每秒每个连接大概500KB,运行正常。

linin

请问如何创建多个聊天室?

Conan Zhang

可以在服务器端来控制房间及用户的访问权限,可以从网上找找例子。

hellsam

Uncaught ReferenceError: require is not defined socket.io.js:12
(anonymous function)
请问这是什么情况?

Conan Zhang

socket.io包安装了吗? 运行npm install

hy

这个怎么一对一聊天啊 一直没搞定 求博主指教啊

Conan Zhang

这个例子不是一对一的,是聊天室,进入的人都可以发言。

j60017268

大侠,能不能再实现一下:聊天室中的私聊功能。

Conan Zhang

私聊,其实就在server端,增加一个group数组的控制,group保留私聊用户的列表,广播时只在组里发就行了。

程序猿小卡

很详细的例子,顶一个。

Conan Zhang

🙂

[…] socket.io, restify, cleaver, stylus, […]

mokingone

一直http://127.0.0.1:3000/socket.io/socket.io.js 提示找不到 。改了好久 不知道啥原因。 我用的express4

Conan Zhang

express4可能要改动的地方比较多,为什么不用文章中的版本呢?

mokingone

非常感谢楼主的回复,我已经修改好了,express4 可以使用。看楼主的node系列的文章受益颇多。

Conan Zhang

🙂

nike

socket.io做聊天室,做服务器集群(负载均衡)会有什么问题吗?

Conan Zhang

可以前置Nginx做负载均衡,http://blog.fens.me/nodejs-websocket-nginx

nike

如果nginx配置了websocket,会不会有这样的问题,比如:聊天在做socket连接的时候有人命中了服务器1,有人
命中了服务器2,这样在发送聊天广播信息的时候会不会有人收不到消息呢,只给连接到当前的服务器的用户发送?

Conan Zhang

1. nginx只是负载均衡
2. 后台是有Websocket Server是集群,不会出现广播收不到的情况,全站广播性能开销很大。
3. 如果是小范围的广播,你可以自己实现聊天室的功能,让这些用户命中到一台服务器上。

亏小会

楼主请问这是什么问题呢?

Conan Zhang

express没有装好,本文是基于express 3.x的,不支持express4。

luke

你好 有个问题

luke

您好,我是下载的源代码,安装了3.5.0的express,执行顺序如下,却出现了错误,希望指点一下,刚玩nodejs

Conan Zhang

把错误日志贴一下。

yisuoyanyu111

好!

Conan Zhang

🙂

ta_shuo

Conan你好,我现在想在 /chat 控制器下引用socket.io,我该怎么去引用app.js里面的http服务器,require(‘socket.io’)(‘这里怎么引用已经创建的server’), 我现在是直接监听了另外一个端口

Conan Zhang

最近太忙回复晚了。

1. 可以向本文代码一样,写到一个文件,就没有你的问题了。
2. 如果你把代码分到多个文件,用app.js调其他文件时,通过把http作为参数传过去。

jeff

不错,完美运行

Conan Zhang

嘿嘿,棒棒哒

Fangch

感谢!

Conan Zhang

🙂

王强

博主,我用node.js、socket.io和express搭建了一个聊天室例子,PC浏览器访问起来没有问题,手机浏览器为什么没有反应?android手机上下载了最新的chrome也不行

王强

我又找了一部iphoneSE测试也不行

Conan Zhang

不应该啊,是不是你在PC上用的本地IP地址,手机找不到那个IP?

Conan Zhang

在客户端的JS里面,要指定好服务器端的IP地址,不能用localhost,你检查一下?

liheng peng

如何做群的概念,,,加入a群,b群,a群发的消息只有所有a群的人才能接收

84
0
Would love your thoughts, please comment.x
()
x