• NodeJs开发的CLI——与telnet进行通信的聊天程序


    前言: (在NodeJs中,我们想要开启一个tcp协议的做法就是引入net内置对象: 

            const net = require('net'); ——ES6

            var net = require('net'); ——ES5)

    今天,我们来实现一个

      基于TCP协议完成node服务器与telnet客户端通信的聊天程序

    首先,思考我们的需求

         1.开启多个终端页面,可在不同终端中进行用户注册,注册成功后,即开始聊天

         2.使用net开启TCP服务,net.stream的设计

    具体实现界面贴图:

    我们需要做的有两件事

         1.用NodeJs搭建服务器流 ——to dev

         2.实现telnet可视化界面 ——to user

    那么,我们就开始用NodeJs搭建服务器

      • 首先思考,我们是通过TCP协议进行通信,那么选用net模块(nodejs内置)
      • 其次,我们使用net.createServer创建一个服务,createServer方法中参数为一个回调函数,符合事件驱动概念,该回调函数中的参数为connection对象,咱们就使用该对象进行net.stream数据流的传递

    滤清思路后,我们开始:

    const net = require('net');
    // 介于目前ES2015已成新标准,所以采用ES6写法
    
    let server = net.createServer(function (conn) {
        // ...code
    };

    在上述代码中,我们创建了一个server服务器,接下来我们思考,我们的服务器需要对端口进行监听:

    const net = require('net');
    // 介于目前ES2015已成新标准,所以采用ES6写法
    
    let server = net.createServer(function (conn) {
        // ...code
    };
    
    server.listen(3000, function () {
        console.log('33[96m    server listening on *:300033[39m');
    });

    监听端口号为3000,当我们启动服务器时,可以在终端中显示: 

    接下来我们尝试用telnet客户端连接咱们刚搭建好的服务器:(在命令行或者终端内输入 telnet 127.0.0.1 3000)

    *Telnet协议TCP/IP协议族中的一员,是Internet远程登陆服务的标准协议和主要方式

    *若您不知道如何打开telnet,请阅读——<如何在windows10下开启telnet服务>

    那么,我们在telnet客户端界面看到,目前没有任何显示,所以我们需要去设计一个用户使用的界面:

    效果如图:

    我们考虑:如何在终端上显示提示符?

    connection对象上提供了write方法,可以在通过连接的客户端上显示输入内容,所以我们在server对象内部设计用户界面:

    let server = net.createServer(function (conn) {
        // 页面tip
        conn.write(
            '
     > welcome to 33[92mnode-chat33[39m!'
            + '
     > ' + count + ' other people are connected at this time.'
            + '
     > please write your name and press enter: '
        );
    };

    此时,重新利用终端开启telnet,此时用户界面显示如上图。

    既然已经设计好用户注册界面,那么我们应该去考虑如何处理用户输入的数据,这时,我们需要通过connection对象对输入数据注册事件:

    let count = 0;
    // 参数count作计算接入客户端数

    let server = net.createServer(function (conn) {
    count++;
    // 页面tip conn.write( ' > welcome to 33[92mnode-chat33[39m!' + ' > ' + count + ' other people are connected at this time.' + ' > please write your name and press enter: ' ); conn.on('data', function (data) { // ... code } };

    data参数就是用户输入的数据了,不过我们考虑,用户首先输入的应该是用户名其次才是聊天数据,所以我们应该:

        1.用列表对用户名进行保存

        2.判断用户名是否第一次输入信息,以便重新注册

        3.判断用户输入昵称是否已存在

    let count = 0;
    let users = {};
    
    let server = net.createServer(function (conn) {
        count++;
        let nickname;
    
        // 页面tip
        conn.write(
            '
     > welcome to 33[92mnode-chat33[39m!'
            + '
     > ' + count + ' other people are connected at this time.'
            + '
     > please write your name and press enter: '
        );
    
        conn.on('data', function (data) {
            // 删除回车符,否则会出现空行
            data = data.replace('
    ', '');
            if (!nickname) {
                if (users[data]) {
                    conn.write('33[93m> nickname already in use. try again:33[39m ');
                    return;
                } else {
                    nickname = data;
                    users[nickname] = conn;
                    // 将conn对象赋予用户,赋予用户可操作权限
                 }
            } else {
                 // 验证用户为已注册,则输入数据data为聊天信息
                 for (var i in users) {
                       if (i != nickname) {
                           console.log('33[96m > ' + nickname + ':33[39m ' + data + '
    ');
                       }
                 }
              }
        }
    };

    实现后效果:

    用户关闭客户端时,我们不想保存用户名,我们可以注册close事件:

    let count = 0;
    let users = {};
    
    let server = net.createServer(function (conn) {
        count++;
        let nickname;
    
        // ...code
        // 当其中某个用户断开连接时,需要清楚数据
        conn.on('close', function () {
            count--;
            console.log('33[90m > ' + nickname + ' left the room33[39m
    ');
            delete users[nickname];
        });
    };

    到目前为止,我们已经实现了整个聊天程序的功能,那么我们应该思考代码重构:

    我们在用户接入与断开连接时,都写入了提示信息,那么,我们应该将提示信息抽离出来,作为一个广播函数:

    let count = 0;
    let users = {};
    
    let server = net.createServer(function (conn) {
        count++;
        let nickname;
    
        // ...code
        // 当用户退出时,进行广播通知
        let broadcast = (msg, exceptMyself) => {
            for (var i in users) {
                if (!exceptMyself || i != nickname) {
                    users[i].write(msg);
                }
            }
        };
    
    
        // 监听用户行为作出处理
        conn.on('data', function (data) {
            // 删除回车符
            data = data.replace('
    ', '');
            if (!nickname) {
                if (users[data]) {
                    conn.write('33[93m> nickname already in use. try again:33[39m ');
                    return;
                } else {
                    nickname = data;
                    // 将conn对象赋予用户,赋予用户可操作权限
                    users[nickname] = conn;
    
                    broadcast('33[90m > ' + nickname + ' joined the room33[39m
    ');
                }
            } else {
                // 验证用户为已注册,则输入数据(data)为聊天信息
                for (var i in users) {
                    if (i != nickname) {
                        broadcast('33[96m > ' + nickname + ':33[39m ' + data + '
    ', true);
                    }
                }
            }
        });
    
    
        // 当其中某个用户断开连接时,需要清楚数据
        conn.on('close', function () {
            count--;
            broadcast('33[90m > ' + nickname + ' left the room33[39m
    ');
            delete users[nickname];
        });
    };

    实现广播效果:

    加入:

    退出(关闭客户端):

    *注:处理data数据时应设置编码格式  conn.setEncoding('utf8');

    至此,我们的整个聊天程序就大功告成了!

    大家可以在我的github上获取源码——https://github.com/TimRChen/NodeCLI-telnet

    相应操作文档——click here!

     

  • 相关阅读:
    POJ2481:Cows(树状数组)
    Go语言用堆排序的方法进行一千万个int随机数排序.
    【一】注入框架RoboGuice使用:(A brief example of what RoboGuice does)
    POJ3067:Japan(树状数组求逆序对)
    Android开发之ListView实现不同品种分类分隔栏的效果(非ExpandableListView实现)
    躁动不安的const
    JAVA实现RSA加密解密 非对称算法
    Cocos2d坐标系具体解释
    leetcode_Product of Array Except Self
    IIS2008配置URlRewriter
  • 原文地址:https://www.cnblogs.com/tim100/p/6517712.html
Copyright © 2020-2023  润新知