nodejs实现聊天室

http://download.csdn.net/download/u013401219/8960433

项目结构图

这里写图片描述
需要安装的模块:express,body-parser,cookie-parser ,ejs,express-session,mysql,socket.io
app.js

var express = require('express'); //引用express
var crypto = require('crypto'); //加密
var path = require('path');
var bodyParser = require("body-parser");
var app = express();
var server = require('http').Server(app);
var mysql = require("mysql"); //数据库模块
var io = require('socket.io').listen(server); //socket io模块
var session = require('express-session'); //如果要使用session,需要单独包含这个模块

//连接数据库
var connPool = mysql.createPool({
    host: '127.0.0.1', //主机
    user: 'root', //MySQL认证用户名
    password: '123456', //MySQL认证用户密码
    port: '3306', //端口号
    database: 'sv_chat', //数据库
    waitForConnections: true, //当连接池没有连接或超出最大限制时,设置为true且会把连接放入队列
    //connectionLimit:10,//连接数限制
});
/*conn.connect(function(err) {
    if (err) {
        console.log('[mysql connect] failed:' + err);
        return;
    }
    console.log('[mysql connect] succeed!');
});*/

//express基本配置
app.set('port', 3000);
app.set('views', __dirname + '/views');
app.set("view engine", "ejs");
app.use(bodyParser.urlencoded({
    extended: false
}));
app.use(session({
    secret: 'ScumVirus',
    name: 'sv_chat', //这里的name值得是cookie的name,默认cookie的name是:connect.sid
    cookie: {
        maxAge: 3600000
    }, //设置maxAge是3600000ms,即1h后session和相应的cookie失效过期
    resave: false,
    saveUninitialized: true,
}));
app.use(express.static(path.join(__dirname, 'static')));

//配置路由
//登录页
app.get('/', function(req, res) {
    //res.send('hello world');
    //console.log(getTime() + "  " + req.ip + "  visited.");
    //var ip = req.ip.substring(req.ip.lastIndexOf(":") + 1);
    res.redirect('/login');
});
app.get('/login', function(req, res) {
    res.sendFile(app.get("views") + '/login.html');
});
//登录方法 post
app.post('/login', function(req, res) {
    var name = req.body.name;
    var pwd = getMD5(req.body.pwd);
    var ip = req.ip.substring(req.ip.lastIndexOf(":") + 1);
    var time = getTime();
    var resData = {};
    getUserById(name, function(obj) {
        if (pwd == obj.pwd) {
            var params = [time, ip, obj.id];
            updateLoginInfo(params);
            resData.msg = 0;
        } else {
            resData.msg = -1;
        }
        //设置session
        req.session.user = name;
        res.send(resData);
    });
});
//注册页
app.get('/register', function(req, res) {
    res.sendFile(app.get("views") + '/register.html');
});
//注册方法 post
app.post('/register', function(req, res) {
    var name = req.body.name;
    var pwd = getMD5(req.body.pwd);
    var email = req.body.email;
    var phone = req.body.phone;
    var time = getTime();
    var params = [name, pwd, email, phone, time];
    var resData = {};
    addUser(params, function(flag) {
        if (flag) {
            resData.msg = 0;
        } else {
            resData.msg = -1;
        }
        res.send(resData);
    });
});
//聊天室首页
app.get('/index', function(req, res) {
    if (req.session.user == undefined) {
        res.redirect('/login');
    } else {
        res.render("index", {
            "user": req.session.user
        });
        //res.sendFile(app.get("view") + '/index.html');
    }
});

//监听服务器启动
server.listen(app.get('port'), function() {
    console.log("Express server listening on port " + app.get('port'));
});

//全局变量
var onlineMember = [];

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

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

    // 构造客户端对象
    var client = {
        name: '',
    }

    // 对message事件的监听
    //登录事件
    socket.on('login', function(name) {
        var time = getTime();
        client.name = name;
        var index = getArrIndex(name,onlineMember);
        if(index == -1){
            onlineMember.push(client.name);
            console.log(time + " " + client.name + " login");
        }
        var obj = {
            time: time,
            author: client.name,
            text: '',
            type: 'login',
            member: onlineMember
        };
        socket.emit('system', obj);
        socket.broadcast.emit('system', obj);
    });
    //消息事件
    socket.on('message', function(msg) {
        var obj = {
            time: getTime(),
        };
        obj['msg'] = msg;
        obj['author'] = client.name;
        obj['type'] = 'message';

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

    //监听退出事件
    socket.on('disconnect', function() {
        var index = getArrIndex(client.name, onlineMember);
        if (index > -1) {
            onlineMember.splice(index, 1);
        }
        var time = getTime();
        var obj = {
            time: time,
            author: client.name,
            text: '',
            type: 'loginout',
            member: onlineMember
        };
        console.log(time + " " + client.name + " loginout");

        // 广播用户已退出
        socket.broadcast.emit('system', obj);
    });
});

