• 利用Node.js的Net模块实现一个命令行多人聊天室


    1、net模块基本API
    要使用Node.js的net模块实现一个命令行聊天室,就必须先了解NET模块的API使用。NET模块API分为两大类:Server和Socket类、工厂方法。
    Server类如下图所示:
    server类
    net.Server类可以用来创建一个TCP或本地服务器,继承了EventEmitter。
    Socket类如下:
    socket类
    net.Socket类一般用创建一个socket客户端或者是net.Server connection事件的参数。
    工厂方法如下:

    以上三个图展示了API的使用,其实NET模块的内部原理和C++网络编程差不多的,都是以下步骤。
    服务端:

    1. 创建socket套接字
    2. 绑定IP和端口
    3. 启动监听
    4. 等待客户端连接
    5. 与客户端进行通信
    6. 关闭socket

    客户端:

    1. 创建socket套接字
    2. 连接server服务器
    3. 与服务器进行通信
    4. 关闭socket

    如下图所示:

    2、聊天室的设计和实现
    上面学习了NET模块API的使用,接下来便开始实现命令行聊天室,我们不需要做的很复杂,只需实现如下功能即可:

    1. 用户自定义昵称,不可更改
    2. 当有新的用户进入聊天室,或者用户离开聊天室,广播给其他用户
    3. 用户发送信息,需广播给其他用户
    4. 客户端与服务端建立心跳机制
    5. 用户输入'exit'或者'quit'可以退出聊天室

    确定功能之后,便开始代码的编写。这里我就不一步步分析,直接上代码了,首先是服务端:
    Server:

    const net = require('net');
    const server = net.createServer();
    const clients = {};//保存客户端的连接
    var client = null;//当前客户连接
    var uid = 0;
    server.on('connection',(socket)=>{
        //启动心跳机制
        var isOnline = !0;
        var keepAliveTimer = socket.timer = setInterval(()=>{
            if(!isOnline){
                client = socket;
                quit(socket.nick);
                return;
            }
            if(socket.writable){
                isOnline = !1;
                socket.write('::');
            }else{
                client = socket;
                quit(socket.nick);
            }
        },3000);
        socket.on('end',()=>{
            console.log(`client disconnected.
    
    `);
            socket.destroy();
        });
        socket.on('error',(error)=>{
            console.log(error.message);
        });
        socket.on('data',(chunk)=>{
            client = socket;
            var msg = JSON.parse(chunk.toString());
            if(msg.cmd=='keep'){
                isOnline = !0;
                return;
            }
            dealMsg(msg);
        });
    });
    server.on('error',(err)=>{
        console.log(err);
    });
    server.on('listening',()=>{
        console.log(`listening on ${server.address().address}:${server.address().port}
    
    `);
    });
    server.listen(8060);//启动监听
    /**
     * 处理用户信息
     */
    function dealMsg(msg){
        const cmd = msg.cmd;
        const funs = {
            'login':login,
            'chat':chat,
            'quit':quit,
            'exit':quit
        };
        if(typeof funs[cmd] !== 'function') return !1;
        funs[cmd](msg);
    }
    /**
     * 释放连接资源
     */
    function freeConn(conn){
        conn.end();
        delete clients[conn.uuid];
        conn.timer&&clearInterval(conn.timer);
    }
    /**
     * 用户首次进入聊天室
     */
    function login(msg){
        var uuid = '';
        uuid = getRndStr(15)+(++uid);//产生用户ID
        client.write(`欢迎你,${msg.nick}:这里总共有${Object.keys(clients).length}个小伙伴在聊天.
    `)
        client.nick = msg.nick;
        client.uuid = uuid;
        clients[uuid] = client;
        broadcast(`系统:${msg.nick}进入了聊天室.`);
    
    }
    /**
     * 广播消息
     */
    function broadcast(msg){
        Object.keys(clients).forEach((uuid)=>{
            if((clients[uuid]!=client)& clients[uuid].writable){
                clients[uuid].write(msg);
            }
        });
    }
    /**
     * 退出聊天室
     */
    function quit(nick){
        var message = `小伙伴${nick}退出了聊天室.`;
        broadcast(message);
        freeConn(client);
    }
    
    function chat(msg){
        if(msg.msg.toLowerCase()=='quit'||msg.msg.toLowerCase()=='exit'){
            quit(msg.nick);
            return ;
        }
        var message = `${msg.nick}说:${msg.msg}`;
        broadcast(message);
    }   
    /**
     * 随机指定长度(len)的字符串
     */
    function getRndStr(len=1){
        var rndStr = '';
        for (; rndStr.length < len; rndStr += Math.random().toString(36).substr(2));
        return rndStr.substr(0, len);
    }
    

    客户端代码如下:
    client:

    const net = require('net');
    const cout = process.stdout;
    const cin = process.stdin;
    
    var client = null;
    var nick = '';
    
    cout.write(`请输入昵称:`);
    //监听命令行输入
    cin.on('data',(chunk)=>{
    	if(chunk.toString()!='
    '){
    		if(client === null){
    			nick = (chunk+'').replace(/[
    ]/ig,"");
    			createClient();
    		}else{
    			msg = (chunk+'').replace(/[
    ]/ig,"");
    			client.write(JSON.stringify({
    				cmd: 'chat',
    				msg: msg,
    				nick: nick
    			}));
    			//如果输入是exit或quit则断开连接并退出
    			if(msg.toLowerCase() == 'exit' || msg.toLowerCase() == 'quit'){
    				client.end();
    				cin.end();
    				return;
    			}
    			cout.write(`你说:${msg}
    
    `);
    		}
    	}else{
    		cout.write(`请输入昵称:`);
    	}
    });
    
    function addListener(client) {
    	client.on('connect', () => {
    		cout.write(`已连接到服务器
    
    `);
    		client.write(JSON.stringify({
    			cmd: 'login',
    			msg: 'hello server',
    			nick: nick
    		}));
    	});
    	client.on('end', (chunk) => {
    		cout.write(`与服务器断开连接.
    
    `);
    	});
    	client.on('data', (chunk) => {
    		//如果是心跳信息则回应keep命令
    		if(chunk.toString()=='::'){
    			client.write(JSON.stringify({
    				cmd: 'keep',
    				msg: '',
    				nick: nick
    			}));
    			return ;
    		}
    		cout.write(`${chunk}
    
    `);
    	});
    	client.on('error', (err) => {
    		cout.write(`an error has occured.
    
    ${err}`);
    	});
    }
    /**
     * 创建socket并连接服务器
     */
    function createClient(){
    	console.log('33[2J');//清屏操作
    	cout.write(`输入'EXIT OR QUIT'退出聊天室.
    `);
    	client = new net.Socket()
    	client.connect({port:8060/*,host:'1.1.1.69'*/});
    	addListener(client);
    }
    

    执行结果如下如下:

    到此,一个命令行聊天室便做完了。

  • 相关阅读:
    为什么每天都在学习,生活还是没有任何改善?
    MySql基础汇总
    BeanUtils.copyProperties(待复制对象, 待更新对象) || PropertyUtils.copyProperties(待更新对象, 待复制对象)
    ThreadLocal
    synchronized 锁
    STS报could not find tools.jar in the active JRE
    SpringBoot 定时任务 || cron表达式
    lombok注解
    cron表达式
    Thymeleaf 模板引擎
  • 原文地址:https://www.cnblogs.com/zmxmumu/p/6212054.html
Copyright © 2020-2023  润新知