2011年12月4日 星期日

[PHP] PHP 的多人聊天 Socket 伺服器


恩~為了幫我的 prueMVC 的作測試!
尋找多種不同 伺服器端是很重要的!
這次的目標是 Socket 伺服器

恩~不過這個有很多的問題~

第一個
遇上的就是 PHP 不支援的部分~
首先看 php 的 info
如果
Sockets Support enabled
這樣就 OK了~如果沒有
那請到 php.ini
去修改 extension=php_sockets.dll 把這個的# 用掉
這樣就可以成功的寫出 PHP的程式了

第二個
就是執行的問題
根據 因為是伺服器的關係~
不太建議大家使用 網頁去開~
特別是沒改限制時間的
比較建議使用 命令列的方式去開

像我在這會遇上一個問題就是
php_exif.dll 會說有問題 (或是 沒找到)
基本上~你只要修改一下順序就可以了
把 extension=php_exif.dll 移到
extension=php_mbstring.dll 的後面
重開一次 伺服器~
就可以正常執行了

3. 程式的部分!
因為我PHP只會一點點~
所以這邊 99% 都是照抄的
這個網站教PHP Socket 的部分我覺得蠻詳細的
一開始我都是照這個去建構~
基本上 只要照作範例是跑得起來的
不過會遇上防火牆的問題

4.
如果遇上防火牆的問題
這邊就是需要你把防火牆打開

怎樣判斷是否式防火牆的問題!
如果你 網址 PORT都設定正確~
伺服器也跑起來了
但是 你使用 CLIENT 沒有出現任何的回應
在你伺服器端的畫面上
那應該是防火牆的問題~

記得按照你寫的PROT 和 TCP UCP去開~
而且要開兩個~一個傳出 一個傳入~

5.
一個多人架構的 PHP 伺服器
PHP+Flex实现多人聊天(berrywu寫的)
他的這個 伺服器只能在 DEBUG的情況下使用
當然是以WEB的FLASH來說~
用AIR也是可以正常執行~不過要雙開有點麻煩~
然後這個在ADOBE中有一個範例 是解決安全性問題的SOCKET的策略
不過不記得網址了