//获取当前的时间yyyy-MM-dd HH:ii:ss
var getTime = function() {
    var date = new Date();
    return date.getFullYear() + "-" + tc(date.getMonth() + 1) + "-" + tc(date.getDate()) + " " + tc(date.getHours()) + ":" + tc(date.getMinutes()) + ":" + tc(date.getSeconds());
}

//不足10的首位补0
var tc = function(num) {
    if (num >= 10) {
        return num;
    } else {
        return "0" + num;
    }
}

//根据用户获取用户信息
var getUserById = function(name, callback) {
    //执行SQL语句
    var sql = 'select * from sv_user where name=?';
    var params = [name];
    connPool.query(sql, params, function(err, result) {
        if (err) {
            console.log('[SELECT ERROR] - ', err.message);
            return;
        }
        return callback(result[0]);
    });
}

//添加新用户
var addUser = function(params, callback) {
    //执行SQL语句
    var sql = 'insert into sv_user(`name`,`pwd`,`email`,`phone`,`create_time`) values(?,?,?,?,?)';
    connPool.query(sql, params, function(err, result) {
        if (err) {
            console.log('[INSERT ERROR] - ', err.message);
            return callback(false);
        } else {
            console.log('INSERT ID:', result.insertId);
            return callback(true);
        }
    });
}

//更新登录时间和ip
var updateLoginInfo = function(params) {
    var sql = 'update sv_user set login_time=?,login_ip=? where id=?';
    connPool.query(sql, params, function(err, result) {
        if (err) {
            console.log('[UPDATE ERROR] - ', err.message);
        } else {
            console.log('affectedRows:', result.affectedRows);
        }
    });
}

//获取md5加密后的值
var getMD5 = function(str) {
    var md5 = crypto.createHash('md5');
    md5.update(str);
    var d = md5.digest('hex');
    return d;
}

//获取数组中指定值的下标
var getArrIndex = function(str, arr) {
    var index = -1;
    for (var i = 0; i < arr.length; i++) {
        if (arr[i] == str) {
            index = i;
            break;
        }
    }
    return index;
}

chat.js

$(function() {
    //建立websocket连接
    socket = io.connect('http://localhost:3000');
    var userName = $("#user").val();
    socket.emit('login', userName);
    //收到server的系统消息
    socket.on('system', function(obj) {
        if (obj.type === "login") {
            //登录消息
            var msg = '<p><span class="time">' + obj.time + '</span><span class="person">' + obj.author + '</span><span class="msg">进入了聊天室。</span></p>';
            $(".chat-panel").append(msg);

            //刷新在线列表
            $("#memer-list").empty();
            var member = obj.member;
            var cnt = member.length;
            $("#memeber-count").text(cnt);
            for (var i = 0; i < cnt; i++) {
                var html = '<li><span class="label label-info user">' + member[i] + '</span></li>';
                $("#memer-list").append(html);
            }
        } else if (obj.type === "loginout") {
            //登出消息
            var msg = '<p><span class="time">' + obj.time + '</span><span class="person">' + obj.author + '</span><span class="msg">离开了聊天室。</span></p>';
            $(".chat-panel").append(msg);

            //刷新在线列表
            $("#memer-list").empty();
            var member = obj.member;
            var cnt = member.length;
            $("#memeber-count").text(cnt);
            for (var i = 0; i < cnt; i++) {
                var html = '<li><span class="label label-info user">' + member[i] + '</span></li>';
                $("#memer-list").append(html);
            }
        }
    });

    socket.on('message', function(obj) {
        if (obj.type === "message") {
            //发送消息
            var msg = '<p><span class="time">' + obj.time + '</span><span class="person">' + obj.author + '</span>:<span class="msg">' + obj.msg + '</span></p>';
            $(".chat-panel").append(msg);
        }
    });

    $("#send").on("click", function() {
        var msg = $("#text").val();
        if (msg !== "") {
            socket.emit('message', msg);
            $("#text").val("");
        }
    });
});

