zoukankan      html  css  js  c++  java
  • 将js进行到底:node学习3

    node重要API之NET——TCP编程之旅

    废话:最近去了一趟上海会了会一个程序员朋友,途径SNH48握手会,说好我就去看看,没想到握手了王诗蒙,掉入巨坑:塞纳河。回来后边听着《春夏秋冬》,边学习用node.js写了一个基于TCP的简易聊天室服务端案例。

    基本网络知识

    目前的互联网共有七层,自下而上分别是:物理层,数据链路层,网络层,传输层,会话层,表示层,应用层。这七层从前往后是从底层到高层设计的,层层封装,每经过一层都会添加自己层的报头,对于普通程序员来说所能接触的其实就是第七层——应用层。说起应用层我们所知道的依然甚少,SSH,FTP,HTTP,SMTP就是属于应用层,从模型上说,他们都继承自TCP,都是对TCP封装扩展加强后运用于不同领域的应用服务。

    了解七层协议关系可参考:http://blog.csdn.net/qq_33044095/article/details/52754295

    早期的Telnet协议更加接近TCP,他能将非telnet服务器的连接(比如http)降级为纯TCP模式。

    基于Telnet客户端使用node.js开发一个基于TCP的telnet的服务端,建立一个聊天室应用。

    需要使用的API——NET模块介绍

    node.js的net模块可以理解为TCP基本模块,与http不同,他是更加基础更加底层的模块。

    引入模块:

    var net = require("net");
    

    创建服务:

    net.createServer(function(conn){
    	//连接后做什么...
    });
    

    注意上述代码在node中,只要客户端请求一次就会执行一次,并且创建一个连接传送给回调函数(这很重要),每一个连接都会存在于内存中,最好在一个外部作用域的数组或者对象中保存这些引用(后面开发聊天室会提到)。

    事件:

    conn.on("事件名",func);
    

    事件名:data,end,close。
    end只能在用户离开关闭连接时触发,如果发生了网络错误是不触发的
    close则只要断开都会触发,更为合适!

    重点是data事件——接收客户端数据:

    1. 用户发送数据后,node接收到就会触发该事件
    2. 特别注意,telnet在用户每按下一个字符(键盘上的键位)就会发送一次,都触发data事件!

    上面的第二点很是麻烦,这会导致绑定在data事件上的function在用户按下键盘后就执行,后面代码中讲述如何解决telnet按下就发送的问题。

    写回:
    向客户端返回数据使用:

    conn.write();
    

    聊天室需求说明

    • Telnet连接上服务器后返回欢迎信息,并要求输入用户名,告诉用户当前多少人在线
    • 输入用户名后连接完成,聊天中显示消息来自的用户名
    • 输入消息后按回车键发送给聊天室其他用户看

    值得注意的是:如何解决回车发送:用户名,消息?因为Telnet会在用户每按下一次键盘发送一次数据,就好像用键盘控制你自己电脑一样每一次输入都有反馈事件!

    《了不起的node.js》一书中并没有对此进行解释,该书代码不完成,使用将会导致按一次发送一次的问题。

    后面代码中提供解决办法!

    package.json

    {
        "name":"chat-serv",
        "version":"1.0",
        "description":"a chat server based on node and TCP/IP"
    }
    

    代码

    var tcp = require("net");
    //users存储在线用户,键为nickname,值为conn引用
    var users = {};
    var count = 0;
    //每一次telnet请求都会生成一个conn
    tcp.createServer(function(conn){
        console.log("New connection come in!")
        conn.write("
     > Welcome to char-serv on node.js!
     > "+count+" people are in the room! 
     > Plese type a nickname for this session(press enter to submit): ");
        count++;
        conn.setEncoding("utf8");
        var nickname = null;
        var line="";//一行字符串(按下回车键)
        conn.on("data",function(data){
            //由于telnet每次输入一个字符都会上传到服务器,对回车进行判断
            if(data == "
    "){
                //如果没有nickname则视为第一次进入,让设置nickname
                //else就视为消息
                if(!nickname){
                    //已存在
                    if(users[line]) {
                        conn.write("
    The nickname "+line+" has already existed, try another:");
                        line = "";//清空之前输入的字符
                        return;
                    }
                    //空名字
                    if(line == ""){
                        conn.write("
    You can't use empty nickname! Try another:");
                        return;
    
                    }
                    nickname = line;
                    users[nickname] = conn;
                    broadcast("
    User["+nickname+"] has joined in!",false);
                    line="";
                }else{
                    broadcast("
    ["+nickname+"]: "+line,true,nickname);
                    line="";
                }
            }else{
                line+=data;//不是回车则补到字符串中
            }
        });
        conn.on("close",function(){
            broadcast("User["+nickname+"] has leaved the room!
    ",true,nickname);
            count--;//计数减一
            delete users[nickname];//删除连接引用
        });
    }).listen(3000,function(){
        console.log("The server listen on port 3000");
    });
    /*
     * 参数:
     * mes:消息
     * excSelf:是否除去本人为false
     * nickname:当前连接者的昵称,作为key主键
     */
    function broadcast(mes,excSelf,nickname){
        for(var i in users){
            if(!excSelf || nickname != i)
                users[i].write(mes+"
    ");
        }
    }
    
    

    分析

    变量分析:
    count:保存连接数目。
    users:对象数组,以用户名为键,以连接引用为值,保存连接引用,主要用于循环推送消息给其他用户,使得消息可以共享。

    解决输入即触发data事件问题
    很简单:

    1. 建立一个新变量line,合并用户每一次发送的单个字符。
    2. 每一次data事件触发都检测输入是否为" ",也就是windows下的回车键,只有当输入为回车键时才broadcast。

    如何判断用户刚刚连接进入?
    nickname设置为空,nickname的作用范围应该在当前连接下,所以放在createServer里。当nickname为空说明这个连接刚建立要求用户输入用户名,在判断为回车按下后设置为用户名;当nickname不是空的时候则将接受的数据合并作为聊天消息广播到聊天室中。

    效果

    这里写图片描述

    这里写图片描述

    这里写图片描述

    这里写图片描述

  • 相关阅读:
    Shapefile文件数据库操作ArcEngine +C#
    INewFeedBack接口ArcGlobe
    如何获取免费Aster GDem数据
    FUCK EFS!!!!!
    5种方法解除开机密码[转]
    blobtracking references[转]
    cvMatND
    二值图像相似性[转]
    OpenCV中打印CvMat的元素
    视觉&图像处理相关链接
  • 原文地址:https://www.cnblogs.com/devilyouwei/p/8423961.html
Copyright © 2011-2022 走看看