Fork me on GitHub
一共有 61 篇文章,分页:11 / 13

AS3的原型:prototype

写过JS的都清楚原型:prototype,AS3也有这个机制,不多说,直接上代码:

//给数组原型增加一个获取随机数组元素的方法:randomItem 
Array.prototype.getRandomItem = function(): * 
{ 
    return this[Math.floor(Math.random() * this.length)]; 
}; 

//这里设置这个方法不可被(for...in、foreach...in)枚举,非常重要! 
Array.prototype.setPropertyIsEnumerable('getRandomItem', false); 

var arr:Array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 
trace(arr.getRandomItem());

虽然看起来原型也牛逼,可以直接在运行时赋予某个对象一个功能函数,但是也因为如此我们在写代码的时候并不一定知道这个对象有个方法,由此给代码结构带来混乱,所以一般还是不要去扩展原型。

当然总有原型的用武之地,之前看到有人利用原型机制对as代码进行加密解密来保护代码安全。

AS3使用自定义元数据标记,类似C#的类属性、成员属性

在AS3 使用元数据标签最多的应该是swf标签了,如:

[SWF(backgroundColor = 0x000000, frameRate = 24, width = 1000, height = 600)]

那么能不能自定义我们自己的标签,然后在运行时,获取那些含有自定义标签的类或者类成员,然后达到注入的目的呢?答案是肯定的,在robotleg等一些开源框架中也用到了注入。

要是有元数据标签需要在编译器的扩展参数中指定-keep-as3-metadatac参数:

-keep-as3-metadata+=Inject,BindData

后面的Inject、BindData就是下面的例子要用的两个标签

需要注意的是这个编译参数只需要在一个运行库中指定即可,比如我们使用swc,那么编译这个swc指定了-keep-as3-metadatac参数,那么在使用swc的库的项目是不需要再指定的。在-keep-as3-metadatac参数使用“+=”是为了避免把别的参数定义覆盖掉。

比如Test1.as类使用元标签:

package 
{ 
    /** 
     * ... 
     * @author yoyo 
     */ 
    [Inject]      
    public class Test1 
    { 
        [BindData(param1="abc")] 
        public var s:String = 'ssss'; 

        public function Test1() 
        { 
        } 

    } 

};

接下来我们做注入操作,在不需要直接引用s成员的情况下,根据BindDta标签,修改这个成员变量的值:

package 
{ 
    import flash.display.Sprite; 
    import flash.events.Event; 
    import flash.utils.describeType; 
    import flash.xml.XMLNode; 

    /** 
     * ... 
     * @author yoyo 
     */ 
    public class Main extends Sprite 
    { 

        public function Main():void 
        { 
            if (stage) 
                init(); 
            else 
                addEventListener(Event.ADDED_TO_STAGE, init); 
        } 

        private function init(e:Event = null):void 
        { 
            removeEventListener(Event.ADDED_TO_STAGE, init); 
            // describeType方法返回的信息类似下面的xml 
            /** 
               <type name="Test1" base="Object" isDynamic="false" isFinal="false" isStatic="false"> 
               <extendsClass type="Object"/> 
               <variable name="s" type="String"> 
               <metadata name="BindData"> 
               <arg key="param1" value="abc"/> 
               </metadata> 
               <metadata name="__go_to_definition_help"> 
               <arg key="pos" value="131"/> 
               </metadata> 
               </variable> 
               <metadata name="Inject"/> 
               <metadata name="__go_to_ctor_definition_help"> 
               <arg key="pos" value="173"/> 
               </metadata> 
               <metadata name="__go_to_definition_help"> 
               <arg key="pos" value="79"/> 
               </metadata> 
               </type> 

             */ 

            var t:Test1 = new Test1();   

            initMeta(t); 

            trace(t.s);//打印修改后的值 
        } 

        /** 
         * 开始对这个对象进行注入 
         * @param   obj 
         */ 
        private function initMeta(obj:*):void 
        { 
            var xml:XML = describeType(obj); 

            var typeName:String = xml.@name; 

            initClassMeta(obj,typeName,xml); 

            for (var i:int = 0, len:int = xml.variable.length(); i < len;i++) 
            { 
                var variables:XML = xml.variable[i]; 

                initVariableMeta(obj,typeName,variables); 
            } 
        } 

        /** 
         * 获取含有自定义标签的类对象 
         * @param   xml 
         */ 
        private function initClassMeta(obj:*,typeName:String,xml:XML):void 
        { 
            for (var i:int = 0, len:int = xml.metadata.length(); i < len;i++) 
            { 
                var mata:XML = xml.metadata[i]; 
                var metaName:String = mata.@name; 
                switch (metaName) 
                { 
                    case 'Inject': 
                        trace(typeName + ',Inject:' + metaName); 
                        break; 
                } 
            } 
        } 

        /** 
         * 获取含有自定义标签的成员变量 
         * @param   xml 
         */ 
        private function initVariableMeta(obj:*,typeName:String,xml:XML):void 
        { 
            var name:String = xml.@name; 
            var type:String = xml.@type; 
            for (var i:int = 0, len:int = xml.metadata.length(); i < len;i++) 
            { 
                var mata:XML = xml.metadata[i]; 
                var metaName:String = mata.@name; 
                switch (metaName) 
                { 
                    case 'BindData': 
                        trace(typeName + ',BindData:' + metaName + '(' + mata.arg[0].@value + ')'); 
                        obj[name] = 'inject';//修改这个成员变量的值 
                        break; 
                } 
            } 
        } 

    } 
}

