2016-08-21 socket多人聊天室

一分鐘實現(xiàn)網(wǎng)頁多人聊天室【Socket.IO】

socket.io是個基于node.js的快平臺實時通訊框架。
只用不到10行代碼档址,就可以搭建一個簡單的多人實時聊天室血崭。

安裝node.js

由于socket.io使用node.js為服務(wù)端嚷闭,所以必須安裝node.js
Node.js 是一個基于 Chrome V8 引擎的 JavaScript 運(yùn)行環(huán)境。
Node.js 使用了一個事件驅(qū)動眯杏、非阻塞式 I/O 的模型今瀑,
使其輕量又高效。Node.js 的包管理器 npm且警,
是全球最大的開源庫生態(tài)系統(tǒng)。

編寫package.json

新建一個項目文件夾礁遣,編寫package.json文件來描述項目的信息和依賴關(guān)系   
{
  "name": "socket-chat-example",
  "version": "0.0.1",
  "description": "my first socket.io app",
  "dependencies": {}
}     

編寫index.js -服務(wù)端代碼

//使用express模塊快速搭建web服務(wù)器
var express = require('express');
var app = express();
var http = require('http').Server(app);
//使用socket.io監(jiān)聽事件
var io = require('socket.io')(http);
//使用express發(fā)送css js等靜態(tài)資源
app.use(express.static('public'));

//express獲得GET請求時將index.html文件返回給瀏覽器
app.get('/',function(req,res){
    res.sendFile(__dirname + '/index.html');
});

//socket監(jiān)聽連接事件
io.on('connection', function(socket){
  console.log('一個用戶上線了');
  //socket監(jiān)聽失去連接的事件
  socket.on('disconnect', function(){
        console.log('一個用戶下線了');
  });

//當(dāng)socket監(jiān)聽到了'chat message'事件
  socket.on('chat message', function(msg){
   //將收到的信息返回給所有客戶端
    io.emit('chat message',msg);
  });

});