index.ejs

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>SV在线聊天系统</title>
    <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" href="css/bootstrap.css">
    <link rel="stylesheet" type="text/css" href="css/font-awesome.css">
    <style type="text/css">
        * {
            font-size: 13px;
            font-family: cursive;
        }
        #header {
            position: fixed;
            width: 100%;
            height: 40px;
            background:#0f0f0f;
            border-bottom: 1px solid #ddd;
        }
        #header > h4 {
            margin: 0;
            padding: 0;
            font-weight: bold;
            padding-left: 15px;
            line-height: 40px;
            color:#fff;
        }
        .online-member-panel {
            margin: 55px 0 10px 0;
            width: 100%;
            padding: 15px;
            border:1px solid #ddd;
            border-radius: 10px;
            overflow-y:auto;
            box-shadow:0 0 10px #ccc; 
        }
        .online-member-panel > .header {
            width:100%;
            padding-left: 15px;
            padding-right: 15px;
        }
        ul {
            margin: 10px 0 0 0;
            padding: 0;
            list-style: none;
        }
        #memer-list > li {
            float: left;
            margin-left: 10px;
            margin-right: 10px;
        }
        .chat-panel {
            margin: 10px auto;
            width: 100%;
            height: 300px;
            padding: 15px;
            border:1px solid #ddd;
            border-radius: 10px;
            overflow-y:auto;
        }
        textarea {
            resize:none;
        }
        #send {
            margin-top: 10px;
            float:right;
        }
        p {
            color:#ccc;
        }
        p > span.time {
            color:#ccc;
            margin-right: 10px;
        }
        p > span.person {
            color:#428bca;
            padding-right: 5px;
            padding-left: 5px;
        }
        p > span.msg {
            color:#666;
        }
    </style>
</head>
<body>
    <div id="header">
        <h4>SV在线聊天系统</h4>
    </div>
    <div class="row-fluid">
        <div class="col-md-2"></div>
        <div class="col-md-8">
            <div class="online-member-panel">
                <div class="header">
                    <span>在线会员:<span id="memeber-count" class="label label-warning">0</span></span>
                </div>
                <ul id="memer-list">
                    <!--<li><span class="label label-info">ScumVirus</span></li>-->
                </ul>
            </div>
            <div class="chat-panel">
                <!--<p><span class="time">2015-06-06 06:06:06</span><span class="person">ScumVirus</span>:<span class="msg">233333333333</span></p>-->
            </div>
            <div class="form-group">
                <textarea class="form-control input-sm" rows="3" id="text"></textarea>
                <button class="btn btn-success" type="button" id="send">发送消息</button>
            </div>
        </div>
        <div class="col-md-2"></div>
        <input type="hidden" value="<%=user%>" id="user" />
    </div>
</body>
<script src="js/jquery-2.1.1.js" type="text/javascript"></script>
<script src="js/bootstrap.js" type="text/javascript"></script>
<script src="/socket.io/socket.io.js"></script>
<script src="js/chat.js" type="text/javascript"></script>
</html>

login.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>SV在线聊天系统</title>
    <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" href="css/bootstrap.css">
    <link rel="stylesheet" type="text/css" href="css/font-awesome.css">
    <style type="text/css">
        * {
            font-size: 13px;
            font-family: cursive;
        }
        #login-frame {
            margin:150px auto;
            float:none;
            text-align:center;
        }
        .form-group {
            text-align: center;
        }
        .form-control {
            width:200px;
            display: initial;
        }
        #login-frame > h3 {
            color:#428bca;
        }
        .form-group > button {
            width: 80px;
        }
        .form-group > button:nth-child(2) {
            margin-left: 15px;
        }
    </style>
</head>
<body>
    <div class="container-fluid">
        <div class="row-fluid">
            <div class="col-md-4" id="login-frame">
                <h3>SV在线聊天系统</h3>
                <div class="form-group">
                    <input type="text" class="form-control input-sm" placeholder="请输入用户名" id="name" />
                </div>
                <div class="form-group">
                    <input type="password" class="form-control input-sm" placeholder="请输入密码" id="pwd" />
                </div>
                <div class="form-group">
                    <button type="button" class="btn btn-success" id="login">登录</button>
                    <button type="button" class="btn btn-danger" id="register">注册</button>
                </div>
            </div>
        </div>
    </div>