ExBuffer,NodeJs的TCP中的粘包、分包问题的解决方案!

ExBuffer,NodeJs的TCP中的粘包、分包问题的解决方案!

推荐结合ByteBuffer来做通信协议!https://github.com/play175/ByteBuffer

主页://play175.github.com/ExBuffer/

代码:https://github.com/play175/ExBuffer

var ExBuffer = require('./ExBuffer'); 

/*************************基本操作****************************/ 

//构造一个ExBuffer,采用4个字节(uint32无符号整型)表示包长,而且是little endian 字节序 
var exBuffer = new ExBuffer().uint32Head().littleEndian(); 
//或者构造一个ExBuffer,采用2个字节(ushort型)表示包长,而且是big endian 字节序 (默认) 
var exBuffer = new ExBuffer().ushortHead().bigEndian(); 

//只要收到满足的包就会触发事件 
exBuffer.on('data',function(buffer){ 
    console.log('>> receive data,length:'+buffer.length); 
    //console.log(buffer); 
}); 


//传入一个9字节长的数据,分多次put (对应于TCP中的分包的情况) 
exBuffer.put(new Buffer([0,9])); 
exBuffer.put(new Buffer([1,2,3,4,5,6,7])); 
exBuffer.put(new Buffer([8,9])); 

//传入一个3个字节的数据和一个6个字节的数据,一次put(对应于TCP中的粘包的情况) 
exBuffer.put(new Buffer([0,3,1,2,3,0,6,1,2,3,4,5,6])); 


//大数据处理测试 (20MB) 
var exBuffer = new ExBuffer().uint32Head().bigEndian(); 
exBuffer.on('data',function(buffer){ 
    console.log('>> receive data,length:'+buffer.length); 
    console.log(buffer); 
}); 
var sbuf = new Buffer(4); 
sbuf.writeUInt32BE(1024*1024*20,0);//写入包长 
exBuffer.put(sbuf); 
exBuffer.put(new Buffer(1024*1024*20)); 


/*************************在socket中的应用****************************/ 

console.log('-----------------------use in socket------------------------'); 

var net = require('net'); 

//测试服务端 
var server = net.createServer(function(socket) { 
  console.log('client connected'); 
  new Connection(socket);//有客户端连入时 
}); 
server.listen(8124); 

//服务端中映射客户端的类 
function Connection(socket) { 
    var exBuffer = new ExBuffer(); 
    exBuffer.on('data',onReceivePackData); 

    socket.on('data', function(data) { 
        exBuffer.put(data);//只要收到数据就往ExBuffer里面put 
    }); 

    //当服务端收到完整的包时 
    function onReceivePackData(buffer){ 
        console.log('>> server receive data,length:'+buffer.length); 
        console.log(buffer.toString()); 

        var data = 'wellcom, I am server'; 
        var len = Buffer.byteLength(data); 

        //写入2个字节表示本次包长 
        var headBuf = new Buffer(2); 
        headBuf.writeUInt16BE(len, 0) 
        socket.write(headBuf); 

        var bodyBuf = new Buffer(len); 
        bodyBuf.write(data); 
        socket.write(bodyBuf); 
    } 
} 

//测试客户端 
var exBuffer = new ExBuffer(); 
var client = net.connect(8124, function() { 

  var data = 'hello I am client'; 
  var len = Buffer.byteLength(data); 

  //写入2个字节表示本次包长 
  var headBuf = new Buffer(2); 
  headBuf.writeUInt16BE(len, 0) 
  client.write(headBuf); 

  var bodyBuf = new Buffer(len); 
  bodyBuf.write(data); 
  client.write(bodyBuf); 

}); 

client.on('data', function(data) { 
  exBuffer.put(data);//只要收到数据就往ExBuffer里面put 
}); 

