Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

WebSocket.swf源码

  为了在低版本浏览器上兼容WebSocket不得不动用猥琐的AS来实现了。这份源码是第一个版本,基本上模拟了WebSocket在浏览器上的行为。但是由于接口不支持二进制通信,所以目前只支持文本通信。不过其它一些机制,如分片帧、关闭握手,等都实现了。
  这个程序是第一个版本,也许还存在一些不完善的地方,这将在以后的版本慢慢改进。如果有重大的更新我会重新发布新版本。另外由于AS本身没有提供sha1和base64之类的函数,我也不想引用第三方的库。所以对于WebSocket的握手协议需要的随机序列我使用固定序列,使用上并不会造成影响。只是逻辑上和标准的WebSocket规范有些出入而已。
package {
  /*****************************************
    WebSocket兼容方案(AS3)   2013-08-22
    作者:次碳酸钴(admin@web-tinker.com)
  *****************************************/
  import flash.display.*;
  import flash.events.*;
  import flash.net.*;
  import flash.utils.*;
  import flash.system.Security;
  import flash.external.ExternalInterface;
  public class WebSocket extends Sprite {
    private var
    //全局变量
    socket=new Socket,
    stream=new ByteArray,fragment,testament,
    state=0,//0连接,1打开,2半关闭,3关闭
    id=stage.loaderInfo.parameters.id,
    host=stage.loaderInfo.parameters.host,
    port=stage.loaderInfo.parameters.port*1,
    path=stage.loaderInfo.parameters.path,
    //全局函数
    writeFrame=function(opcode,data=null){
      var buffer,data,mask,l,i;
      if(!data)data=new ByteArray;
      buffer=new ByteArray,l=data.length;
      //头信息
      buffer.writeByte(128+opcode);
      if(l<126)
        buffer.writeByte(128+l);
      else if(l<0x10000)
        buffer.writeByte(128+126),
        buffer.writeShort(l);
      else
        buffer.writeByte(128+127),
        buffer.writeUnsignedInt(0),
        buffer.writeUnsignedInt(l);
      //掩码信息
      mask=new ByteArray;
      mask.writeUnsignedInt(Math.random()*0x100000000|0);
      buffer.writeBytes(mask);
      //数据信息
      for(i=0;i<l;i++)buffer.writeByte(data[i]^mask[i%4]);
      //发送
      socket.writeBytes(buffer);
      socket.flush();
    },readFrame=function(){
      var fin,opcode,data,size,temp,frame;
      stream.position=0;
      //读取帧的头信息
      temp=stream.readByte(),fin=(temp&128)>>7,opcode=temp&127;
      size=stream.readByte()&127;
      if(size==126)size=stream.readUnsignedShort();
      else if(size==127)
        stream.posotion+=4,size=stream.readUnsignedInt();
      if(stream.length<stream.position+size)return; //不足一帧
      //读取需要的数据,并从stream中删除不需要的部分
      data=new ByteArray,temp=new ByteArray;
      stream.readBytes(data,0,size);
      data.position=data.length;
      stream.readBytes(temp);
      stream.clear(),stream.writeBytes(temp);
      //处理分片帧
      if(fin){
        return opcode?{data:data,opcode:opcode}
        :(fragment.data.writeBytes(data),fragment);
      }else{
        if(opcode)fragment={data:data,opcode:opcode};
        else fragment.data.writeBytes(data);
        if(stream.length)return readFrame();
      };
    },js=function(...s){
      return ExternalInterface.call.apply(this,s);
    },log=function(e){
      //调试用,正式使用时可以注释掉
      js("console.log","WebSocket: "+e);
    };
    //构造函数
    public function WebSocket(){
      log("加载完成");
      //发起TCP连接
      socket.connect(host,port);
      //监听事件
      socket.addEventListener("connect",function(e){
        log("连接成功");
        //TCP连接成功发送WebSocket握手请求
        socket.writeUTFBytes([
          "GET "+path+" HTTP/1.1",
          "Upgrade: websocket",
          "Connection: Upgrade",
          "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==",
          "Sec-WebSocket-Version: 13"
        ].join("\r\n")+"\r\n\r\n");
      });
      socket.addEventListener("close",function(e){
        log("服务器断开");
        if(state==3)return;
        if(state==2)socket.close();
        js("state"+id,++state);
        js("close"+id,testament=testament||{code:1006,reason:""});
      });
      socket.addEventListener("socketData",function(e){
        //读取接收到的字节并放入stream中
        var buf=new ByteArray;
        socket.readBytes(buf);
        stream.position=stream.length;
        stream.writeBytes(buf);
        if(state==0){
          //握手返回
          stream.position=0;
          var i,s=stream.readUTFBytes(stream.length);
          stream.position=0;
          if((i=s.indexOf("\r\n\r\n"))==-1)return;
          s=stream.readUTFBytes(i+4);
          var temp=new ByteArray;
          stream.readBytes(temp);
          stream.clear(),stream.writeBytes(temp);
          if(s.indexOf("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=")!=-1)
            log("握手成功"),js("state"+id,state=1),js("open"+id);
          else js("error"+id,"握手失败");
        }else{
          //处理数据帧
          var frame,temp;
          while(frame=readFrame()){
            switch(frame.opcode){
              case 1:
                log("收到文本帧");
                //某些特殊字符无法发送到浏览器,需要转码
                var s=frame.data.toString();
                js("message"+id,{
                  data:s.replace(/\W/g,function(e){
                    e=e.charCodeAt(0).toString(16);
                    return "\\u"+("000"+e).slice(-4);
                  })
                });
                break;
              case 8:
                log("收到结束帧");
                var o=frame.data,code=1006,reason="";
                o.position=0;
                if(o.length>=2)code=o.readUnsignedShort();
                if(o.length>2)reason=o.readUTFBytes(o.length-2);
                testament={code:code,reason:reason};
                break;
              case 9:
                log("收到ping帧");
                writeFrame(10);
                log("发送pong帧");
                break;
              default:
                log("收到不支持的操作码:"+frame.opcode);
            };
          };
        };
      });
      socket.addEventListener("securityError",function(e){
        js("error"+id,{message:"SecurityError"});
      });
      //添加接口方法
      ExternalInterface.addCallback("send",function(e){
        if(state>1)return js("error"+id,{message:"连接已断开"});
        if(state==0)return js("error"+id,{message:"连接未就绪"});
        var temp=new ByteArray;
        temp.writeUTFBytes(e);
        writeFrame(1,temp);
        log("发送文本帧");
      });
      ExternalInterface.addCallback("close",function(e){
        if(state==3)return;
        js("state"+id,++state);
        if(testament)return socket.close();
        writeFrame(8); 
        log("发送结束帧");
      });
    };
  };
};
网名:
3.84.186.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^