转载

每日一博 | 基于 Chromium 内核的 WebSocket 实现

在Android版本4.4之前,由于维护和开发Android版本时使用的是AppleWebkit开源内核,虽然也非常不错,但不支持许多html5 api,在Android4.4使用了Chromium才得以发展,

目前支持的html 5如下:

Web Workers 支持 javaScript多线程
WebSocket 支持 javascript套接字,TCP长链接
IDBFactory/ indexDB 支持 索引数据库
ApplicationCache 支持 web离线缓存
postMessage/onMessage 支持 收发消息
ondeviceorientation,ondevicemotion,onorientationchange 支持 屏幕旋转,移动
onvolumechange 支持 声音改变
RequestAnimationFrame 支持 页面UI动画更新引擎
LocalStorage/sessionStorage 支持 本地缓存
FileReader 支持 本地文件读取
FormData 支持 模拟表单,表单模型
EventSource 支持 Server-Sent Events(SSE)功能,允许服务端推送数据到客户端。(通常叫数据推送)
CacheStorage 不支持 异步缓存
Promise 不支持 异步范式
Crypto 不支持 javascript加密API
WebAudio 不支持 流媒体播放
WebRTC 不支持 流媒体通讯
WebGL 不支持 Web GL图像框架
GeoLocation 支持 地理定位

在这里我们主要了解WebSocket在Android WebView上的支持,这里给出一个基于php WebSocketServer的例子:

【以下例子来自开源中国博客】

体验位置: http://www.yxsss.com/ui/sk.html

(请使用你的PC浏览器和你的Android4.4的设备上的浏览器)

php端

