開發基礎:SFSObject 與 SFSArray

» SFSObject 與SFSArray

SmartFoxServer 2X 採用了兩個基本的類別,SFSObject  與SFSArray,這兩個類別是伺服端與客戶端之間操作與傳送資料的中心。它們是跨所有語言的API(包含伺服端API)。這讓移植代碼到任何平台和應用程序的每個面變得非常容易。

SFSObject 與SFSArray 代表著平台中立的高階物件,將客戶端與伺服端間的資料傳輸抽象化。它們分別用於以Map/Dictionary或是List/Array的型態來表示數據,它們可以巢狀的建立出複雜的資料結構並且支援許多不同的資料型態。(從位元組到整數,倍浮點數,字串以及更多)。
這兩個類提供細緻的控制通過網絡發送的每個數據元素並且使用預設的SFS2X二進制協議提供高速的序列化。

讓我們來想一下這個簡單的例子:在一個多人遊戲中,我們需要送出關於戰車一些資料。

ISFSObject sfso = new SFSObject();
sfso.putByte("id", 10);
sfso.putShort("health", 5000);
sfso.putIntArray("pos", Arrays.asList(120,150));
sfso.putUtfString("name", "Hurricane"); 

在程式碼中我們使用單一個位元組(有符號的8位元)來傳送任何的小整數值,一個短整數(有符號的16位元)來應付較大的值,以及整數來應付任何需要以標準(有符號的)32位元表示的值。在這個例子中我們想像有個廣域的RTS(維克:即時戰略)環境,並且使用一個Int陣列來傳送戰車的x/y位置。

» 支援的資料型態

下面列表是兩個類別所支援的所有型態:

型態

使用的位元組

範圍與限制

NULL

1

N/A

BOOL

1

true/false

BYTE

1

8 bit Int, 0 - 2^8 (Java uses a signed byte)

SHORT

2

16 bit Int, -2^15 to 2^15

INT

4

32 bit Int, -2^31 to 2^31

LONG

8

64 bit Int, -2^63 to 2^63

FLOAT

4

請參考單精確浮點格式

DOUBLE

8

請參考倍精確浮點格式

UTF-STRING

variable

UTF-8 多位元編碼
max string len: 2^15 chars (32 KB)

BOOL ARRAY

variable

max array len: 2^15 items (32767)

BYTE ARRAY

variable

max array len: 2^31 items (2147483648)

SHORT ARRAY

variable

max array len: 2^15 items (32767)

INT ARRAY

variable

max array len: 2^15 items (32767)

LONG ARRAY

variable

max array len: 2^15 items (32767)

FLOAT ARRAY

variable

max array len: 2^15 items (32767)

DOUBLE ARRAY

variable

max array len: 2^15 items (32767)

UTF-STRING ARRAY

variable

max array len: 2^15 items (32767)

SFSOBJECT

variable

max key-pair values: 2^15 items (32767)

SFSARRAY

variable

max array len: 2^15 items (32767)

CLASS

variable

N/A —Please 請查看進階教學

當傳送相同型態的值的列表時,陣列型態特別有用。因為其結果會是一個非常緊湊的數據結構,換個方面來看,假如你要傳送有不同資料型態的值的列表,SFSObject 會是最好的選擇。

ActionScript 3 開發者注意事項
AS3陣列與這裡提到的陣列有一個根本上的不同,後者是屬於密集陣列,這意味著它們必須要有一值(或者至少是null),而AS3陣列則沒有這樣的限制。

» 使用範例

一個SFSObject/SFSArray 的使用範例是在擴展當中用來傳遞每個要求與回應。將這篇文章開頭的例子擴大,讓我們來看看完整的使用案例。

客戶端(ActionScript 3)需要將下面的資料傳送到伺服端擴展中:

