2016-08-21 socket多人聊天室

一分钟实现网页多人聊天室【Socket.IO】

socket.io是个基于node.js的快平台实时通讯框架。
只用不到10行代码,就可以搭建一个简单的多人实时聊天室。

安装node.js

由于socket.io使用node.js为服务端,所以必须安装node.js
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。
Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,
使其轻量又高效。Node.js 的包管理器 npm,
是全球最大的开源库生态系统。

编写package.json

新建一个项目文件夹,编写package.json文件来描述项目的信息和依赖关系   
{
  "name": "socket-chat-example",
  "version": "0.0.1",
  "description": "my first socket.io app",
  "dependencies": {}
}     

编写index.js -服务端代码

//使用express模块快速搭建web服务器
var express = require('express');
var app = express();
var http = require('http').Server(app);
//使用socket.io监听事件
var io = require('socket.io')(http);
//使用express发送css js等静态资源
app.use(express.static('public'));

//express获得GET请求时将index.html文件返回给浏览器
app.get('/',function(req,res){
    res.sendFile(__dirname + '/index.html');
});

//socket监听连接事件
io.on('connection', function(socket){
  console.log('一个用户上线了');
  //socket监听失去连接的事件
  socket.on('disconnect', function(){
        console.log('一个用户下线了');
  });

//当socket监听到了'chat message'事件
  socket.on('chat message', function(msg){
   //将收到的信息返回给所有客户端
    io.emit('chat message',msg);
  });

});

//服务器监听端口3000
http.listen(3000,function(){
    console.log('listening on *:3000');
})
cd到当前目录,并在命令行用npm安装express和socket.io

编写index.html

<!doctype html>
<html>
  <head>
    <title>Socket.IO chat</title>
    <style>
      * { margin: 0; padding: 0; box-sizing: border-box; }
      body { font: 13px Helvetica, Arial; }
      form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
      form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
      form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
      #messages { list-style-type: none; margin: 0; padding: 0; }
      #messages li { padding: 5px 10px; }
      #messages li:nth-child(odd) { background: #eee; }
    </style>
  </head>
  <body>
    <ul id="messages"></ul>
    <form action="">
      <input id="m" autocomplete="off" /><button>Send</button>
    </form>
    <script src="/socket.io/socket.io.js"></script>
    <script src="http://libs.baidu.com/jquery/1.11.3/jquery.min.js"></script>
    <script>
      var socket = io();
      $('form').submit(function(){
       //点击发送按钮,提交输入的信息
        socket.emit('chat message', $('#m').val());
        $('#m').val('');
        return false;
      });
     //接收到chat message时
      socket.on('chat message', function(msg){
        //将chat message显示在页面
        $('#messages').append($('<li>').text(msg));
      });
    </script>
  </body>
</html>
最后,在命令行中输入node index.js 
在浏览器上输入http://localhost:3030 
就可以开始多窗口聊天啦!

教学例子

client.js

const net = require('net');

const client = net.connect({port: 8888}, () => {
  // 'connect' listener
  console.log('连上去了~!');
  
});
client.on('data', (data) => {
  console.log('收到数据');
  console.log(data.toString());
});

client.on('end', () => {
  console.log('断开了');
    rl.close();
  
});

const readline = require('readline');
const rl = readline.createInterface(process.stdin, process.stdout);
console.log('多人聊天室');
// console.log('1.进入聊天室');
console.log('88.不聊了走人~');
rl.setPrompt('随便说点什么吧~> ');
rl.prompt();


rl.on('line', (line) => {
  switch(line.trim()) {
    // case '1':
    //   console.log('连接聊天服务中');
    //   break;
    case '88':
      console.log('欢迎下次再来~玩耍哦~~!');
      rl.close();
      break;
    default:
        s=line.trim();
        client.write(s+'\r\n');

        rl.setPrompt('随便说点什么吧~> ');
        rl.prompt();
      break;
  }
}).on('close', () => {
  console.log('记得回来继续聊.');
  process.exit(0);
});

server.js

var net = require('net');