<?php error_reporting(E_ALL ^ E_NOTICE); ob_implicit_flush();  $sk=new Sock('127.0.0.1',8000); $sk->run(); class Sock{  public $sockets;  public $users;  public $master;    private $sda=array();//已接收的数据  private $slen=array();//数据总长度  private $sjen=array();//接收数据的长度  private $ar=array();//加密key  private $n=array();    public function __construct($address, $port){   $this->master=$this->WebSocket($address, $port);   $this->sockets=array($this->master);  }      function run(){   while(true){    $changes=$this->sockets;    $write=NULL;    $except=NULL;    socket_select($changes,$write,$except,NULL);    foreach($changes as $sock){     if($sock==$this->master){      $client=socket_accept($this->master);      $key=uniqid();      $this->sockets[]=$client;      $this->users[$key]=array(       'socket'=>$client,       'shou'=>false      );     }else{      $len=0;      $buffer='';      do{       $l=socket_recv($sock,$buf,1000,0);       $len+=$l;       $buffer.=$buf;      }while($l==1000);      $k=$this->search($sock);      if($len<7){       $this->send2($k);       continue;      }      if(!$this->users[$k]['shou']){       $this->woshou($k,$buffer);      }else{       $buffer = $this->uncode($buffer,$k);       if($buffer==false){        continue;       }       $this->send($k,$buffer);      }     }    }       }     }    function close($k){   socket_close($this->users[$k]['socket']);   unset($this->users[$k]);   $this->sockets=array($this->master);   foreach($this->users as $v){    $this->sockets[]=$v['socket'];   }   $this->e("key:$k close");  }    function search($sock){   foreach ($this->users as $k=>$v){    if($sock==$v['socket'])    return $k;   }   return false;  }    function WebSocket($address,$port){   $server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);   socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);   socket_bind($server, $address, $port);   socket_listen($server);   $this->e('Server Started : '.date('Y-m-d H:i:s'));   $this->e('Listening on   : '.$address.' port '.$port);   return $server;  }      function woshou($k,$buffer){   $buf  = substr($buffer,strpos($buffer,'Sec-WebSocket-Key:')+18);   $key  = trim(substr($buf,0,strpos($buf,"/r/n")));     $new_key = base64_encode(sha1($key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true));      $new_message = "HTTP/1.1 101 Switching Protocols/r/n";   $new_message .= "Upgrade: websocket/r/n";   $new_message .= "Sec-WebSocket-Version: 13/r/n";   $new_message .= "Connection: Upgrade/r/n";   $new_message .= "Sec-WebSocket-Accept: " . $new_key . "/r/n/r/n";      socket_write($this->users[$k]['socket'],$new_message,strlen($new_message));   $this->users[$k]['shou']=true;   return true;     }    function uncode($str,$key){   $mask = array();     $data = '';     $msg = unpack('H*',$str);   $head = substr($msg[1],0,2);     if ($head == '81' && !isset($this->slen[$key])) {      $len=substr($msg[1],2,2);    $len=hexdec($len);    if(substr($msg[1],2,2)=='fe'){     $len=substr($msg[1],4,4);     $len=hexdec($len);     $msg[1]=substr($msg[1],4);    }else if(substr($msg[1],2,2)=='ff'){     $len=substr($msg[1],4,16);     $len=hexdec($len);     $msg[1]=substr($msg[1],16);    }    $mask[] = hexdec(substr($msg[1],4,2));      $mask[] = hexdec(substr($msg[1],6,2));      $mask[] = hexdec(substr($msg[1],8,2));      $mask[] = hexdec(substr($msg[1],10,2));    $s = 12;    $n=0;   }else if($this->slen[$key] > 0){    $len=$this->slen[$key];    $mask=$this->ar[$key];    $n=$this->n[$key];    $s = 0;   }      $e = strlen($msg[1])-2;   for ($i=$s; $i<= $e; $i+= 2) {      $data .= chr($mask[$n%4]^hexdec(substr($msg[1],$i,2)));      $n++;     }     $dlen=strlen($data);      if($len > 255 && $len > $dlen+intval($this->sjen[$key])){    $this->ar[$key]=$mask;    $this->slen[$key]=$len;    $this->sjen[$key]=$dlen+intval($this->sjen[$key]);    $this->sda[$key]=$this->sda[$key].$data;    $this->n[$key]=$n;    return false;   }else{    unset($this->ar[$key],$this->slen[$key],$this->sjen[$key],$this->n[$key]);    $data=$this->sda[$key].$data;    unset($this->sda[$key]);    return $data;   }     }      function code($msg){   $frame = array();     $frame[0] = '81';     $len = strlen($msg);   if($len < 126){    $frame[1] = $len<16?'0'.dechex($len):dechex($len);   }else if($len < 65025){    $s=dechex($len);    $frame[1]='7e'.str_repeat('0',4-strlen($s)).$s;   }else{    $s=dechex($len);    $frame[1]='7f'.str_repeat('0',16-strlen($s)).$s;   }   $frame[2] = $this->ord_hex($msg);     $data = implode('',$frame);     return pack("H*", $data);    }    function ord_hex($data)  {     $msg = '';     $l = strlen($data);     for ($i= 0; $i<$l; $i++) {      $msg .= dechex(ord($data{$i}));     }     return $msg;    }    //用户加入  function send($k,$msg){   parse_str($msg,$g);   $ar=array();   if($g['type']=='add'){    $this->users[$k]['name']=$g['ming'];    $ar['type']='add';    $ar['name']=$g['ming'];    $key='all';   }else{    $ar['nrong']=$g['nr'];    $key=$g['key'];   }   $this->send1($k,$ar,$key);  }    function getusers(){   $ar=array();   foreach($this->users as $k=>$v){    $ar[]=array('code'=>$k,'name'=>$v['name']);   }   return $ar;  }    //$k 发信息人的code $key接受人的 code  function send1($k,$ar,$key='all'){   $ar['code1']=$key;   $ar['code']=$k;   $ar['time']=date('m-d H:i:s');   $str = $this->code(json_encode($ar));   if($key=='all'){    $users=$this->users;    if($ar['type']=='add'){     $ar['type']='madd';     $ar['users']=$this->getusers();     $str1 = $this->code(json_encode($ar));     socket_write($users[$k]['socket'],$str1,strlen($str1));     unset($users[$k]);    }    foreach($users as $v){     socket_write($v['socket'],$str,strlen($str));    }   }else{    socket_write($this->users[$k]['socket'],$str,strlen($str));    socket_write($this->users[$key]['socket'],$str,strlen($str));   }  }    //用户退出  function send2($k){   $this->close($k);   $ar['type']='rmove';   $ar['nrong']=$k;   $this->send1(false,$ar,'all');  }    function e($str){   //$path=dirname(__FILE__).'/log.txt';   $str=$str."/n";   //error_log($str,3,$path);   echo iconv('utf-8','gbk//IGNORE',$str);  } } ?>

client端

<!doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/> <title>HTML5 websocket 网页聊天室 javascript php</title> <style type="text/css"> body,p{margin:0px; padding:0px; font-size:14px; color:#333; font-family:Arial, Helvetica, sans-serif;} #ltian,.rin{width:98%; margin:5px auto;} #ltian{border:1px #ccc solid;overflow-y:auto; overflow-x:hidden; position:relative;} #ct{margin-right:111px; height:100%;overflow-y:auto;overflow-x: hidden;} #us{width:110px; overflow-y:auto; overflow-x:hidden; float:right; border-left:1px #ccc solid; height:100%; background-color:#F1F1F1;} #us p{padding:3px 5px; color:#08C; line-height:20px; height:20px; cursor:pointer; overflow:hidden; white-space:nowrap; text-overflow:ellipsis;} #us p:hover,#us p:active,#us p.ck{background-color:#069; color:#FFF;} #us p.my:hover,#us p.my:active,#us p.my{color:#333;background-color:transparent;} button{float:right; width:80px; height:35px; font-size:18px;} input{width:100%; height:30px; padding:2px; line-height:20px; outline:none; border:solid 1px #CCC;} .rin p{margin-right:160px;} .rin span{float:right; padding:6px 5px 0px 5px; position:relative;} .rin span img{margin:0px 3px; cursor:pointer;} .rin span form{position:absolute; width:25px; height:25px; overflow:hidden; opacity:0; top:5px; right:5px;} .rin span input{width:180px; height:25px; margin-left:-160px; cursor:pointer}  #ct p{padding:5px; line-height:20px;} #ct a{color:#069; cursor:pointer;} #ct span{color:#999; margin-right:10px;} .c2{color:#999;} .c3{background-color:#DBE9EC; padding:5px;} .qp{position:absolute; font-size:12px; color:#666; top:5px; right:130px; text-decoration:none; color:#069;} #ems{position:absolute; z-index:5; display:none; top:0px; left:0px; max-width:230px; background-color:#F1F1F1; border:solid 1px #CCC; padding:5px;} #ems img{width:44px; height:44px; border:solid 1px #FFF; cursor:pointer;} #ems img:hover,#ems img:active{border-color:#A4B7E3;} #ems a{color:#069; border-radius:2px; display:inline-block; margin:2px 5px; padding:1px 8px; text-decoration:none; background-color:#D5DFFD;} #ems a:hover,#ems a:active,#ems a.ck{color:#FFF; background-color:#069;} .tc{text-align:center; margin-top:5px;} </style> </head>  <body> <div id="ltian">  <div id="us" class="jb"></div>  <div id="ct"></div>     <a href="javascript:;" class="qp" onClick="this.parentNode.children[1].innerHTML=''">清屏</a> </div> <div class="rin">     <button id="sd">发送</button>     <span><img src="http://www.yxsss.com/ui/sk/t.png" title="表情" id="imgbq"><img src="http://www.yxsss.com/ui/sk/e.png" title="上传图片"><form><input type="file" title="上传图片" id="upimg"></form></span>     <p><input id="nrong"></p> </div> <div id="ems"><p></p><p class="tc"></p></div> <script> if(typeof(WebSocket)=='undefined'){  alert('你的浏览器不支持 WebSocket ,推荐使用Google Chrome 或者 Mozilla Firefox');  } </script> <script src="http://www.yxsss.com/ui/p/a.js" type="text/javascript"></script> <script> (function(){  var key='all',mkey;  var users={};  var url='ws://127.0.0.1:8000';  var so=false,n=false;  var lus=A.$('us'),lct=A.$('ct');  function st(){   n=prompt('请给自己取一个响亮的名字:');   n=n.substr(0,16);   if(!n){    return ;    }   so=new WebSocket(url);   so.onopen=function(){    if(so.readyState==1){     so.send('type=add&ming='+n);    }   }      so.onclose=function(){    so=false;    lct.appendChild(A.$$('<p class="c2">退出聊天室</p>'));   }      so.onmessage=function(msg){    eval('var da='+msg.data);    var obj=false,c=false;    if(da.type=='add'){     var obj=A.$$('<p>'+da.name+'</p>');     lus.appendChild(obj);     cuser(obj,da.code);     obj=A.$$('<p><span>['+da.time+']</span>欢迎<a>'+da.name+'</a>加入</p>');     c=da.code;    }else if(da.type=='madd'){     mkey=da.code;     da.users.unshift({'code':'all','name':'大家'});     for(var i=0;i<da.users.length;i++){      var obj=A.$$('<p>'+da.users[i].name+'</p>');      lus.appendChild(obj);      if(mkey!=da.users[i].code){       cuser(obj,da.users[i].code);      }else{       obj.className='my';       document.title=da.users[i].name;      }     }     obj=A.$$('<p><span>['+da.time+']</span>欢迎'+da.name+'加入</p>');     users.all.className='ck';    }        if(obj==false){     if(da.type=='rmove'){      var obj=A.$$('<p class="c2"><span>['+da.time+']</span>'+users[da.nrong].innerHTML+'退出聊天室</p>');      lct.appendChild(obj);      users[da.nrong].del();      delete users[da.nrong];     }else{      da.nrong=da.nrong.replace(/{//(/d+)}/g,function(a,b){       return '<img src="sk/'+b+'.gif">';      }).replace(/^data/:image//png;base64/,.{50,}$/i,function(a){       return '<img src="'+a+'">';      });      //da.code 发信息人的code      if(da.code1==mkey){       obj=A.$$('<p class="c3"><span>['+da.time+']</span><a>'+users[da.code].innerHTML+'</a>对我说:'+da.nrong+'</p>');       c=da.code;      }else if(da.code==mkey){       if(da.code1!='all')       obj=A.$$('<p class="c3"><span>['+da.time+']</span>我对<a>'+users[da.code1].innerHTML+'</a>说:'+da.nrong+'</p>');       else       obj=A.$$('<p><span>['+da.time+']</span>我对<a>'+users[da.code1].innerHTML+'</a>说:'+da.nrong+'</p>');       c=da.code1;      }else if(da.code==false){       obj=A.$$('<p><span>['+da.time+']</span>'+da.nrong+'</p>');      }else if(da.code1){       obj=A.$$('<p><span>['+da.time+']</span><a>'+users[da.code].innerHTML+'</a>对'+users[da.code1].innerHTML+'说:'+da.nrong+'</p>');       c=da.code;      }     }    }    if(c){      obj.children[1].onclick=function(){       users[c].onclick();      }     }    lct.appendChild(obj);    lct.scrollTop=Math.max(0,lct.scrollHeight-lct.offsetHeight);   }  }  A.$('sd').onclick=function(){   if(!so){     return st();   }   var da=A.$('nrong').value.trim();   if(da==''){    alert('内容不能为空');    return false;    }   A.$('nrong').value='';   so.send('nr='+esc(da)+'&key='+key);  }  A.$('nrong').onkeydown=function(e){   var e=e||event;   if(e.keyCode==13){    A.$('sd').onclick();   }  }  function esc(da){   da=da.replace(/</g,'<').replace(/>/g,'>').replace(//"/g,'"');   return encodeURIComponent(da);  }  function cuser(t,code){   users[code]=t;   t.onclick=function(){    t.parentNode.children.rcss('ck','');    t.rcss('','ck');    key=code;   }  }  A.$('ltian').style.height=(document.documentElement.clientHeight - 70)+'px';  st();     var bq=A.$('imgbq'),ems=A.$('ems');  var l=80,r=4,c=5,s=0,p=Math.ceil(l/(r*c));  var pt='sk/';  bq.onclick=function(e){   var e=e||event;   if(!so){     return st();   }   ems.style.display='block';   document.onclick=function(){    gb();    }   ct();   try{e.stopPropagation();}catch(o){}  }    for(var i=0;i<p;i++){   var a=A.$$('<a href="javascript:;">'+(i+1)+'</a>');   ems.children[1].appendChild(a);   ef(a,i);  }  ems.children[1].children[0].className='ck';    function ct(){   var wz=bq.weiz();   with(ems.style){    top=wz.y-242+'px';    left=wz.x+bq.offsetWidth-235+'px';   }  }     function ef(t,i){   t.onclick=function(e){    var e=e||event;    s=i*r*c;    ems.children[0].innerHTML='';    hh();    this.parentNode.children.rcss('ck','');    this.rcss('','ck');    try{e.stopPropagation();}catch(o){}   }  }    function hh(){   var z=Math.min(l,s+r*c);   for(var i=s;i<z;i++){    var a=A.$$('<img src="'+pt+i+'.gif">');    hh1(a,i);    ems.children[0].appendChild(a);   }   ct();  }    function hh1(t,i){   t.onclick=function(e){    var e=e||event;    A.$('nrong').value+='{//'+i+'}';    if(!e.ctrlKey){     gb();    }    try{e.stopPropagation();}catch(o){}   }  }    function gb(){   ems.style.display='';   A.$('nrong').focus();   document.onclick='';  }  hh();  A.on(window,'resize',function(){   A.$('ltian').style.height=(document.documentElement.clientHeight - 70)+'px';   ct();  })    var fimg=A.$('upimg');  var img=new Image();  var dw=400,dh=300;  A.on(fimg,'change',function(ev){   if(!so){    st();    return false;   }   if(key=='all'){    alert('由于资源限制 发图只能私聊');    return false;    }   var f=ev.target.files[0];   if(f.type.match('image.*')){    var r = new FileReader();    r.onload = function(e){     img.setAttribute('src',e.target.result);       };    r.readAsDataURL(f);   }  });  img.onload=function(){   ih=img.height,iw=img.width;   if(iw/ih > dw/dh && iw > dw){    ih=ih/iw*dw;    iw=dw;   }else if(ih > dh){    iw=iw/ih*dh;    ih=dh;   }   var rc = A.$$('canvas');   var ct = rc.getContext('2d');   rc.width=iw;   rc.height=ih;   ct.drawImage(img,0,0,iw,ih);   var da=rc.toDataURL();   so.send('nr='+esc(da)+'&key='+key);  }   })(); </script> </body> </html>
原文  http://my.oschina.net/ososchina/blog/644834?fromerr=8zwTTQ4L
正文到此结束
Loading...