public function sendSomeData():void
{
    var sfso:SFSObject = new SFSObject();
    sfso.putByte("id", 10);
    sfso.putShort("health", 5000);
    sfso.putIntArray("pos", [120,150]);
    sfso.putUtfString("name", "Hurricane"); 
     
    // Send request to Zone level extension on server side
    sfs.send( new ExtensionRequest("data", sfso) );
}

伺服器擴展的其中一個請求處理器會接收到相同的資料作為其參數物件:

public class DataRequestHandler extends BaseClientRequestHandler
{
    @Override
    public void handleClientRequest(User sender, ISFSObject params)
    {
        // Get the client parameters
        byte id = params.getByte("id");
        short health = params.getShort("health");
        Collection<Integer> pos = params.getIntArray("pos");
        String name = params.getUtfString("name");
          
        // Do something cool with the data...
    }
}

» 檢視SFSObject/SFSArray

讓我們更接近一點來看一下這兩個類別,並且仔細看看背後到底發生什麼事情。兩個物件都提供了兩個有用的方法以階級式或是hex-dump的格式傾倒出它們的內容。

假如我們以這篇文章開頭時所提到範例做為例子,並且呼叫SFSObject的getDump 方法,我們會看到這些輸出:

(short) health: 5000
(utf_string) name: Hurricane
(byte) id: 10
(int_array) pos: [120, 150]

在物件中的每個元素都以這樣的格式列表出來:(型態)鍵名:值((type) key-name: value)

假如你想要更低階的觀點,看看物件如何以二進位表示,你可以呼叫SFSObject.getHexDump 方法:

Binary size: 54
12 00 04 00 06 68 65 61 6C 74 68 03 13 88 00 04 	.....health.....
6E 61 6D 65 08 00 09 48 75 72 72 69 63 61 6E 65 	name...Hurricane
00 02 69 64 02 0A 00 03 70 6F 73 0C 00 02 00 00 	..id....pos.....
00 78 00 00 00 96  

» 位元組陣列

要特別一提的是ByteArray 型態,它提供一個方法來傳送二進位資料到伺服端以及出伺服端。這可以用來傳送小檔案,圖形,多媒體檔案,加密資料等等。譬如說Flash開發者可以透過socket來傳送額外的SWF檔,藉此改善應用程式的安全性,這樣在監測(spying)HTTP流量時將不會被檢查到。

當傳送大塊資料時,我們強烈建議要事先壓縮資料以最佳化資料大小並且避免伺服端的高負擔。特別是動態協定壓縮不會為了大量資料(幾十或是幾百MB)而開始運作,以避免顯著的效能降低(特別是在大量並發的情況下)。強烈建議在傳送前,Zipping 或是gzipping 資料。

» SFSObject/SFSArray 最好的練習

SFSObject 與SFSArray 並非線程安全的,因此在多執行續環境下分享這些物件必須格外小心。有90%的案例SFSObject/SFSArray 只是用來傳送資料,並且被當成區域變數處理,因此並發的狀況並沒有被考慮進來。

假如你有數個類別來表示你遊戲中的模型,為了能幫助他們轉換成為SFSObject的表示方法,或是由SFSObject轉為遊戲中的模型,在類別中加入toSFSObject 與newFromSFSObject方法會是個明智的選擇 。至少你得為那些經常在網路上傳輸的類別做這些事。

下面是一個Java中的例子,假設我們有一個RTS遊戲,有著數個戰車會經常地更新到客戶端。具體來說,我們有一個CombatQuad類(Dune II 懷舊的,唉!) 代表著遊戲中其中一個戰車。在服務器端,我們可能至少會需要一個toSFSObject方法,才能藉此從實例中提取相關的屬性,並在更新時將它們發送出去。

 

public class CombatQuad
{
    private int unitID;
    private int posx;
    private int posy;
    private int energyLevel;
    private int bulletCount;
     
    public CombatQuad(int unitID)
    {
        this.unitID = unitID;
        this.energyLevel = 100;
        this.bulletCount = 20;
    }
     
    //... More getters and setters...
     