//服務(wù)器監(jiān)聽端口3000
http.listen(3000,function(){
    console.log('listening on *:3000');
})
cd到當(dāng)前目錄,并在命令行用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(){
       //點擊發(fā)送按鈕斑芜,提交輸入的信息
        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 
就可以開始多窗口聊天啦祟霍!

教學(xué)例子

client.js

const net = require('net');

const client = net.connect({port: 8888}, () => {
  // 'connect' listener
  console.log('連上去了~!');
  
});
client.on('data', (data) => {
  console.log('收到數(shù)據(jù)');
  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.進(jìn)入聊天室');
console.log('88.不聊了走人~');
rl.setPrompt('隨便說點什么吧~> ');
rl.prompt();


rl.on('line', (line) => {
  switch(line.trim()) {
    // case '1':
    //   console.log('連接聊天服務(wù)中');
    //   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('記得回來繼續(xù)聊.');
  process.exit(0);
});

server.js

var net = require('net');

var server = net.createServer((socket) => {
    
    var time = getTime();
    //加入一個標(biāo)志
    //加入到客戶列表中    
    var name = clients.add( socket );
    
    //接收客戶發(fā)過來的信息
    socket.on('data', function(data) {      
        // guangbo(data, socket);// 接受來自客戶端的信息  
        console.log('收到數(shù)據(jù)啦---' );
        console.log(data.toString());
        //發(fā)送給所有人  
        clients.sendAll(socket,data);
    });
    //客戶關(guān)閉了連接
    socket.on('close', function(data) {
        console.log('客戶走了~: ' + socket.remoteAddress + ' ' + socket.remotePort);
        //刪除客戶
        clients.del(socket);
        //發(fā)送給所有人
        clients.sendAll(socket, '有人走了!當(dāng)前聊天人數(shù):' + clients.length());

    });
    console.log('有人連了,客戶端數(shù)量:'+ clients.length() );
    console.log('有人連了,客戶端數(shù)量:'+ clients.length() );
    
    socket.write(time +'  歡迎進(jìn)來聊天~ ' + name + '!\n' + '當(dāng)前聊天人數(shù):' + clients.length());   
    clients.sendAll(socket, '有人進(jìn)來了!當(dāng)前聊天人數(shù):' + clients.length());
}).on('error', (err) => {
  // handle errors here
  throw err;
});

clients = new Object();
clients.list = [];
//添加客戶端到客戶列表
/*
    socket  當(dāng)前的連接
*/
clients.add = function(socket){
    socket.name = socket.remoteAddress + ':' + socket.remotePort;
    this.list.push(socket);
    return socket.name;
}
//從客戶端列表刪除
/*
    socket  當(dāng)前的socket
*/
clients.del = function(socket){
    console.log('用戶要走了!刪除他!!');

    for(var i=0 ; i < this.list.length ; i++){
        if(socket == this.list[i]){
            console.log('用戶要走了!已經(jīng)刪除了~~~');
            
            this.list.splice(i,1);
        }
    }
}
//把信息發(fā)給全部人
/*
    socket 當(dāng)前的連接
    data   要發(fā)送的數(shù)據(jù)
*/
clients.sendAll = function(socket,data){
    console.log('開始發(fā)送給全部人啦!!');
    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('發(fā)送給:' + o.name);
                o.write('-----\n' + getTime() + '\n' + socket.name + '說:\n');
                o.write(data);
                o.write('-----');
            } else {
                console.log('socket 失效了:' + o.name);
                //socket斷開了什么的,就不能發(fā)送啦,需要刪除這個連接
                this.del(o);
            }

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


// 對Date的擴(kuò)展杏头,將 Date 轉(zhuǎn)化為指定格式的String
// 月(M)盈包、日(d)、小時(h)醇王、分(m)呢燥、秒(s)、季度(q) 可以用 1-2 個占位符寓娩, 
// 年(y)可以用 1-4 個占位符疮茄,毫秒(S)只能用 1 個占位符(是 1-3 位的數(shù)字) 
// 例子: 
// (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");
}


//開啟聊天服務(wù)器~~
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">發(fā)送</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("連接人數(shù):" + clientList.length);
        console.log("連接數(shù)據(jù)" + 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) {//發(fā)送內(nèi)容存入數(shù)據(jù)庫
            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);//廣播內(nèi)容
        });

         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;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市根暑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌徙邻,老刑警劉巖排嫌,帶你破解...
    沈念sama閱讀 223,126評論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異缰犁,居然都是意外死亡淳地,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評論 3 400
  • 文/潘曉璐 我一進(jìn)店門帅容,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颇象,“玉大人,你說我怎么就攤上這事并徘∏睬” “怎么了?”我有些...
    開封第一講書人閱讀 169,941評論 0 366
  • 文/不壞的土叔 我叫張陵麦乞,是天一觀的道長蕴茴。 經(jīng)常有香客問我,道長姐直,這世上最難降的妖魔是什么倦淀? 我笑而不...
    開封第一講書人閱讀 60,294評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮声畏,結(jié)果婚禮上撞叽,老公的妹妹穿的比我還像新娘。我一直安慰自己插龄,他們只是感情好愿棋,可當(dāng)我...
    茶點故事閱讀 69,295評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著辫狼,像睡著了一般初斑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上膨处,一...
    開封第一講書人閱讀 52,874評論 1 314
  • 那天见秤,我揣著相機(jī)與錄音砂竖,去河邊找鬼。 笑死鹃答,一個胖子當(dāng)著我的面吹牛乎澄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播测摔,決...
    沈念sama閱讀 41,285評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼置济,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了锋八?” 一聲冷哼從身側(cè)響起浙于,我...
    開封第一講書人閱讀 40,249評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎挟纱,沒想到半個月后羞酗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,760評論 1 321
  • 正文 獨居荒郊野嶺守林人離奇死亡紊服,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,840評論 3 343
  • 正文 我和宋清朗相戀三年檀轨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片欺嗤。...
    茶點故事閱讀 40,973評論 1 354
  • 序言:一個原本活蹦亂跳的男人離奇死亡参萄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出煎饼,到底是詐尸還是另有隱情讹挎,我是刑警寧澤,帶...
    沈念sama閱讀 36,631評論 5 351
  • 正文 年R本政府宣布吆玖,位于F島的核電站淤袜,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏衰伯。R本人自食惡果不足惜铡羡,卻給世界環(huán)境...
    茶點故事閱讀 42,315評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望意鲸。 院中可真熱鬧烦周,春花似錦、人聲如沸怎顾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽槐雾。三九已至夭委,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間募强,已是汗流浹背株灸。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評論 1 275
  • 我被黑心中介騙來泰國打工崇摄, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人慌烧。 一個月前我還...
    沈念sama閱讀 49,431評論 3 379
  • 正文 我出身青樓逐抑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親屹蚊。 傳聞我的和親對象是個殘疾皇子厕氨,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,982評論 2 361

推薦閱讀更多精彩內(nèi)容