Google Code Prettify

顯示具有 Others 標籤的文章。 顯示所有文章
顯示具有 Others 標籤的文章。 顯示所有文章

2015年5月26日 星期二

byte ordering functions

前一篇「Linux Network Programming - 初體驗」目的很單純,就是快速的對 socket 有基本的認識,這一篇要說明 byte ordering 的問題及解決辦法!

在一般人的直覺裡,如果一個 short 有兩個 byte,在記憶體中應該就是由左到右放,就像寫字一樣,由左到右寫,那麼就會是高位元放在左、低位元放在右,這就是 big-endian byte order。可偏偏並非所有的電腦都是這麼放的! 有些電腦是低位元放在左,高位元放在右,這稱為 little-endian byte order。而 socket 的傳輸資料不管原來是什麼型別,傳輸過程一定會是以 byte array 的方式傳輸,接收端接收後,再依原先約定好的格式還原成各種型別。在網路上傳輸的 byte array 順序,是規範成 big-endian。

上圖是「Unix Netowrk Programming」的3.4節中的一個插圖,用來說明 big-endian 和 little-endian 的差別。在開始寫程式前,一定要先了解這些基本的知識,並且知道 C 提供了那些函式來解決這個問題!

回過頭去看前一篇的 server 程式,有如下一段程式碼:
22 servaddr.sin_family = AF_INET;
23 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
24 servaddr.sin_port = htons(1513);
23、24行中呼叫的 htonlhtons 就是本篇要介紹的,他們會將如 1513、INADDR_ANY 這樣的常數值,轉成合乎網路傳輸所需要的值 (轉換後如果值改變的話,基本上就是高位元、低位元排列順序變了造成的!)。
兩個函式是 host to netowrk 的縮寫,l 和 s 就是 long 和 short,htonl 用來處理 32 位元的值,htons 用來處理 16 位元的值。要特別注意,就算是在 64 位元電腦上,htonl 還是只用來處理 32 位元的值!




2015年5月10日 星期日

BCD pack & unpack

在程式裡的 I/O 和 CPU 計算比起來,速度差距非常大,網路傳輸又是 I/O  中比較慢的一種,所以,常可見到網路傳輸的資料,會先經過壓縮後再傳輸,接收端接到後再解壓縮。BCD 是一種常用的壓縮技術,當要傳輸的資料中,某一段確定是數字,即可將該段的資料以 BCD 壓縮後傳輸,例如:
123456789665,或許它代表著12點34分56秒789毫秒665奈秒,如果不壓縮直接傳就是 12 個 bytes,BCD 則只會有 6 個 bytes,為什麼? 說明如下:
  1. 一個 byte 有 8 個 bits,用來存數字,實際上只會用到後面 4 個 bits,前面的 4 個 bits 永遠為 0000,例如 5 的 8 個 bits 會是 00000101 (二進位表示法),9 的 8 bits 為 00001001。
  2. BCD 用一個 byte 來存兩個數字,前 4 個 bits (高位元) 存一個數字,後 4 個 bits 再存一個數字,所以,如果有兩個數字 95,用 BCD 儲存就會是 10010101,前 4 個 bits 是 9,後 4 個 bits 是 5。
底下是 BCD 的程式:

public class BCD {
 static public byte[] pack(long number) {
  byte hi, lo;
  String s = String.valueOf(number);
  byte[] data = s.getBytes();
  byte[] bcd = new byte[s.length() / 2];
  for (int i = 0; i < bcd.length; i++) {
   hi = (byte)(data[i * 2] << 4);
   lo = (byte)(data[i * 2 + 1] & 0x0f);
   bcd[i] = (byte)(hi | lo);
  }
  return bcd;
 }
 static public long unpack(byte[] pack, byte[] data) {
  byte hi, lo;
  long number = 0L;
  for (int i = 0; i < pack.length; i++) {
   hi = (byte)((pack[i] >> 4) & 0x0f);
   lo = (byte)(pack[i] & 0x0f);
   data[i * 2] = hi;
   data[i * 2 + 1] = lo;
   number = number * 100 + (hi * 10) + lo;
  }
  return number;
 }
}

應該很容易看的懂,pack 就是將正常的資料壓縮成 BCD 格式,unpack 是將 BCD 格式的資料還原。測試程式如下:
要被壓縮的資料為 number,值為 123456789665,參考下面的輸出,可以看到 BCD 長度只有 6 bytes,其 hex 值即是我們要傳輸的值; 把 BCD 資料解壓縮,即可回 number 的值。
bcd len: 6
bcd value: [B@439f5b3d
bcd value (hex): 123456789665
data len: 12
data value: [B@685f4c2e
data value (hex) 010203040506070809060605
number: 123456789665
要特別注意的是,上面 unpack 程式中,高位元的資料在向左 shift 4 位到低位元後,一定要再 & 0x0f,將高位元清為 0,因為 shift 時,如果最高的 bit 為 1,往左 shift 4 位,高位的 4 個 bits 會全部變成 1,例如最前面例子舉的 95,二進位是 10010101,5 是存在低位元,只要將高位元清成0 ( & 0x0f),即可得到 5,但是 高位元的 9 向左 shift 4 位後會是 11111001,所以要將高位元再清為 0 (& 0x0f)。