var server = net.createServer((socket) => {
    
    var time = getTime();
    //加入一个标志
    //加入到客户列表中    
    var name = clients.add( socket );
    
    //接收客户发过来的信息
    socket.on('data', function(data) {      
        // guangbo(data, socket);// 接受来自客户端的信息  
        console.log('收到数据啦---' );
        console.log(data.toString());
        //发送给所有人  
        clients.sendAll(socket,data);
    });
    //客户关闭了连接
    socket.on('close', function(data) {
        console.log('客户走了~: ' + socket.remoteAddress + ' ' + socket.remotePort);
        //删除客户
        clients.del(socket);
        //发送给所有人
        clients.sendAll(socket, '有人走了!当前聊天人数:' + clients.length());

    });
    console.log('有人连了,客户端数量:'+ clients.length() );
    console.log('有人连了,客户端数量:'+ clients.length() );
    
    socket.write(time +'  欢迎进来聊天~ ' + name + '!\n' + '当前聊天人数:' + clients.length());   
    clients.sendAll(socket, '有人进来了!当前聊天人数:' + clients.length());
}).on('error', (err) => {
  // handle errors here
  throw err;
});

clients = new Object();
clients.list = [];
//添加客户端到客户列表
/*
    socket  当前的连接
*/
clients.add = function(socket){
    socket.name = socket.remoteAddress + ':' + socket.remotePort;
    this.list.push(socket);
    return socket.name;
}
//从客户端列表删除
/*
    socket  当前的socket
*/
clients.del = function(socket){
    console.log('用户要走了!删除他!!');

    for(var i=0 ; i < this.list.length ; i++){
        if(socket == this.list[i]){
            console.log('用户要走了!已经删除了~~~');
            
            this.list.splice(i,1);
        }
    }
}
//把信息发给全部人
/*
    socket 当前的连接
    data   要发送的数据
*/
clients.sendAll = function(socket,data){
    console.log('开始发送给全部人啦!!');
    for(var i=0 ; i < this.list.length ; i++){
        if(socket !== this.list[i]){
            var o = this.list[i];
            //检查socket是否可以写
            if (o.writable) {
                console.log('发送给:' + o.name);
                o.write('-----\n' + getTime() + '\n' + socket.name + '说:\n');
                o.write(data);
                o.write('-----');
            } else {
                console.log('socket 失效了:' + o.name);
                //socket断开了什么的,就不能发送啦,需要删除这个连接
                this.del(o);
            }

        }
        
    }
}
clients.length = function(){
    return this.list.length;
}


// 对Date的扩展,将 Date 转化为指定格式的String
// 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符, 
// 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) 
// 例子: 
// (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423 
// (new Date()).Format("yyyy-M-d h:m:s.S")      ==> 2006-7-2 8:9:4.18 
Date.prototype.Format = function (fmt) { //author: meizz 
    var o = {
        "M+": this.getMonth() + 1, //月份 
        "d+": this.getDate(), //日 
        "h+": this.getHours(), //小时 
        "m+": this.getMinutes(), //分 
        "s+": this.getSeconds(), //秒 
        "q+": Math.floor((this.getMonth() + 3) / 3), //季度 
        "S": this.getMilliseconds() //毫秒 
    };
    if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
    for (var k in o)
    if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
    return fmt;
}


function getTime(){
    return new Date().Format("yyyy-MM-dd hh:mm:ss");
}


//开启聊天服务器~~
server.listen({
  host: 'localhost',
  port: 8888,
  exclusive: true
});

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title></title>
    <script src="javascripts/jquery-1.11.1.min.js"></script>
    <script src="javascripts/socket.io-1.4.5.js"></script>
    <style>
      *{padding: 0;margin: 0}
      p{}
        table {
            width: 100%;
        }
        .p2{background: pink;}
        .p1{background: rgb(106, 216, 255);}
        .p1,.p2{border: 1px solid pink;padding: 10px;border-radius: 5px;display:inline-block}
        span{margin: 5px 0px;}
        table.tool {
            position: fixed;
            bottom: 0;
            left: 0;
            right: 0;
            padding: 10px;
        }

        #content {
            height: auto;
            padding: 10px;
            padding-bottom: 32px;
        }

        #content tr {
            margin-bottom: 10px;
        }
        #textContent {
            border: 1px solid rgb(106, 216, 255);
            border-radius: 5px;
            padding: 6px;
        }

        .left {
            width: 75%;
        }

        #send {
            width: auto;
            padding: 2px 12px;
            margin: 0 10px;
            line-height: 26px !important;
            border-radius: 5px;
            font-weight: bold;
            color: white;
            border: 0px;
            background-color:rgb(106, 216, 255);
        }
        #findmsg {
            width: auto;
            padding: 2px 12px;
            line-height: 26px !important;
            border-radius: 5px;
            font-weight: bold;
            color: white;
            border: 0px;
            background-color:pink;
        }
        .timer {
            display: block;
            text-align: center;
        }
    </style>
