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 = '' + obj.time + '' + obj.author + '进入了聊天室。
';
$(".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 = '' + member[i] + ' ';
$("#memer-list").append(html);
}
} else if (obj.type === "loginout") {
//登出消息
var msg = '' + obj.time + '' + obj.author + '离开了聊天室。
';
$(".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 = '' + member[i] + ' ';
$("#memer-list").append(html);
}
}
});
socket.on('message', function(obj) {
if (obj.type === "message") {
//发送消息
var msg = '' + obj.time + '' + obj.author + ':' + obj.msg + '
';
$(".chat-panel").append(msg);
}
});
$("#send").on("click", function() {
var msg = $("#text").val();
if (msg !== "") {
socket.emit('message', msg);
$("#text").val("");
}
});
});
index.ejs
<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">0span>span>
div>
<ul id="memer-list">
ul>
div>
<div class="chat-panel">
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
<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
<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>