Swoole从入门到入土(19)——WebSocket服务器[文件传输]

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

 

1、发送方是客户端 

1)服务端接收

$server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
    switch ($frame->opcode)
    {
        case 0x09:
            $pongFrame = new Swoole\WebSocket\Frame;
            $pongFrame->opcode = WEBSOCKET_OPCODE_PONG;
            $server->push($frame->fd, $pongFrame);
            echo "pone\n";
            break;
        case 0x08:
            echo "Close frame received: Code {$frame->code} Reason {$frame->reason}\n";
            break;
        case 0x01:
            echo "Text string\n";
            break;
        case 0x02:
            echo "Binary data\n";
            //服务端在这里接收,$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编码方法进行传递。办法有很多,期待大家多多练习。本章到此结束。

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

 

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

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

posted on 2020-12-29 15:42  咚..咚  阅读(759)  评论(0编辑  收藏  举报

导航