zoukankan      html  css  js  c++  java
  • Swoole从入门到入土(19)——WebSocket服务器[文件传输]

    要利用WebSocket进行文件传输,我们需要讨论两种情况,分别是:发送方可以是客户端,和 发送方是服务端。

    1、发送方是客户端 

    1)服务端接收

    $server->on('message', function (SwooleWebSocketServer $server, $frame) {
        switch ($frame->opcode)
        {
            case 0x09:
                $pongFrame = new SwooleWebSocketFrame;
                $pongFrame->opcode = WEBSOCKET_OPCODE_PONG;
                $server->push($frame->fd, $pongFrame);
                echo "pone
    ";
                break;
            case 0x08:
                echo "Close frame received: Code {$frame->code} Reason {$frame->reason}
    ";
                break;
            case 0x01:
                echo "Text string
    ";
                break;
            case 0x02:
                echo "Binary data
    ";
                //服务端在这里接收,$frame->data即是客户端发过来的文件二进制数据
                $server->push($frame->fd,$frame->data,WEBSOCKET_OPCODE_BINARY);
                break;
            default:
                $server->push($frame->fd, $frame->data);
                break;
        }
    });

    2) 客户端(Javascript)发送

     form.on('submit(fileSend)',function(data){
        var jqFile=$("#file");
        
        try
        {
          var file=jqFile.get(0).files[0];
          var filesize=file.size;
          var reader=new FileReader();
          //reader.readAsBinaryString(file);
          //这里可以通过slice函数,对文件进行分割,多次传送
          var blob=file.slice(0,filesize);
          reader.readAsArrayBuffer(blob);
          
          reader.onload=function(e){
            var arrBuf=e.target.result;
            oWs.send(arrBuf);
          };
        }catch(e){
          layer.msg("文件异常");
        }
        
        return false;
      });

    到这里,有一点需要进行强调,利用slice对文件进行分割发送,多次send将文件内容多次传输到接收端,websocket协议会保证先发的先到达,后发的后达到,不会存在乱序问题。 

    2、发送方是服务端

    1) 服务端发送

    上面的例子已经提到,利用服务端的push可以将文件二进制数据推送到客户端。

    $server->push($frame->fd,$frame->data,WEBSOCKET_OPCODE_BINARY);

    2)客户端接收

    这一部分,就有比较多的讨论内容。首先,服务端将文件数据以二进制的形式传递到客户端,客户端可以选择以blob或arraybuffer方式接收。

    下面我们指定以arraybuffer方式接收:

    oWs=new WebSocket(url);
    oWs.binaryType="arraybuffer";

    接下来,如果arraybuffer存储的是字符串数据,我们面临的就是要判断这些二进制数是是GBK还是UTF-8,并转成对应的string。

    //传入arraybuffer,判断是否utf8
    function IsTextUtf8(inputStream)
    {
      var byteArray=new Uint8Array(inputStream);
    
      var encodingBytesCount = 0;
      var allTextsAreASCIIChars =  true ;
     
      for  ( var  i = 0; i < byteArray.length; i++)
      {
        var current = byteArray[i];
     
        if  ((current & 0x80) == 0x80) allTextsAreASCIIChars =  false ;
             
        if  (encodingBytesCount == 0)
        {
          if  ((current & 0x80) == 0) continue ;
     
          if  ((current & 0xC0) == 0xC0)
          {
            encodingBytesCount = 1;
            current <<= 2;
     
            while  ((current & 0x80) == 0x80)
            {
              current <<= 1;
              encodingBytesCount++;
            }
          }                   
          else
          {
            // Invalid bits structure for UTF8 encoding rule.
            return  false ;
          }
        }               
        else
        {
          if  ((current & 0xC0) == 0x80)
          {                       
            encodingBytesCount--;
          }
          else
          {
            return  false ;
          }
        }
      }
     
      if  (encodingBytesCount != 0)  return  false ;
      
      return  !allTextsAreASCIIChars; 
    }
    //传入arraybuffer,判断是否GBK
    function IsTextGbk(inputStream)
    {
      var byteArray=new Uint8Array(inputStream);
      var nBytes = 0;//GBK可用1-2个字bai节编码,中文两个 ,英文一个
      var chr = byteArray[0];
      var bAllAscii = true; //如果全部都是ASCII,
      for (var i = 0; i<byteArray.length; i++)
      {
        chr = byteArray[i];
        if ((chr & 0x80) != 0 && nBytes == 0)    // 判断是否ASCII编码,如果不是,说明有可能是GBK
        {
            bAllAscii = false;
        }
        if (nBytes == 0)
        {
          if (chr >= 0x80)
          {
            if (chr >= 0x81 && chr <= 0xFE)
            {
              nBytes = +2;
            }
            else
            {
              return false;
            }
            
            nBytes--;
          }
        }
        else
        {
          if (chr < 0x40 || chr>0xFE) return false;
          nBytes--;
        }//else end
      }
      
      if (nBytes != 0) return false;
    
      if (bAllAscii) return true;
    
      return true;
    }
    /*
    以对应字符集读出arraybuffer的内容并转为字符串
    u:arraybuffer
    f:回调函数,参数为string
    encoding:可选gbk和utf-8
    */
    function ab2str(u,f,encoding) {
       var b = new Blob([u]);
       var r = new FileReader();
        r.readAsText(b, encoding);
        r.onload = function (){if(f)f.call(null,r.result)}
    }
    oWs.onmessage=function(e){
          var d;
    if(typeof e.data=="object") { if(IsTextUtf8(e.data)) { ab2str(e.data,function(str){ d="接收UTF-8:"+str; },"utf-8"); return; } else if(IsTextGbk(e.data)) { ab2str(e.data,function(str){ d="接收GBK:"+str; },"gbk"); return; } else { var arr=new Uint8Array(e.data); var str=''; for(var i=0;i<arr.length;i++) { str+=String.fromCharCode(arr[i]); } d="接收对象埂进制数据:"+str; } } else d="接收文本数据:"+e.data; };

    以上,就是关于文件传输的基本操作。我们可以设定自己的机制,连续传输图片数据,不断画到canvas上,造成小电影即视感;或者采用对传输的二进制数据进行base64编码方法进行传递。办法有很多,期待大家多多练习。本章到此结束。

    完整的代码,大家可以看这里:传送门

    ---------------------------  我是可爱的分割线  ----------------------------

    最后博主借地宣传一下,漳州编程小组招新了,这是一个面向漳州青少年信息学/软件设计的学习小组,有意向的同学点击链接,联系我吧。

  • 相关阅读:
    使用python自带的http server共享文件
    vim常用快捷键与快捷操作
    wordpress 安装中文插件提示:To perform the requested action, WordPress needs to access your web server. Please enter your FTP cr
    python async异步编程,await后接task(三)
    虚拟机下ubuntu使用df命令查看磁盘空间小于实际空间
    python线程池
    使用Redis连接错误处理It was not possible to connect to the redis server(s);to create a disconnected multiple
    Sqlite数据库设置密码小工具
    [转]netcore一键部署到linux服务器以服务方式后台运行
    C#3种常见的定时器(多线程)
  • 原文地址:https://www.cnblogs.com/ddcoder/p/14137224.html
Copyright © 2011-2022 走看看