美文网首页
使用Node.js编写简易聊天室

使用Node.js编写简易聊天室

作者: 一个认真学代码的pm | 来源:发表于2017-09-23 19:08 被阅读0次

    准备工作

    1. 安装Node.js
      官网下一个装上就行了,不表。
    2. 安装expressWeb 框架
    • 在项目文件夹下创建package.json文件和其他支持文件
    npm init
    
    • 安装并将express其保存在依赖项列表中
    npm install --save express
    
    • 安装并将socket.io其保存在依赖项列表中,作为实时应用提供跨平台实时通信的库
    npm install --save socket.io
    
    • 安装时请务必注意有没有报错,如果报错了没装上,后面是会出现未定义io什么的错误的。
      如果出现 'No repository field.' 的错误,请修改package.json ,添加"private": true,如下:
    {
      "name": "my-super-amazing-app",
      "version": "1.0.0",
      "private": true
    }
    
    1. 按照以下目录创建项目文件
    root/
        - index.js
        - package.json
        - public/
            - index.html
            - main.js
            - style.css
        - node_modules/
            - ...
    

    搭建本地服务器

    index.js中添加:

    var express = require('express');
    var app = express();
    var http = require('http').Server(app);
    
    http.listen(3000, function(){
      console.log('listening on *:3000');
    });
    

    终端运行node index.js,将会看到以下输出:

    证明服务器已经开起来了。

    创建服务器与html、html与js/css的关联

    index.js开头添加:

    var path = require('path');
    

    index.js末尾添加:

    //设定访问地址时所需加载文件的路径,其中__dirname指向当前js代码文件的目录
    //可自行根据目录安排调整路径
    app.get('/', function(req, res){
        res.sendFile(path.join(__dirname, 'public', 'index.html'));
    });
    
    //设定资源目录,为了html能够正常引入js和css等静态文件
    //这里如果不设置或者目录有问题,css和js都是加载不上的
    //只能看见一个光秃秃的html
    app.use(express.static(path.join(__dirname, 'public')));
    

    index.html中引入style.css

    <link rel="stylesheet" href="/style.css">
    

    index.html中引入main.js

    <script src="/main.js"></script>
    

    完成UI界面

    页面说明:

    index.html代码:
    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <title>chat</title>
            <link rel="stylesheet" href="/style.css">
            <script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
        </head>
        <body>
            <div class="wrapper">
                <div class="banner">
                    <h3>聊天室</h3>
                    <span id='status' style='color: #ff6347'></span>
                </div>
                <div id='historyMsg'>
    
                </div>
                <div class="controls">
                    <textarea id="messageInput" placeholder='输入消息'></textarea>
                    <input id="sendBtn" type="button" value="发送">
                </div>
            </div>
    
            <div id="loginWrapper">
                <p id='info'>正在连接到服务器...</p>
                <div id="nickWrapper">
                    <input type="text" placeholder="请输入昵称" id="nicknameInput">
                    <input type="button" id="loginBtn" value="登录">
                </div>
            </div>
     
            <!--【【 高亮 】】一定要引入socket.io.js,不然main.js会报错-->
            <script src="/socket.io/socket.io.js"></script>
            <script src="/main.js"></script>
        </body>
    </html>
    

    style.css代码:

    html, body{
        margin: 0;
        background-color: #efefef;
        font-family: sans-serif;
    }
    
    .wrapper{
        width: 500px;
        height: 520px;
        padding: 5px;
        margin: 0 auto;
        background-color: #ddd;
    }
    
    #loginWrapper{
        position: fixed;
        top: 0;
        right: 0;
        left: 0;
        bottom: 0;
        background-color: rgba(5, 5, 5, 0.6);
        text-align: center;
        color: #fff;
        display: block;
        padding-top: 200px;
    }
    
    #nickWrapper{
        display: none;
    }
    
    .banner{
        height: 70px;
        width: 100%;
        text-align: center;
    }
    
    .controls{
        height: 100px;
        margin: 5px 0px;
        position: relative;
    }
    
    #historyMsg{
        height: 300px;
        background-color: #fff;
        overflow: auto;
        padding: 2px;
    }
    
    .timespan{
        color: #ddd;
    }
    
    #messageInput{
        width: 440px;
        height: 90px;
        max-height: 90px;
        float: left;
    }
    
    #sendBtn{
        width: 50px;
        height: 96px;
        float: left;
    }
    

    事件设计

    建议对照后面的实际事件查看。


    登录流程 发消息流程

    完成客户端 main.js

    (最麻烦的部分来了,在这里将会创建一系列的事件,供本页面的操作、服务器的操作调用)

    1. 首页在页面加载的时候实例并初始化一个chat的程序,然后调用chat的init方法运行程序
    window.onload = function() {
        //实例并初始化chat程序
        var hichat = new Chat();
        hichat.init();
    };
    
    1. 定义Chat类,在这个对象里面将socket初始化
    var Chat = function() {
        this.socket = null;
    };
    
    1. 向Chat类添加两个方法
    Chat.prototype = { //向Chat类添加方法
        init: function() { //初始化方法 },
        _displayNewMsg: function (user, msg, color) { //展示新消息的方法 },
    };
    
    1. 完成init方法
      (以下内容均为init方法内的代码)

    4.1 定义that变量,并将this赋值给that,供事件内部使用

    var that = this;
    

    4.2 建立到服务器的socket连接

    this.socket = io.connect();
    

    4.3 设置客户端的预置事件,'connect'事件和'error'的事件(事件名都是预置的,不能改)

    设置监听socket的'connect'的事件:
    此事件表示成功与服务器创建连接后的行为。
    行为为将p#info的文本变成"请输入昵称",将div#nickWrapper(昵称输入部分)由不可见变为可见,昵称输入框获取焦点。

    this.socket.on('connect', function(){
        $('#info').text('请输入昵称');
        $('#nickWrapper').css({
            "display":"block",
        });
        $('#nicknameInput').focus();
    });
    

    设置监听socket的'error'的事件:
    此事件表示与服务器连接失败后的行为。
    根据是否已经成功登录,分别在不同的位置提示连接失败。

    this.socket.on('error', function(err) {
        if ($('#loginWrapper').css('display') == 'none') {
            $('#status').text('连接失败');
        }
        else {
            $('#info').text('!连接失败');
        }
    });
    

    4.4 设置客户端自定义事件

    设置监听socket的、名为'nickExisted'的事件:
    此事件在判定用户输入的昵称是否被占用时使用。
    用户输入昵称后上传到服务端,如果服务端发现昵称已被占用,则触发此事件,将结果返回到客户端,并将p#info的文本变成"昵称已被占用"。

    this.socket.on('nickExisted', function() {
        $('#info').text('昵称已被占用!');
    });
    

    设置监听socket的、名为'loginSuccess'的事件:
    此事件表示成功登录聊天室后的行为。
    登录成功后整个弹窗部分隐藏,聊天内容输入框获取焦点。

    this.socket.on('loginSuccess', function() {
        $('#loginWrapper').css({
            "display":"none",
        });
        $('#messageInput').focus();
    });
    

    设置监听socket的、名为'system'的事件:
    服务端有人登录的时候触发此事件,服务端将在线人数通知到客户端,并在span#status中展示出来。

    this.socket.on('system', function(nickName, userCount, type){
        var msg = nickName +(type == 'login' ? 'joined' : 'Left');
        that._displayNewMsg('system ', msg, 'red');
        $('#status').text(userCount +'用户在线');
    });
    

    设置监听socket的、名为'newMsg'的事件:
    服务端收到新消息时触发此事件。服务端向客户端广播消息的内容供客户端展示使用。

    this.socket.on('newMsg', function(user, msg, color) {
        that._displayNewMsg(user, msg, color);
    });
    

    4.5 为前端页面绑定事件
    为登录按钮绑定事件:
    当用户点击登录按钮时,如果输入的昵称为非空时,触发服务端的'login'事件。输入昵称为空时继续获取焦点,但不提交。

    $('#loginBtn').bind('click',function(){
        var nickName = $('#nicknameInput').val();  //获取输入框内的昵称
        if (nickName.trim().length != 0){  //判定内容去除空格后是否为空
            that.socket.emit('login', nickName);
        }
        else{
            $('#nicknameInput').focus();
        }
    });
    

    为发送消息的按钮绑定事件:
    当用户点击发送消息按钮时,如果发送内容不为空,则触发服务端的'postMsg'事件,并且在本客户端展示刚发出的消息。

    $('#sendBtn').bind('click', function() {
        var msg = $('#messageInput').val();  //获取输入框内的消息
        var color = '#000';  //由于是用户发出的内容,所以设置为#000色
        $('#messageInput').val('');  //清空输入框
        $('#messageInput').focus(); 
        if (msg.trim().length != 0) {  //判定内容去除空格后是否为空
            that.socket.emit('postMsg', msg, color);  
            that._displayNewMsg('我', msg, color);
            return;
        };
    });
    
    1. 完成'_displayNewMsg'方法
    _displayNewMsg: function (user, msg, color) {  //展示新消息的方法
        var msgToDisplay = $("<p></p>");
        var date = new Date().toTimeString().substr(0, 8);  //获取当前的时间
        msgToDisplay.css({
            "color":color,  //消息字体的颜色
        });
        msgToDisplay.html(user + '<span class="timespan">(' + date + '): </span>' +msg);
        $('#historyMsg').append(msgToDisplay);  //向消息列表插入子元素
        $('#historyMsg').scrollTop = $('#historyMsg').scrollHeight;  //超出窗口高度酒有滚动条
    },
    

    补全服务端 index.js

    1. 补充引入socket.io,及定义用户数组。
      现在index.js的最上方应该是这样的:
    var express = require('express');
    var app = express();
    var http = require('http').Server(app);
    var path = require('path');
    var io = require('socket.io')(http);
    var users = [];
    
    1. 创建服务端事件
      io.on('connection', function(socket){});事件中,console.log('a user connected');的下方创建服务端的一系列事件。

    2.1 设置服务端的预置事件,'disconnect'事件
    (事件名都是预置的,不能改)
    设置监听socket的'disconnect'的事件:
    如果离线用户昵称不为空,则将数组中相应的用户删除,并向其他用户广播。

    socket.on('disconnect', function(){
        if (socket.nickname != null){
            users.splice(users.indexOf(socket.nickname),1);
            socket.broadcast.emit('system', socket.nickname, users.length, 'logout');
        }
    });
    

    2.2 设置服务端自定义事件
    设置监听socket的、名为'login'的事件:
    如果昵称重复,则触发客户端的'nickExisted'事件。
    如果昵称不重复,则将昵称加入数组,并触发客户端的'loginSuccess'事件及'system'事件。

    socket.on('login', function(nickname){
        if (users.indexOf(nickname)>-1){
            socket.emit('nickExisted');
        }
        else{
            socket.nickname = nickname;
            users.push(nickname);
            socket.emit('loginSuccess');
            io.emit('system', nickname, users.length, 'login');
        }
    });
    

    设置监听socket的、名为'postMsg'的事件:
    触发本事件时,服务端触发客户端的'newMsg'事件,并向其他用户广播。

    socket.on('postMsg', function(msg, color) {
        socket.broadcast.emit('newMsg' ,socket.nickname, msg, color);
    });
    
    1. socket.emitio.emit(io.sockets.emit)、socket.broadcast.emit的区别
    • socket.emit 对触发的客户端生效
    • io.emit(io.sockets.emit) 对在线的所有客户端生效
    • socket.broadcast.emit 对除了触发的客户端以外的所有客户端生效

    测试

    完成~

    辣鸡的源码(

    辣鸡写的辣鸡源码

    相关文章

      网友评论

          本文标题:使用Node.js编写简易聊天室

          本文链接:https://www.haomeiwen.com/subject/sdrcextx.html