我修改 (berrywu的) 伺服器 只要在他的
function readFromSocket( $clientId ){
中加入一段就可以解決
基本加完的結果如下


function readFromSocket( $clientId ){
   // start with empty string
   $data   = "";
   // 新增安全規則的部分!
   $buf2 = socket_read( $this->clientFD[$clientId], $this->readBufferSize );
   if(strpos($buf2,'policy-file-request')){
    $msg ="<cross-domain-policy><allow-access-from domain='*' to-ports='*' /></cross-domain-policy>";
socket_write($this->clientFD[$clientId], $msg."\0", strlen($msg."\0"));
}
   // read data from socket
   while( $buf = socket_read( $this->clientFD[$clientId], $this->readBufferSize ) ){
    $data .= $buf;

    $endString = substr( $buf, - strlen( $this->readEndCharacter ) );
    if( $endString == $this->readEndCharacter )
     break;
    if( $buf == NULL )
     break;
   }
   if( $buf === false ) $this->sendDebugMessage( "Could not read from client2 ".$clientId." ( ".$this->getLastSocketError( $this->clientFD[$clientId] )." )." );
   return $data;
}


好了到此算是完全結束~
當然你也可以使用 (berrywu的) 範例做一下試驗!
不過我等等來看看 zend 有沒有這方面的東東~
忘了一開始使就去查 orz

2011年12月3日 星期六

[AS3] zendAmf + PureMVC + flex 4.5的聊天室

先來來 看看效果巴
聊天室的原碼


這個放在空間上的是 單機板的!
所以不是即時的可以多人聊天版
我的改法是錯誤示範把 Proxy 中的 Delegate 的通通停掉
然後直接傳出事件
其實應該寫另外一個 Delegate 來作單機的
而不是像現在的方式~
直接寫在 Proxy 中!

然後我來講一下這個裡面我遇上的一些問題!

1. 中文問題
很久以前FLEX MX版的textInput有問題~
後來修正了~現在 S版的 textInput
也是一樣有中文的問題~
所以我打算找 MX的INPUT和S版的對照改
看看能不能解決~
但是我覺得這兩個 相差太多 我照改改出一些問題~
所以最後又改回 MX版的 不過這樣做有不少的問題~
MX和S版的 參數有差~本身對 RICHTEXT有差~~
基本上~我遇過 input的中文輸入問題 大概就下面這幾個
 a . 因為 HTML上的鑲入語法沒設成 windows 或是  transparent
 b. input有沒有 focus~沒有焦點不能換成中文輸入
 c. 沒有設 IME.enabled = true;
 d. 組件使用 s版的
這邊我是使用 MX版的組件~
配合HTML設成 windows  來解決這個問題

2.文字框置底問題
以前都是使用MX版的組件~
設置置底很簡單~都使用verticalScrollPosition
現在使用 S版的沒有這個屬性變得很麻煩
而且 S版的 textArea的組件 要設 verticalScrollPosition 和 verticalCenter 屬性~
使用 appendText 區增加文字~
會自動置底~
我是過直接將 文字給 組件的方式
但是這個方式不會置底喔

後來置換 SKIN的時候! 又遇上了置底的問題~
建議是換了後 過一小段時間
在執行appendText 就會置底了

3. 文字格式的問題
這個範例中 可以簡單的設 5種不同的屬性
大小 顏色 粗細 斜體 底線
恩~因為MX沒有 textFlow這個屬性..
最後看 官方文件 才知道怎樣做
我選擇的是 Creating a TextFlow object in ActionScript 的方法~
如果沒看到這個 我想我的做法應該還是回去老方式
使用 htmlText的方法去做~

4.換SKIN的方法~
本來我是想使用CSS的方式~或是換MC元件的方式
後來想到之前 Ticore 大大教的方法 (之前我要換官方的Alert的SKIN 不會換~)
所以就使用 MXML SKIN的方式去做~
基本上我設計不太行 所以我就基本的改了幾個屬性看看效果
像是 button 的部分我只有改框線的顏色
textArea 的部分 我就加了背景
如果仔細研究一下應該是可以加更多的地方
還有支援這樣換的 只有S版的 不過可能是因為介面的關係~
說不定仔細的GOOGLE一下 說不定可以找到兼容MX版的方式
我覺得兩個應該不會相差太大!

5. 表情符號的部分~雖然我有看到範例!
但是我沒辦法傳回 主機端處理在使用!
最重要原因是 一次傳輸裡面可以有多個表情符號!
傳到伺服器是沒問題!
但是伺服器的記錄和在使用的部分就麻煩了
最主要是我不想在建一個表然後JOIN
但是這邊我還沒想到好的方法~
基本上應該是存成文字檔 存進資料庫
然後 讀出來在解析成 ARRAY然後傳回去的方式
傳回的文字串和圖片位置的問題
而且這一塊 我是想要獨立出來 自己成一個 VIEW的方式去處理!

6. PHP 處理數據的問題~
本來想使用remote的方式可以減低PHP的連線數的
不過看起來好像是沒啥幫助!
不過也可能是SQL寫的太爛的關係
我在本機端測 1秒鐘 更新一次資料
兩個人同時連的情況~我的電腦 CPU 就會吃到100%...
就是因為這個原因~
讓我不敢放在主機上這種自動更新的即時機置


2011年11月29日 星期二

[AS3] 寶石方塊的實作

寶石方塊~
一個算是基本中的基本的東西~
我是拿來練習Vector的部分~
用這個練習 Vector得到了很多的部分~
例如 一些檢驗的部分~ 非常的嚴謹~
Vector 如果超出所引範圍會報錯~
但是 array 不會~
Vector 就算是有限定了長度 如果不是設定固定~
也是可以利用 push去增加的
Vetor的排序較快! Array慢很多~

基本上這是一個很堅單的東東~
所以我就沒太多講解了~
如果有高手願意提供更好的寫法我也願意學習~
話說 Vector版的寶石方塊~
比我用 array的還慢~
理論上應該可以寫的比 array版的快~不過我是寫不出來了!
下面是我的原始碼!

vo 的部分 是地板的類別 目的是讓 vector可以使用


package vo
{
import flash.display.MovieClip;

public class jwFloor extends Object
{
public var mc:MovieClip;
public var type:int = 1;

public function jwFloor(
_mc:MovieClip,
_type:int = 1
)
{
super();
mc = _mc;
type = _type;
}
}
}



gameEvent 這個是放這遊戲傳出的動作 基本上有兩個 消去地板時傳出的 和結束遊戲傳出的

這個是如果整個遊戲中 沒有任何兩個方塊互換 可以消去時會傳出這個!

package gameEvent
{
import flash.events.Event;

public class gameOverEvent extends Event
{
public static const GAME_OVER:String = 'gameOver';

public function gameOverEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
}


這個是每次消去方塊時會傳出

package gameEvent
{
import flash.events.Event;

public class HitCountEvent extends Event
{
public static const HIT_COUNT_EVENT:String = 'gameHitCountEvent';

public var hit:int  = 0;        //總共有幾組方塊 在這一次操作中消去
public var countFloorMax:int = 0;// 這一次最大消去數量
public var totalFloor:int = 0; // 總共消去的地板數量

public function HitCountEvent(type:String,
hit:int = 0,
countFloorMax:int = 0,
totalFloor:int = 0,
bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
this.hit = hit;
this.countFloorMax = countFloorMax;
this.totalFloor = totalFloor;
}
}
}



下面這個是 整個遊戲的主體!

package
{
import caurina.transitions.Tweener;

import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;

import gameEvent.HitCountEvent;
import gameEvent.gameOverEvent;

import vo.jwFloor;

public class bejeweledGame extends Sprite
{
private var horNum:int = 10;
private var verNum:int = 10;
private var jwMap:Vector.<Vector.<jwFloor>>;
private var floorClasVec:Vector.<Class>;
private var selectMC:Sprite;
private var moveFlag:Boolean = false;
private var countHit:int = 0;
private var countFloor:int = 0;
private var totalFloor:int = 0;
public var hitPoint1:Point = new Point(-1,-1);
public var hitPoint2:Point = new Point(-1,-1);
//
private const moveFloorTime:Number = 0.3; // 兩塊地板移動使用的時間!
private const actionDelay:Number   = 0.1; // 動作結束後的延遲時間!
private const clearFloorTime:Number = 0.5;// 清理地板時間


public function bejeweledGame(_fcv:Vector.<Class>,
 _horNum:int=10,
 _verNum:int=10)
{
super();
this.addEventListener(Event.ADDED_TO_STAGE, onInitFun);
floorClasVec = _fcv;
horNum = _horNum;
verNum = _verNum;
countHit = 0;
countFloor = 0;
totalFloor = 0;
}

private function onInitFun(e:Event):void
{
this.removeEventListener(Event.ADDED_TO_STAGE, onInitFun);
jwMap = new Vector.<Vector.<jwFloor>>(verNum);
for(var i:int=0;i<verNum;i++)
{
var tempV:Vector.<jwFloor> = new Vector.<jwFloor>(horNum);
jwMap[i]= tempV;
for(var j:int=0;j<horNum;j++)
{
do{
var tempRn:int = Math.random()*floorClasVec.length;
var cla:Class  = floorClasVec[tempRn];
jwMap[i][j] = new jwFloor(new cla() as MovieClip, tempRn);
}while(hk(j,i)>2||vk(j,i)>2)
this.addChild(jwMap[i][j].mc);
jwMap[i][j].mc.x = j*jwMap[i][j].mc.width;
jwMap[i][j].mc.y = i*jwMap[i][j].mc.height;
jwMap[i][j].mc.dx = j;
jwMap[i][j].mc.dy = i;
}
}
selectMC = new Sprite();
selectMC.graphics.lineStyle(3,0xff0000,1);
selectMC.graphics.drawRect(0,0,jwMap[0][0].mc.width, jwMap[0][0].mc.height);
selectMC.mouseEnabled  = false;
selectMC.mouseChildren = false;
selectMC.visible = false;
this.addChild(selectMC);
this.addEventListener(MouseEvent.CLICK,  onClickFun);
}
// 點擊檢測
private function onClickFun(e:MouseEvent):void
{
var mc:MovieClip = e.target as MovieClip;
if(moveFlag) return;
if(!mc)return;
if(mc.dx == undefined){
mc = mc.parent as MovieClip;
}
if(mc.dx == undefined) return;
var x:int = mc.dx;
var y:int = mc.dy;
var sx:int = int(selectMC.x / mc.width);
var sy:int = int(selectMC.y / mc.height);
if((((x==sx+1||x==sx-1)&&y==sy)||
   ((y==sy+1||y==sy-1)&&x==sx)))
{
switchFloor( x, y, sx, sy);
if(hk(x,y)>2 ||
  vk(x,y)>2 ||
  hk(sx,sy)>2 ||
  vk(sx,sy)>2)
{
selectMC.x = -10;
selectMC.y = -10;
selectMC.visible = false;
Tweener.addTween(selectMC, {alpha:1,
time:moveFloorTime+actionDelay,
onComplete:SwitchMoveEndFun,
onCompleteParams:[sy>y?sy:y]});
return;
}else{
switchFloor( x, y, sx, sy);
}
}
selectMC.x = mc.x;
selectMC.y = mc.y;
selectMC.visible = true;
if(!getHit()) this.dispatchEvent(new gameOverEvent(gameOverEvent.GAME_OVER));
return;
}
//
private function SwitchMoveEndFun(maxY:int):void
{
clearFloor(maxY);
}
//
private function clearFloor(MaxY:int):void
{
var typeNum:int = -1;
var cont:int = 1;
var flagF:Boolean = false;
for(var i:int=0;i<this.verNum;i++)
{
typeNum = -1;
cont = 1;
for(var j:int=0;j<horNum;j++)
{
if(jwMap[i][j].type == typeNum)
{
cont++;
}else{
if(cont >= 3) flagF = clearHorFloor(j, i, cont);
cont = 1;
typeNum = jwMap[i][j].type;
}
}
if(cont >= 3) flagF = clearHorFloor(j, i , cont);
}
if(flagF){
Tweener.addTween(selectMC, {alpha:1, time:this.clearFloorTime+this.actionDelay, onComplete:HorMoveEndFun});
}else{
clearFloor2();
}
}
// 橫向清除動畫結束!
private function HorMoveEndFun():void
{
clearFloor2();
}
//
private function clearFloor2():void
{
var typeNum:int = -1;
var cont:int = 1;
var flagF:Boolean = false;
for(var j:int = 0;j<horNum;j++)
{
typeNum = -1;
cont = 1;
for(var i:int=0;i<verNum;i++)
{
if(jwMap[i][j])
{
if(jwMap[i][j].type == typeNum)
{
cont++;
}else{
if(cont >= 3) flagF = clearHVerFloor(j, i , cont);
cont = 1;
typeNum = jwMap[i][j].type;
}
}else{
if(cont >= 3) flagF = clearHVerFloor( j, i , cont);
cont = 1;
typeNum = -1;
}
}
if(cont >= 3) flagF = clearHVerFloor(j, i, cont);
}
//
if(flagF){
Tweener.addTween(selectMC, {alpha:1, time:this.clearFloorTime+this.actionDelay, onComplete:VerMoveEndFun});
}else{
clearFloor3();
}
}
//
private function VerMoveEndFun():void
{
clearFloor3();
}
//
private function clearFloor3():void
{
var typeNum:int = -1;
var cont:int = 1;
var flagF:Boolean = false;
// 垂直落下
var tempFloor:Vector.<jwFloor>;
for(var j:int=0;j<this.horNum;j++)
{
tempFloor = new Vector.<jwFloor>();
for(var i:int=0;i<this.verNum;i++) if(jwMap[i][j]) tempFloor.push(jwMap[i][j]);
for(i=verNum-1;i>=0;i--)
{
if(tempFloor.length>0){
jwMap[i][j] = tempFloor.pop();
}else{
var tempRn:int = Math.random()*floorClasVec.length;
var cla:Class  = floorClasVec[tempRn];
jwMap[i][j] = new jwFloor(new cla() as MovieClip, tempRn);
this.addChild(jwMap[i][j].mc);
jwMap[i][j].mc.x = j*jwMap[i][j].mc.width;
jwMap[i][j].mc.y = 0 - (jwMap[i][j].mc.height*(this.verNum-i));
flagF = true;
}
fixFloor( j, i);
}
}
if(!flagF){
trace("連擊! " + countHit + " Hit. 一共消除: " +  totalFloor + " 消除最大長度: " + countFloor);
moveFlag   = false;
this.dispatchEvent(
new HitCountEvent(HitCountEvent.HIT_COUNT_EVENT,
countHit,countFloor,totalFloor));
countHit   = 0;
totalFloor = 0;
countFloor = 0;
}else{
Tweener.addTween(selectMC,
{x:selectMC.x,
time:this.moveFloorTime+this.actionDelay,
onComplete:clearFloor,
onCompleteParams:[verNum]});
}
}
// 橫向清除 FLOOR
private function clearHorFloor(  x:int, y:int, long:int):Boolean
{
for(var i:int=1;i<=long;i++)
{
if(jwMap[y][(x-i)] || jwMap[y][(x-i)] != null)
{
//this.removeChild(jwMap[y][(x-i)].mc);
Tweener.addTween(jwMap[y][(x-i)].mc, {alpha:0, time:clearFloorTime});
jwMap[y][(x-i)] = null;
moveFlag = true;
}
}
countHit++;
totalFloor+=long;
if(long > countFloor) countFloor = long;
return true;
}
// 垂直清除FLOOR
private function clearHVerFloor( x:int, y:int, long:int):Boolean
{
for(var i:int=1;i<=long;i++)
{
if(jwMap[(y-i)][(x)] || jwMap[y-i][(x)] != null)
{
//this.removeChild(jwMap[(y-i)][(x)].mc);
Tweener.addTween(jwMap[(y-i)][(x)].mc, {alpha:0, time:clearFloorTime});
jwMap[(y-i)][(x)] = null;
moveFlag = true;
}
}
countHit++;
totalFloor+=long;
if(long > countFloor) countFloor = long;
return true;
}
// 兩個互換!
private function switchFloor( x1:int, y1:int,
 x2:int, y2:int, testMode:Boolean = false):void
{
var tempFloor:jwFloor = jwMap[y1][x1];
jwMap[y1][x1] = jwMap[y2][x2];
jwMap[y2][x2] = tempFloor;
fixFloor(x1,y1, testMode);
fixFloor(x2,y2, testMode);
}
//
private function fixFloor(x:int,y:int, testMode:Boolean = false):void
{
if(jwMap[y][x].mc){
jwMap[y][x].mc.dx = x;
jwMap[y][x].mc.dy = y;
if(testMode){
jwMap[(y)][(x)].mc.x = x*jwMap[y][x].mc.width;
jwMap[(y)][(x)].mc.y = y*jwMap[y][x].mc.height;
}else{
Tweener.addTween(jwMap[(y)][(x)].mc, {
x:x*jwMap[y][x].mc.width,
y:y*jwMap[y][x].mc.height,
time:moveFloorTime});
}
// jwMap[y][x].mc.x  = x*jwMap[y][x].mc.width;
// jwMap[y][x].mc.y  = y*jwMap[y][x].mc.height;
}
}
// 垂直檢查
private function hk(x:int, y:int):int
{
var nowCoun:int = 1;
var tempY:int = y;
var type:int = jwMap[y][x].type;
while(twoPointCheck2(type, x, tempY-1))
{
tempY--;
nowCoun++;
}
tempY = y;
while(twoPointCheck2(type, x, tempY+1))
{
tempY++;
nowCoun++;
}
return nowCoun;
}
// 水平檢查
private function vk(x:int, y:int):int
{
var nowCoun:int = 1;
var tempX:int = x;
var type:int = jwMap[y][x].type;
while(twoPointCheck2(type,tempX-1,y))
{
tempX--;
nowCoun++;
}
tempX = x;
while(twoPointCheck2(type, tempX+1,y))
{
tempX++;
nowCoun++;
}
return nowCoun;
}
// 兩個點的 type 是否一樣!!
private function twoPointCheck2(typeNum:int, x:int, y:int):Boolean
{
if(x < 0 || y < 0) return false;
if(x >= horNum || y >= verNum) return false;
if(jwMap[y] == null)    return false;
if(jwMap[y][x] == null) return false;
return typeNum == jwMap[y][x].type;
}
// 提示
public function getHit():Boolean
{
for(var i:int=0;i<verNum-1;i++)
{
for(var j:int= 0;j<horNum-1;j++)
{
switchFloor(j, i , j, i+1,true);
if( hk(j,i)>2 ||
vk(j,i)>2 ||
hk(j,i+1)>2 ||
vk(j,i+1)>2)
{
trace("(" +(j+1) + "," +(i+1)+")->" + "("+(j+1)+","+(i+2)+")");
switchFloor(j, i , j, i+1,true);
hitPoint1 = new Point(j+1,i+1);
hitPoint2 = new Point(j+1,i+2);
return true;
break;

}
switchFloor(j, i , j, i+1,true);
//
switchFloor(j, i , j+1, i,true);
if( hk(j,i)>2 ||
vk(j,i)>2 ||
hk(j+1,i)>2 ||
vk(j+1,i)>2)
{
trace("(" +(j+1) + "," +(i+1)+")->" + "("+(j+2)+","+(i+1)+")");
switchFloor(j, i , j+1, i,true);
hitPoint1 = new Point(j+1,i+1);
hitPoint2 = new Point(j+2,i+1);
return true;
break;
}
switchFloor(j, i , j+1, i,true);
}
}
hitPoint1 = new Point(-1,-1);
hitPoint2 = new Point(-1,-1);
return false;
}
// 清除全部!
public function clearAll():void
{
for(var i:int=0;i<verNum-1;i++)
{
for(var j:int= 0;j<horNum-1;j++)
{
if(jwMap[i][j])
{
this.addChild(jwMap[i][j].mc);
}
jwMap[i][j] = null;
}
jwMap[i] = null;
}
var len:int = floorClasVec.length;
for(i=0;i<len;i++)
{
floorClasVec.pop();
}
floorClasVec = null;
this.removeChild(selectMC);
selectMC = null;
countHit = 0;
this.removeEventListener(MouseEvent.CLICK,  onClickFun);
}

}
}

//
這整個是我的寶石方塊的程式碼~
有用到 tweener的類別~
其中一開始傳入的 _fcv 是存放本次有多少種不同的地板!
//
只有主體的SWF
主體的程式碼(我沒打包我的函數庫!)
有開始結束的版本!

2011年11月28日 星期一

[AS3] 一個超簡單的跑螺旋的方式

很久很久以前~
我自己寫的第一個特效就是跑螺旋!
超興奮的~
看到後來被人改成 瓢蟲 的時候~
我也是超興奮的~~
 不過現在看看~
我好像從那個時候起就沒成長過~~
不過來介紹一下~


package
{
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.Event;

public class bejeweledAS extends Sprite
{
private var box:MovieClip; // 用來跑的物體!
private var ra:Number;     // 角度
private var r:int = 100;   // 長度
//
public function bejeweledAS()
{
box = new box07(); //我在引出的 MovieClip 的類別~
box.width  = 10;
box.height = 10;
this.addChild(box);
ra = 0;
r  = 10;
this.addEventListener(Event.ENTER_FRAME, onEnterFrameFun);
}
//
private function onEnterFrameFun(e:Event):void
{
box.x = r * Math.cos(ra*Math.PI/180) + 100;
box.y = r * Math.sin(ra*Math.PI/180) + 100;
ra += 10;
r  += 0.1;
}
}
}
一個最簡單的跑螺旋的方式~
改變一下角度是遞增遞減~
可以改變旋轉的方向~
改變一下~長度
會是轉入 或 是 轉出

===============================
剛剛學到一招~
使用 point的方法去處理~
據了解也是可以做到螺旋狀 !
Point.polar(r, ra*Math.PI/180);

詳細的可以看這個
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/geom/Point.html

然後我對下面兩個Function 作 unittest


public function test1():void
{
var r:int = 100;
for(var ra:int=0;ra<360;ra++)
{
r * Math.cos(ra*Math.PI/180) + 100;
r * Math.sin(ra*Math.PI/180) + 100;
}
}
public function test2():void
{
var r:int = 100;
var pi:Point;
for(var ra:int=0;ra<360;ra++)
{
Point.polar(r, ra*Math.PI/180);
}
}
結果讓我意外!

Math函數執行100次 消耗的時間為15毫秒 消耗 16384 記憶體 平均每次時間為 0.15毫秒
Point函數執行100次 消耗的時間為40毫秒 消耗 69632 記憶體 平均每次時間為 0.4毫秒
好像中間進行 GC 了

Math函數執行1000次 消耗的時間為163毫秒 消耗 16384 記憶體 平均每次時間為 0.163毫秒
Point函數執行1000次 消耗的時間為351毫秒 消耗 -40960 記憶體 平均每次時間為 0.351毫秒

基本上~使用三角函數計算的結果比用 Point.polar 快一截!
大家就看自己喜歡用啥就用啥瞜!

2011年11月27日 星期日

[AS3]ZendAMF + FLASH Builder 4.5 的測試+心得


恩~學一些新東西~
因為看某論壇說到 AMFPHP更新速度慢了!
裡面核心的人物 去開發 zendAMF了~
而且zend有ADOBE的幫助!
還有速度方面會比AMFPHP快!
所以我就想試看看 zendAMF的部分!
下面是我這次測試的整個的部分和遇上的問題!

先講一下我的系統
OS  WIN7
Apache 2.2.8
PHP 5.2.6
Zend AMF 1.11.11 (我本篇測試是使用傳說中的光棍板!)
FLASH Builder 4.5 (是使用官方下載的 4.5 的原生SDK )
基本上~應該相關的就這幾個!

1. 先拿到檔案 
zendAMF
http://framework.zend.com/download/amf

Appser(我是灌AppServ 2.5.10)
http://www.appservnetwork.com/?appserv

FB
http://www.adobe.com/downloads/

這邊我測過SDK 4.5.1 官網直接下載板 OK
還有置換過 AIR為 3.1版本的 4.5.1 都能順利執行本次測試!
不過為了通用起見~就用原本 4.5.0的SDK作講解

2. 安裝 zendAMF
步驟很簡單~解壓縮到你的 wwwRoot下~
以我的來說是在
C:\AppServ\www
以用光棍板來說解壓後是叫
ZendAMF-1.11.11

C:\AppServ\www\ZendAMF-1.11.11
我是覺得他的名稱太長所以我改成 zendAmf
然後在你的 zendAmf下建立一個PHP
getway.php (是參考 http://blog.corausir.org/programing/ausir-823 寫出來的 )


<?php
ini_set("display_errors", "On");
define ('P_S', PATH_SEPARATOR); //設定P_S是分隔符號;
set_include_path('.' .P_S .'library' .P_S . get_include_path()); // 設定include路徑
require_once 'library/Zend/Amf/Server.php'; //引入 AMF SERVER 類別
$server = new Zend_Amf_Server(); //建立 zendAmf的實體!
$server->addDirectory(dirname(__FILE__).'/services/'); //將整個資料夾都納入 SERVER 內
$result = $server->handle();
echo $result;
?>

先測試一下getway.php 是不是只輸出
Zend Amf Endpoint
如果是的話~那就是正確安裝了~
======如果有錯請看==========
如果不是就 看錯誤除錯吧~
話說我一開始getway.php 是沒有 define 和 set_include_path會報一堆錯~
然後我是傻傻的一個一個PHP去除錯~
基本上都是 require_once 找不到檔案!
後來我除到手痠~就COPY了一份 zend的到zendAmf的目錄下!
可以正常執行 但這個方法太笨! 
後來查一查~發現這個寫法~蠻不錯的 建議大家一定要用 set_include_path
其實整個zendAmf只有用到 library下的東西~
其他的我稍微看過了一下是測試和DOC基本上還是有幫助的!
所以我建議也是裝了吧!


3. 當然是 helloworld
先來 AS 版的!
先用 fb建立一個 as project (因為這個比簡單~設定的也少! 而且這個OK 基本上FLEX版的才會OK)

package
{
import flash.display.Sprite;
import flash.events.NetStatusEvent;
import flash.net.NetConnection;
import flash.net.ObjectEncoding;
import flash.net.Responder;

public class testZendAmfAS extends Sprite
{
public function testZendAmfAS()
{
var nc:NetConnection = new NetConnection();
NetConnection.defaultObjectEncoding = ObjectEncoding.AMF3; //設定傳輸的方式!
nc.connect('http://localhost/zendAmf/gateway.php'); //你的getway.php的路徑! 
var res:Responder = new Responder(onResultFun, onError);
nc.call("helloWorld.sayHelloWorld",res); 
   // 使用 Remote 方式連結 helloWorld 呼叫下面的 sayHelloWorld函數 傳回給 res   
}
private function onResultFun(e:Object):void 
{
trace(e + " Result!");
}
private function onError(err:Object):void 
{
for (var i:String in err) { 
trace(err[i] + ' ' + i + ' Error'); 
}
}
}
}

然後是 PHP的部分~
剛剛還記得
$server->addDirectory(dirname(__FILE__).'/services/');
C:\AppServ\www\zendAmf\services
下的PHP都會被引用近ZENDAMF裡可以供呼叫!
這樣就像是我用慣的AMFPHP的方式! 


基本上我拿了一些簡單的AMFPHP我寫的services 是都可以直接套用
不過拿一個最近案子的services 因為比較複雜~雖然沒錯! 但是整個不能運行!
現在就來一個簡單的 PHP



<?php
class helloWorld {
public  function sayHelloWorld(){
return "zendAmf is Running! Say Hello World!";
}
}
?>
這個PHP是放在  C:\AppServ\www\zendAmf\services 下喔!
雖然有看到說可以放子目錄的方式! 
但是我試過都不成功!
有心的人可以自己試看看!

基本上~
應該會看到
zendAmf is Running! Say Hello World!
的output在輸出面板!
到此為止你的 FLASH 和 zendAmf 已經正常得運行和聯動了!

接下來來一些比較高端的部分~
使用 flex的MXML去聯結 zendAmf的部分!
PHP的部分是一樣的
所以我就只講 FLEX的部分
一開始建一個 flex project
記得要按 next去設
web root 和 root url
以我的來說
web root : C:\AppServ\www
root url  : http://localhost/


然後是寫 services-config.xml 放在src下喔!
這個很重要!
要注意的點有幾個!


<?xml version="1.0" encoding="UTF-8"?>
<services-config>
<services>
<service id="sabreamf-flashremoting-service" class="flex.messaging.services.RemotingService"
messageTypes="flex.messaging.messages.RemotingMessage">
<destination id="zendAmf">
<channels>
<channel ref="my-amfphp" />
</channels>
<properties>
<source>*</source>
</properties>
</destination>
</service>
</services>
<channels>
<channel-definition id="my-amfphp" class="mx.messaging.channels.AMFChannel">
<endpoint uri="http://localhost/zendAmf/gateway.php" class="flex.messaging.endpoints.AMFEndpoint"  ></endpoint>
</channel-definition>
</channels>
</services-config>


endpoint uri="http://localhost/zendAmf/gateway.php"
這個很重要! 不要設錯了!
destination id="zendAmf" 這個也很重要~後面設定MXML會用到~
剩下的請自己去查巴!
對了~記得對你的 project按右鍵!
properties->flex compiler
記的多一行
-services "services-config.xml"
這樣才算設定完成!


然後是重頭戲! MXML的部分!



<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
  xmlns:s="library://ns.adobe.com/flex/spark"
  xmlns:mx="library://ns.adobe.com/flex/mx"
  creationComplete="init();"
  minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.rpc.events.InvokeEvent;
import mx.rpc.events.ResultEvent;

private function init():void{
amf.addEventListener(FaultEvent.FAULT,   onFaultFun); //如果傳回錯誤的監聽
amf.addEventListener(InvokeEvent.INVOKE, onInvokeFun);//如果傳出指令的監聽 理論上可以不設! 不過要了解所有傳出的動作可以從這裡做!
amf.addEventListener(ResultEvent.RESULT, onResultFun);//除理傳回值的部分! 雖然這三個都可以透過 MXML去設!
amf.sayHelloWorld(); //這個就是呼叫 HelloWorld函數的部分!
}
private function onFaultFun(e:FaultEvent):void
{
trace(e.toString() + " 1!!!");
}
//
private function onInvokeFun(e:InvokeEvent):void
{
trace(e.toString());
//trace(e.message.toString() + " ");
}
//
private function onResultFun(e:ResultEvent):void
{
trace(e.result + "End Result.");
// trace(e.headers + " headers");
// trace(e.message + " message");
// trace(e.messageId + " messages !");
// trace(e.statusCode + " statusCode");
// trace(e.target + " tager" );
// trace(e.token.responders + " token.responders ");
// trace(e.type);
}
]]>
</fx:Script>
<fx:Declarations>
<s:RemoteObject id="amf"  destination="zendAmf"  source="helloWorld" />
</fx:Declarations>
<s:layout>
<s:VerticalLayout horizontalAlign="center" verticalAlign="middle" />
</s:layout>
</s:Application>

<s:RemoteObject id="amf"  destination="zendAmf"  source="helloWorld" /> 
是設定你連結的方式!
id="amf" 是指你這個連線的名稱 後面用來呼叫函數 或是 監聽用的
destination="zendAmf" 就是你設定的  services-config.xml裡的 destination 的設定~
記的填一樣的~不然會報錯!
source 就是你servers的PHP的類別名稱


好了自己測試巴!
基本上你會先看到 InvokeEvent 代標有傳出呼叫了!
然後會有 onResultFun 近來!


到此兩種方式都介紹了!
個人是喜歡用 AS 的方式~
因為統一! 而且FLEX也可以用喔!


4.進接的應用VO的使用!
恩~依照 http://blog.corausir.org/programing/ausir-855的介紹!
基本上按照他的作都OK 
只有一步!在新的版本上~會報錯!
這個錯我找了很久~很久~
後來發現!不用設定

$server->setClassMap
$server->setClass
就可以直接對映!
這應該是新版本的威能!
所以我進行了一些測試!
基本上~只要在VO端加上
[RemoteClass(alias="PHP的類別")]
就可以直接 強制轉型!
var cv2:VO2 = e.result as VO2;
假設試不能轉的類型
會變成 null
假設兩個 AS的VO都設同一個 php class
只有最後設(讀到的)
可以強制轉型!
前面的就算有寫 RemoteClass
也會變能 null

好了整個zendAmf的部分 測試完成!
謝謝觀看!
順便記錄一下! 
基本上如個這個有下一篇應該試作和 amfphp 的一些測試評比
說一下心得
不過就一些簡單的傳輸來說~
我是看不出差別!
說真的這個比 amfphp 難設定!
目前我也沒發現簡易的後台~
像AMFPHP有一個試調和測試的介面!
如果有的話高手介紹一下!~
不然只好自己生了~~

不過兩個在轉換上應該問題不大!
目前只有一個超複雜破 三千行又include一堆東東的server跑不起來~
其他簡單的都是 OK的!