</head>
<body style="background: url(images/47.jpg) ;background-repeat: no-repeat;background-size:100%">
<div>
    <table id="content">
    </table>
</div>

<table class="tool">
    <tr>
        <td class="left">
            <div id="textContent" contenteditable="true" type="text"></div>
        </td>
        <td>
            <button id="send">发送</button>
<!--
            <button id="findmsg">查看聊天记录</button>
          -->
        </td>
    </tr><div id="nav"></div>
</table>

<script>
    var socket = io.connect('http://192.168.105.253:3000');
    var userName = "访客某某";
    socket.on('connect', function () {
        userName = prompt("请输入你的姓名?") || userName;
        socket.emit('join', userName);
    });
    socket.on('chat', function (user, data) {
        var p = document.createElement('tr');
        var direct = 'align-left';
        if (user === userName) {
            direct = 'align-right';
            p.innerHTML = '<td><p>' + user + '</p><p class="p1">' + data + '</p></td>';
        } else {
            p.innerHTML = '<td style="text-align:right"><p>' + user + '</p><p class="p2">' + data + '</p></td>';
        }
        p.className = direct;
        $('#content').appendChild(p);
    });

    $('#send').addEventListener('click', function (target) {
        var content = $('#textContent').innerHTML;
        if (content = content.replace(" ", "")) {
            socket.emit('sendMSG', content);
            $('#textContent').innerHTML = "";
        }
    });

    $('#findmsg').addEventListener('click', function (target) {
            socket.emit('getMSG', content);
    });
    function $(flag) {
        return document.querySelector(flag);
    }
</script>

</body>
</html>

chat.js

var express = require('express');
var router = express.Router();
var socket_io = require('socket.io');
 var db = require('../model/mongoose');
/* GET users listing. */
router.get('/', function (req, res, next) {
    res.send('respond with a resource');
});


router.prepareSocketIO = function (server) {
    var io = socket_io.listen(server);
    var clientList = [];
    var interlocutors = [];
    io.sockets.on('connection', function (socket) {//连接

        clientList.push(socket);
        console.log("连接人数:" + clientList.length);
        console.log("连接数据" + socket);
        socket.on('join', function (user) {//获取名称

            socket.user = user;

            var socketusers ={ user_name:user}
            db.socketuserModel.findOne(socketusers,function(error, result){
                console.log('he'+result+error);
                if(result ==null ){

                    db.socketuserModel.create(socketusers, function(error){});

                }
            });

            //socket.emit('state', 'SERVER', true);
            //socket.broadcast.emit('state', 'SERVER', user + '上线了');//广播名字
        });
        socket.on('sendMSG', function (msg) {//发送内容存入数据库
            var date = new Date();
            var year = date.getFullYear();
            var month = date.getMonth()+1;
            var day = date.getDate();
            var hour = date.getHours();
            var minute = date.getMinutes();
            var second = date.getSeconds();
            var times = year+'年'+month+'月'+day+'日 '+hour+':'+minute+':'+second;
            var socketusersmsg ={ user_name:socket.user,send_msg:msg,send_time:times}
            db.socketusermsgModel.create(socketusersmsg, function(error){});
            socket.emit('chat', socket.user, msg);
            socket.broadcast.emit('chat', socket.user, msg);//广播内容
        });

         socket.on('getMSG', function (msg) {//获取聊天记录

            var usersmsg ={ user_name:socket.user}
            db.socketusermsgModel.find(usersmsg,function(error, result){
                console.log('he'+result+error);
                for(var i=0;i<result.length;i++){
                    console.log(result[i].send_msg);
                }
            })//.sort({send_time:-1}).limit(1);
            //socket.emit('chat', socket.user, msg);
            //socket.broadcast.emit('chat', socket.user, msg);
        });
    });

};

module.exports = router;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容