[C#.NET] 開發通訊協定必須要會的技巧

  • 15051
  • 0
  • C#
  • 2016-07-11

[C#.NET] 開發通訊協定必須要會的技巧

基本上處理通訊協定需要瞭解到計算機概論的東西,比如 AND/OR/XOR 這些,理論上的東西可能就不是我能寫的了,我們來看看比較實務上的技巧

 


>> 運算子

 

先來看個例子


public void MyTestMethod2()
{
    ushort source = 6235;
    byte hi = (byte)(source >> 8);
    byte lo = (byte)source;
    Debug.WriteLine("hi:{0},lo:{1}", hi, lo);
}

Result
hi:24,lo:91

6235 變成 2進制 的樣子,最右邊的 Q 是基準點,如下圖

image

 

當調用 source >> 8 後,於是基準點來到了 Q,就可以得到 24

image

 

至於 (byte)source 會得到 91,就是因為byte 只能處理 8 個bit,其餘的都會被丟棄,所以轉型的時候一定要特別注意溢位跟資料遺失的問題

image

 


<<  運算子

再用同樣的例子解釋


public void MyTestMethod2()
{
    ushort source = 6235;
    var hi = source << 8;
    Debug.WriteLine("hi:{0}", hi);
}

Result:
hi:1596160

 

右邊多了 8 個bit ,所以轉成 10 進制的結果就是 1596160

image

 


.NET BCL 提供的 BitConverter.GetBytes 也可以達到 >>  的效果,取出特定的 byte


public void MyTestMethod2()
{
    ushort source = 6235;
    var sourceArray = BitConverter.GetBytes(source);
    Debug.WriteLine("hi:{0},lo:{1}", sourceArray[0], sourceArray[1]);
}

Result:
hi:91,lo:24

眼尖的捧油應該會發現sourceArray[0]存放的是91,不是24,這樣的組合我們稱為 Little-Endian,若需要將陣列要在轉換成所需的字串,要先調用Array.Reverse

以下範例就是將陣列轉成 Hexadecimal

 


public void MyTestMethod2()
{
    short source = 6235;
    var sourceArray = BitConverter.GetBytes(source);
    Array.Reverse(sourceArray);
    var hi = Convert.ToString(sourceArray[0], 16);
    var lo = Convert.ToString(sourceArray[1], 16);
    Debug.WriteLine("hi:{0},lo:{1}", hi, lo);
}

Result:
hi:18,lo:5b


I 運算子

這就是計算機概論講的 OR 運算;當進行 2 進位運算,只要有任一位元為 1 時,運算結果才能為 1


public void MyTestMethod2()
{
    byte result = 25 | 155;
    Debug.WriteLine("result:{0}", result);
}

result:155

轉換成2進制,如下圖:

image


& 運算子

這就是計算機概論講的 AND 運算;當進行 2 進位運算,只有兩個位元為 1 時,運算結果才能為 1,反之為 0


public void MyTestMethod2()
{
    byte result = 25 & 155;
    Debug.WriteLine("result:{0}", result);
}

result:25

轉換成2進制,如下圖:

image

^ 運算子

這就是計算機概論講的 XOR 運算;當進行 2 進位運算,只有一個位元為 1 時,運算結果才能為 1,反之為 0


public void MyTestMethod2()
{
    byte result = 25 ^ 155;
    Debug.WriteLine("result:{0}", result);
}

result:130

轉換成2進制,如下圖:

image

~ 運算子

這就是計算機概論講的 NOT ,就是 0 變 1,1 變 0,正號變負號,負號變正號


public void MyTestMethod2()
{
    sbyte result = 25;
    Debug.WriteLine("result:{0}", ~result);
}

result:-26

image


在用 TCP/Serial 處理通訊時,我們會需要用到byte[],我們有幾個方式可以組合所需的byte[],以下是我常遇到的情境。

  • 情境一:用整數組合 protocol

使用MemoryStream


public void MyTestMethod2()
{
    ushort transport = 1;
    ushort protocol = 0;
    byte function = 1;
    ushort length = 6;
    ushort start = 0;
    ushort quantity = 10;
    byte[] requestArray = null;
    using (MemoryStream memory = new MemoryStream())
    {
        memory.WriteByte((byte)(transport >> 8));
        memory.WriteByte((byte)transport);
        memory.WriteByte((byte)(protocol >> 8));
        memory.WriteByte((byte)protocol);
        memory.WriteByte(function);
        memory.WriteByte((byte)(length >> 8));
        memory.WriteByte((byte)length);
        memory.WriteByte((byte)(start >> 8));
        memory.WriteByte((byte)start);
        memory.WriteByte((byte)(quantity >> 8));
        memory.WriteByte((byte)quantity);
        requestArray = memory.ToArray();
    }
}

當然List<byte> 也是不錯的選擇,在此就不再多描述。

  • 情境二:用byte[] + 整數組合 protocol

基本上這就是動態處理陣列

使用MemoryStream


public void MyTestMethod3()
{
    byte[] transportArray = new byte[2] { 1, 2 };
    ushort protocol = 0;
    byte function = 1;
    ushort length = 6;
    ushort start = 0;
    ushort quantity = 10;
    byte[] requestArray = null;
    using (MemoryStream memory = new MemoryStream())
    {
        memory.Write(transportArray, 0, transportArray.Length);
        memory.WriteByte((byte)(protocol >> 8));
        memory.WriteByte((byte)protocol);
        memory.WriteByte(function);
        memory.WriteByte((byte)(length >> 8));
        memory.WriteByte((byte)length);
        memory.WriteByte((byte)(start >> 8));
        memory.WriteByte((byte)start);
        memory.WriteByte((byte)(quantity >> 8));
        memory.WriteByte((byte)quantity);
        requestArray = memory.ToArray();
    }
}

當然List<byte> 也是不錯的選擇,在此就不再多描述。

還有一個蠻常見的方式就是Array.Copy,不過不太適合在這個情境裡,程式碼會變得又臭又長。

  • 情境三:用 16 進制字串組合 protocol

使用 Convert.ToByte

[TestMethod()]
public void MyTestMethod5()
{
    var request = "00 00 00 00 00 06 01 01 00 00 00 0A".Replace(" ", "");
    var requestArray = Enumerable.Range(0, request.Length)
                    .Where(x => x % 2 == 0)
                    .Select(x => Convert.ToByte(request.Substring(x, 2), 16))
                    .ToArray();
}

更多進制轉換可參考
http://www.dotblogs.com.tw/yc421206/archive/2008/11/15/5992.aspx
http://www.dotblogs.com.tw/yc421206/archive/2008/10/27/5790.aspx
http://www.dotblogs.com.tw/yc421206/archive/2008/11/15/5993.aspx

 

若通訊格式是吃ACSII,可以使用 Encoding.ASCII.GetByte() 方法處理

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo