現在已經從伺服器跟客戶端了解到了發送跟接收的步驟,不過伺服器的如何發送到客戶端並接和處理收似乎是還沒有討論過。不過因為使用相同的規則解析實際上差異並不大。
雖然我們是這樣假設的,不過 Unlight 在這方面出現了一個有點奇怪的機制,伺服器跟客戶端的解析方式竟然有一點點的差異。
前面我們有提到表示「長度」的資訊通常是一個 Short Integer 也就是 2 Bytes 的資料,但是在客戶端接收時原本的 2 Bytes 的封包卻變成了 4 Bytes 跟我們從客戶端發送到伺服器的長度似乎有點不同。
我們先快速的看一下在 src/protocol/command/command.rb
被我們跳過的 #gen_send_cmd
方法實作的部分,因為跟接收的方式很類似我們只需要看一下將資料轉成封包的部分。
# 型によって返す変換する文字列を返す
def type_send_res(t,v)
case t
when :String
v
when :int
"[#{v}].pack('N')"
when :char
"[#{v}].pack('c')"
when :Boolean
"[#{v}.to_i].pack('C')"
end
end
在 Unlight 的伺服器中,如果是「整數」資訊基本上會被使用 #pack
來轉成 Byte Array 並且使用的模式是 N
模式(Big Endian)產生出來的結果會是類似 \0x00\0x00\0x00\0x01
的字串(在程式中傳輸連續的資料就是把封包連著,所以可以用字串表示),而且很明顯的大小是 4 Bytes 而不是 2 Bytes。
這是因為伺服器的接收指令是以配合 ActionScript 的特性來處理,與之相反的客戶端也需要配合伺服器來調整接收指令的方式。扣掉這幾個稍微特別的情況,其他指令上的處理大多是一致的。
這個情況跟前面提到用 JavaScript 當客戶端時發送資料會遇到不少問題的情況是類似的,像是 Ruby 這類語言因為不是強行別的所以大多是在設計時就固定好大小,要產生出特定大小的資料反而因為沒有這樣的設計而不容易製作出來。
因此我們再回來看客戶端的 src/net/command/AuthCommand.as
這個檔案關於接收指令的處理:
private function authReturn(ba:ByteArray):void
{
var salt_length:int;
salt_length = ba.readUnsignedInt();
var salt:String;
salt = ba.readUTFBytes(salt_length);
var server_pub_key_length:int;
server_pub_key_length = ba.readUnsignedInt();
var server_pub_key:String;
server_pub_key = ba.readUTFBytes(server_pub_key_length);
server.authReturn(salt, server_pub_key);
}
我們就會看到在客戶端這邊是是使用 readUnsignedInt
來讀取 4 Bytes,而不是 readShort
(文件) 來讀取 2 Bytes 的資料。
如果可以的話,我們應該在設計的時候應該避免這樣的狀況,不過 Unlight 原本是在怎樣的情境下做出這樣的選擇就無法得知了。
我的個人部落格是弦而時習之平常會把自己發現的一些新技巧紀錄在上面,也歡迎大家來逛逛。