//当客户端收到完整的数据包时 
exBuffer.on('data', function(buffer) { 
    console.log('>> client receive data,length:'+buffer.length); 
    console.log(buffer.toString()); 
}); 

nodejs的ByteBuffer,和C++通信的利器!

昨天晚上花了点时间,把目前项目里用的AS3版本的ByteBuffer整合到nodejs中。

项目中需要和C++通信总是让我们和二进制打交道。我们需要的是一种二进制打包和解包工具,不需要直接面对二进制流。

项目主页://play175.github.com/ByteBuffer/

代码:https://github.com/play175/ByteBuffer

示例:

var ByteBuffer = require('./ByteBuffer'); 

/*************************基本操作****************************/ 

//压包操作 
var sbuf = new ByteBuffer(); 
var buffer = sbuf.string('abc123你好')//变长字符串,前两个字节表示长度 
                       .int32(-999).uint32(999).float(-0.5) 
                       .int64(9999999).double(-0.000005).short(32767).ushort(65535) 
                       .byte(255) 
                       .vstring('abcd',5)//定长字符串,不足的字节补0x00 
                       .pack();//结尾调用打包方法 

console.log(buffer); 

//解包操作 
var rbuf = new ByteBuffer(buffer); 
//解包出来是一个数组 
var arr = rbuf.string()//变长字符串,前两个字节表示长度 
                    .int32().uint32().float() 
                    .int64().double().short().ushort() 
                    .byte() 
                    .vstring(null,5)//定长字符串,不足的字节补0x00 
                    .unpack();//结尾调用解包方法 

console.log(arr); 


/*************************更多操作****************************/ 

//指定字符编码(默认:utf8):utf8/ascii/ 
var sbuf = new ByteBuffer().encoding('ascii'); 

//指定字节序(默认:BigEndian) 
var sbuf = new ByteBuffer().littleEndian(); 

//指定数据在二进制的初始位置 默认是0 
var sbuf = new ByteBuffer(buffer,2); 

//插入数据到指定位置 
var sbuf = new ByteBuffer(); 
sbuf.int32(9999,0);//把这个int32数据插入到ByteBuffer的第一个位置 

//在打包的时候在开始位置插入一个short型表示包长(通信层中的包头) 
var buffer = sbuf.pack(true); 

NPM安装:

npm install ByteBuffer -g 

用nodejs实现反向代理远程桌面(外网连接局域网内机器实现远程)

这是nodejs的stream类的pipe方法的一个简单应用,pipe方法可以将一个stream流的数据直接传送到另一个stream流,而nodejs的socket是从stream类继承,所以socket也可以使用stream的pipe方法。 办公室的网络是局域网,如果想在家里连接办公室的电脑进行远程管理,通常有以下两种办法:

1、在办公室公网路由器上开放端口映射到该台电脑的ip,或者直接开启DMZ主机,让该台电脑的3389端口暴露到公网,在家通过获得办公室的公网IP地址来进行远程桌面连接。 2、通过第三方远程管理软件,比较好的而且免费的有:TeamViewer。

我实现的这种方式均不同上面两类,我称之为反向代理远程桌面:局域网那台电脑我先称之为机器A,家里需要一台外网可以访问的电脑称之为机器B。在机器A上开启客户端client,开启socket(称之为socketA1)连接机器B上的socket(称之为socketB1),如连接不通者间隔一段时间后再次尝试连接,直至连通。连通后机器A和机器B之间就可以通信了,机器A开启一个socket(称之为socketA2)连接本地的3389远程桌面端口。并将socketA1和socketA2用pipe连接起来:

socketA1.pipe(socketA2);
socketA2.pipe(socketA1);

两条tcp连接都连接以后,然后在机器B上开启一个socket(称之为socketB2),绑定到某个本地端口。并且使得socketB1和socketB2直接用pipe连通:

socketB1.pipe(socketB2);
socketB2.pipe(socketB1);

这样我们通过连接机器B的socketB2绑定的端口,两条tcp连接通信,使得数据最终流向机器A的3389端口。就可以连上位于局域网内的机器A的远程桌面了。

代码以及可运行程序下载:proxyrdp.zip (1.98m,包含node.exe)

如果不能下载请从googlecode下载: https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/fastiny/proxyrdp.zip

请不要将程序放在目录包含中文的文件夹中运行 另外需要说明的是:提供下载代码里默认写的服务端地址是localhost,如果你在同一台电脑测试并登录的话,因为这样你试图通过本机的远程桌面客户端连接本机的远程桌面,那么会发生很恐怖的事情。。。你的电脑会黑屏!只能重启 ^_^ 。所以这就是为什么下面的截图只显示登录界面我没有登录的原因了。