    public ISFSObject toSFSObject()
    {
        ISFSObject sfso = new SFSObject();
         
        sfso.putByte("id", unitID);
        sfso.putShort("px", posx);
        sfso.putShort("py", posy);
        sfso.putByte("el", energyLevel);
        sfso.putShort("bc", bulletCount);
         
        return sfso;
    }
}

我們省略了所有的getter / setter方法,單刀直入地直接展現出它是如何運作的。toSFSObject方法負責提取我們需要的屬性,並且盡可能地使用最小的資料型態來格式化這些屬性。當需要在更新中傳送這些物件資料時,我們的擴展程式碼將會大大的簡化。

public void sendMapUpdate(CombatQuad quad, OtherObject other, User recipient)
{
    ISFSObject responseObj = new SFSObject();
    responseObj.putSFSObject("quad", quad.toSFSObject());
    responseObj.putSFSObject("other", other.toSFSObject());
     
    send("quadUpdate", responseObj, recipient);
}

在客戶端我們將使用一個類似但是相反的方法:我們將從借由SFSObject傳送過來的屬性,重新建立類別實例,我們將實現一個靜態的建構子稱之為newFromSFSObject。

public class CombatQuad
{
    private var unitID:int;
    private var posx:int;
    private var posy:int;
    private var energyLevel:int;
    private var bulletCount:int;
     
    public static function newFromSFSObject(sfso:ISFSObject):CombatQuad
    {
        var combatQuad:CombatQuad = new CombatQuad(sfso.getByte("id"));
        combatQuad.posx = sfso.getShort("px");
        combatQuad.posy = sfso.getShort("py");
        combatQuad.energyLevel = sfso.getByte("el");
        combatQuad.bulletCount = sfso.getByte("bc");
 
        return combatQuad;
    }
     
    function CombatQuad(unitID:int):void
    {
        this.unitID = unitID;
    }
     
    //... More getters and setters...
}

» 只適用於AS3: Object/Array 到SFSObject/SFSArray 的轉換

在ActionScript 3 中,SFSObject 與SFSArray 提供了直接的轉換到/從它們各自的原生類別:Object 與Array。這是為了幫助為SmartFoxServer 1.x而寫的舊版程式碼的遷移,並且對使用動態型態的開發者提供額外的便利性。

有必要強調的是,使用泛型對象來替代資料模型類別一般是不被建議的。(維克:泛型對象對象指的是SFSObject/SFSArray,資料模型類別就像是前面提到戰車的類別)因為它無法利用ActionScript 3采用的類型優化。動態附加到物件上的屬性不是強型態的,因此無法由編譯器最佳化。

通常在許多場合中這會是有用的,而且可以加速開發。額外的方法如下:

  • SFSObject.newFromObject: 靜態建構子,從一個物件中產生一個新的SFSObject 實例。支援的型態為:null, Boolean, Number, String, Object, Array 。這個操作支援巢狀物件。
  • SFSObject.toObject: 將SFSObject實例轉換成一個物件,包含巢狀的物件。
  • SFSArray.newFromArray: 靜態建構子,從一個陣列中產生一個新的SFSArray。支援的型態為:null, Boolean, Number, String, Object, Array 。這個操作支援巢狀物件。
  • SFSArray.toArray: 將SFSArray 實例轉化成為陣列,包含巢狀的物件。

使用這種方法的缺點是,你失去了微調數值型態的能力:所有的數字將被轉換為一個32位整數(非十進制值non decimal values)或64位雙精度(十進制值decimal values)。

» 更多的資源

要取得更多關於SFSObject 與SFSArray 的細節與範例,我們強烈建議看一下SFS2X的範例,並且參考這個網站(維克:請看官網)的API Documentation章節的文件。也可以查看Class serialization  這篇進階教學。

上一篇:開發基礎:伺服器變數

下一篇:開發基礎:資料庫方法

 
 

  按個讚!~支持本站!~

FB推薦載入中