</body>
<script src="js/jquery-2.1.1.js" type="text/javascript"></script>
<script src="js/bootstrap.js" type="text/javascript"></script>
<script type="text/javascript">
    $("#register").on("click",function(){
        window.location.href = "/register";
    });

    $("#login").on("click",function(){
        var name = $("#name").val();
        var pwd = $("#pwd").val();

        if(name == "" || pwd == ""){
            alert("用户名和密码不能为空!");
            return;
        }

        $.post("/login",{
            name:name,
            pwd:pwd,
        },function(data){
            if(data.msg == '0'){
                window.location.href = "/index";
            }else{
                alert("账号或密码错误!");
            }
        },'json');
    });
</script>
</html>

register.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>SV在线聊天系统</title>
    <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" href="css/bootstrap.css">
    <link rel="stylesheet" type="text/css" href="css/font-awesome.css">
    <style type="text/css">
        * {
            font-size: 13px;
            font-family: cursive;
        }
        #login-frame {
            margin:100px auto;
            text-align:left;
        }
        .form-group > * {
            display: inline-block;
        }
        .form-control {
            width:200px;
            display: initial;
        }
        .alert {
            margin: 0 0 0 10px;
            padding: 5px;
        }
    </style>
</head>
<body>
    <div class="container-fluid">
        <div class="row-fluid">
            <div class="col-md-12" id="login-frame">
                <div class="form-group">
                    <label style="margin-right:2px;">用 户 名:</label>
                    <input type="text" class="form-control input-sm" placeholder="请输入用户名" id="name" />
                    <div class="alert alert-danger">用户名必须为4-16位的英文字母或数字。</div>
                </div>
                <div class="form-group">
                    <label>密  码:</label>
                    <input type="password" class="form-control input-sm" placeholder="请输入密码" id="pwd" />
                    <div class="alert alert-danger">密码必须为6-16位。</div>
                </div>
                <div class="form-group">
                    <label>确认密码:</label>
                    <input type="password" class="form-control input-sm" placeholder="请再次输入密码" id="repwd" />
                    <div class="alert alert-danger">两次输入的密码要一致。</div>
                </div>
                <div class="form-group">
                    <label>邮  箱:</label>
                    <input type="text" class="form-control input-sm" placeholder="请输入邮箱账号" id="email" />
                    <div class="alert alert-danger">Email格式不正确。</div>
                </div>
                <div class="form-group">
                    <label>手  机:</label>
                    <input type="text" class="form-control input-sm" placeholder="请输入手机号码" id="phone" />
                    <div class="alert alert-danger">手机号码格式不正确。</div>
                </div>
                <div class="form-group">
                    <button type="button" class="btn btn-success form-control" id="register">注  册</button>
                </div>
            </div>
        </div>
    </div>
</body>
    <script src="js/jquery-2.1.1.js" type="text/javascript"></script>
    <script src="js/bootstrap.js" type="text/javascript"></script>
    <script type="text/javascript">
    $(".alert").hide();
    $("#register").on("click",function(){
        $(".alert").hide();
        var name = $("#name").val();
        var pwd = $("#pwd").val();
        var repwd = $("#repwd").val();
        var email = $("#email").val();
        var phone = $("#phone").val();

        if(name.length<4 || name.length>16 || !isName(name)){
            $("#name").next().show();
            return;
        }
//      if(pwd.length<6 || pwd.length>16){
//          $("#pwd").next().show();
//          return;
//      }
        if(repwd != pwd){
            $("#repwd").next().show();
            return;
        }
        if(!isEmail(email)){
            $("#email").next().show();
            return;
        }
        if(!isPhone(phone)){
            $("#phone").next().show();
            return;
        }

        $.post("/register",{
            name:name,
            pwd:pwd,
            email:email,
            phone:phone,
        },function(data){
            if(data.msg == '0'){
                alert("注册成功!");
                window.location.href = "/";
            }else{
                alert("注册失败,该用户名可能已被注册!");
            }
        },'json');
    });

    //验证用户名
    function isName(value){
        var reg = /^(?=.*[a-z])[a-z0-9]+/ig
        return reg.test(value);
    }

    //验证邮箱
    function isEmail(value) {
       return /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
    }

    //验证手机号
    function isPhone(value){
       return /^(0|86|17951)?(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$/.test(value);
    }

</script>
</html>
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值