从零开始搭建node.js+xmlSocket的WEB聊天室(五)

五、chat socket server实现

聊天室服务器是整个程序中最重要的部分了,因为这里只是个demo,所以尽量简化,所有聊天数据都没有做持久化存储,数据都由全局变量的形式存储,有兴趣的同学可以继续完善,比如用redis来存储聊天数据和其他相关数据。

var net = require('net');
var crypto = require('crypto');
var querystring = require('querystring');
var url = require('url'); < /code>

var serverListenPort = 88;
var server = net.createServer();

var policyListenPort = 843;
var policy = net.createServer(); // flash policy server

var onlines = [];
var rooms = [];

/**
动作cmd
system.connect // 连入服务器
system.close // 退出服务器
room.join // 进入房间
room.leave // 离开房间
room.broadcast // 在房间里说话
message.to // 对私人发送消息
*/
server.listen(serverListenPort);
server.on('connection', function(socket) {

    console.log("[" + now() + "][CNN][" + socket.remoteAddress + "] connected.");
    socket.setKeepAlive(true);

    socket.on('data', function(buffer) {
        if (buffer.toString() == '\0') {
            socket.end('\0');
            console.log("[" + now() + "][REQ][" + socket.remoteAddress + "] send cross-domain-policy from policy server on [" + serverListenPort + "] port.");
            return true;
        }
        var s = buffer.toString('utf8', 0, buffer.length).replace("\u0000", "").replace("\0", "");
        console.log("[" + now() + "][REV][" + socket.remoteAddress + "] " + s);

        var req = JSON.parse(s);
        var nickname = req.nickname;
        var nickid = md5(nickname);
        var cmd = req.cmd;
        var roomname = req.roomname;
        var roomid = md5(roomname);
        var body = req.body;

        if (cmd == 'system.connect') {
            system.connect(socket, nickname, nickid, req);
        }

        if (cmd == 'system.close') {
            system.close(nickid);
        }

        if (cmd == 'message.to') {
            message.to(nickname, nickid, req.toname, body);
        }

        if (cmd == 'room.join') {
            room.join(nickid, roomid);
            room.broadcast(nickname, roomid, body);
            room.onlines(nickname, roomid); // 通知数量变化
        }

        if (cmd == 'room.leave') {
            room.leave(nickid, roomid);
            room.broadcast(nickname, roomid, body); // 临终遗言
            room.onlines(nickname, roomid); // 通知数量变化
        }

        if (cmd == 'room.onlines') {
            var members = room.onlines(roomname);
            // todo
        }

        if (cmd == 'room.broadcast') {
            room.broadcast(nickname, roomid, body);
        }
    });

    socket.on('error', function(exception) {
        console.log("[" + now() + "][ERR][" + socket.remoteAddress + "] " + exception);
    });

    socket.on('close', function() {
        // socket已经断线,不能用remoteAddress获取ip地址。
        console.log("[" + now() + "][CLS][" + socket._peername.address + "] closed.");
    });

});

policy.listen(policyListenPort);
policy.on('connection', function(socket) {
    console.log("[" + now() + "][REQ][" + socket.remoteAddress + "] send cross-domain-policy from policy server on [" + policyListenPort + "] port.");
    socket.end('\0');

    socket.on('error', function(error) {
        console.log("[" + now() + "][ERR] " + error);
    })
})

function md5(str) {
    if (str == '' || str == 'undefined' || str == null) return false;
    var md5hash = crypto.createHash('md5');
    md5hash.update(str);
    return md5hash.digest('hex');
}

function now() {
    var date = new Date();
    var now = date.getFullYear() + "-" + date.getMonth() + "-" + date.getDate() + " " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds();
    return now;
}

var system = {}
system.connect = function(socket, nickname, nickid, query) {
    var member = {
        "socket": socket,
        "nickid": nickid,
        "nickname": nickname,
        "camera": query.camera,
        "microphone": query.microphone,
    };
    onlines[nickid] = member;
};

system.close = function(nickid) {
    delete(onlines[nickid]);
};

var room = {};
room.open = function(roomid) { // 开房?
    for (var id in rooms) {
        if (id == roomid) return id; // 房间已经存在,不要修改对象
    }

    var _room = {
        "admin": [],
        "onlines": []
    };

    rooms[roomid] = _room;
    return roomid;
}

room.join = function(nickid, roomid) {
    var member = onlines[nickid]; // 取得活动用户的信息

    this.open(roomid); // 取得房间id
    rooms[roomid].onlines[nickid] = member; // 加入,更新用户信息

}

room.onlines = function(from, roomid) { // 列出房间在线用户
    var body = [];
    var members = rooms[roomid].onlines;
    for (var nickid in members) {
        if (!members[nickid].socket.writable) { // 处理异常掉线
            delete(rooms[roomid].onlines[nickid]);
            delete(onlines[nickid]);
            continue;
        }

        var s = {
            "nickid": members[nickid].nickid,
            "nickname": members[nickid].nickname,
            "microphone": members[nickid].microphone,
            "camera": members[nickid].camera,
        }
        body.push(s);
    }

    var alived = rooms[roomid].onlines;
    for (var nickid in alived) { // 向所有活着的人发送
        var msg = {
            "cmd": "room.onlines",
            "from": from,
            "body": body
        };
        message.send(alived[nickid].socket, msg);
    }
}

room.block = function(nickname, roomname) { // 某用户在房间被禁言
    // todo:
}

room.leave = function(nickid, roomid) { // 离开房间
    // 离开之前先发表遗言
    delete(rooms[roomid].onlines[nickid]);
}

room.broadcast = function(from, roomid, body) {
    for (var nickid in rooms[roomid].onlines) {
        var msg = {
            "cmd": "room.broadcast",
            "from": from,
            "body": body
        };
        if (rooms[roomid].onlines[nickid].socket.writable) { // 向活着的人发消息
            message.send(rooms[roomid].onlines[nickid].socket, msg);
        } else {
            delete(rooms[roomid].onlines[nickid]);
            delete(onlines[nickid]);
        }
    }
}

var message = {}
message.send = function(socket, msg) {
    if (!socket.writable) return false; // 不可写
    if (msg.body == '' || msg.body == 'undefined' || msg.body == null) return false;

    var time = new Date();

    msg.dateline = time.getHours() + ":" + time.getMinutes() + ":" + time.getSeconds();
    msg.ip = socket.remoteAddress;

    socket.write(JSON.stringify(msg) + "\0");
    console.log("[" + now() + "][SNT][" + socket.remoteAddress + "] " + JSON.stringify(msg));
    return true;
}

message.to = function(fromname, fromid, toname, body) {
    var toid = md5(toname);

    var str = {
        "cmd": "message.to",
        "fromname": fromname,
        "toname": toname,
        "body": body
    }

    message.send(onlines[fromid].socket, str); // 发给自己

    if (!onlines[toid].socket.writable) return false;
    message.send(onlines[toid].socket, str); // 发给对方
}

保存为server.js,在服务器端用node.js来运行,您也可以在http://chat.phpmsg.com/server.js来下载代码。

注意:在调试的时候可能会发现死活就是连不上socket server,或者是不能收发消息,这很有可能是服务器所在机房对端口的黑名单造成的,这时候最好换个机房,或者干脆在本机调试时最好了。

 

发表评论