第一個項目就這樣快結束了,收獲挺多。感觸最深的是需求分析的重要性,需求分析目的就是為了讓程序員知道自己要干什么,在需求分析的時候一定要充分考慮項目上的難點提出來和對方一起協商解決,不然到時候編碼的時候才考慮到這個問題你就慘了,好的需求分析可以使你在接下的是編程中順風順水,至少不會遇到太大的困難。這次項目中遇到最大的問題就是在歷史數據邊界的問題上卡死,主要原因還是沒有協商好,最后搞得我們好惱火。。。
項目完成之后進行連調由於施工現場在別的地方所以就把程序交給對方調試。。。結果對方好幾次都被小問題卡主浪費好多時間,都怪我們沒有寫好開發文檔。。。
這次項目的傳輸協議是遵循“可再生能源建築應用示范項目數據監測系統技術導則”來寫的,據說是專家寫的但是感覺有點搓。
下面總結一下編碼中使用到的技術
1.數據包的組包
2.AES加密
3.MD5校驗
4.循環冗余檢驗(Cyclic Redundancy Check)
5.xml文件轉換為字節流的方法:使用XmlSerializer類
6.線程同步之 lock
7.線程訪問控件的2種方法
1.數據包的組包
組包首先要創建一個枚舉類型類型的數據來確定具體數據的存放位置

//數據包的組包格式 public enum PackagePosition : int { PackageHead = 0, //0x68 0x68 0x16 0x16 //4字節 EffectiveDataLength = 4, // 4字節 EffectiveData = 8 //M+4字節,M為xml加密后的長度,4字節為指令序號 }
這樣的話填寫數據包的時候就容易多了,整形轉換為字節可以這樣

public byte[] EffectivePack(int Instruct_seq, byte[] data) { byte[] EffectiveData = new byte[4 + data.Length]; EffectiveData[(int)EncodingTable.EffectiveDataPosition.Instruction_Sequence] = (byte)(Instruct_seq >> 24); //高8位 EffectiveData[(int)EncodingTable.EffectiveDataPosition.Instruction_Sequence + 1] = (byte)((Instruct_seq >> 16) & 0xff); EffectiveData[(int)EncodingTable.EffectiveDataPosition.Instruction_Sequence + 2] = (byte)((Instruct_seq >> 8) & 0xff); EffectiveData[(int)EncodingTable.EffectiveDataPosition.Instruction_Sequence + 3] = (byte)(Instruct_seq & 0xff); Array.Copy(data, 0, EffectiveData, (int)EncodingTable.EffectiveDataPosition.Instruction_data, data.Length); return EffectiveData; }
拆包是解包的逆過程做法差不多。
2.AES加密
C#牛逼得很提供了許多加密算法,還要感謝某個網友我們抄你的。。。
高級加密標准(Advanced Encryption Standard,AES),又稱Rijndael加密法
高級加密標准(AES)已然成為對稱密鑰加密中最流行的算法之一。

public class AES { private byte[] _vKey; private byte[] _key; public AES(byte[] vkey, byte[] key) { _vKey = vkey; _key = key; } public byte[] _VKey { set { _vKey = value; } } public byte[] _Key { set { _key = value; } } /// <summary> /// 加密 /// </summary> /// <param name="data"></param> /// <returns></returns> public byte[] AESEncrypt(byte[] data) { SymmetricAlgorithm des = Rijndael.Create(); des.Key = _key; des.IV = _vKey; MemoryStream ms = new MemoryStream(); CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write); cs.Write(data, 0, data.Length); cs.FlushFinalBlock(); byte[] cipherBytes = ms.ToArray(); cs.Close(); ms.Close(); return cipherBytes; } /// <summary> /// 解密 /// </summary> /// <param name="data"></param> /// <returns></returns> public byte[] AESDecrypt(byte[] data) { byte[] decdata; try { SymmetricAlgorithm des = Rijndael.Create(); des.Key = _key; des.IV = _vKey; MemoryStream ms = new MemoryStream(data); CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Read); decdata = new byte[data.Length]; int length = cs.Read(decdata, 0, data.Length); byte[] decdata1 = new byte[length]; Array.Copy(decdata, 0, decdata1, 0, length); cs.Close(); ms.Close(); return decdata1; } catch (Exception ex) { System.Diagnostics.Trace.Write("AESDecrypt Error :" + ex.Message); } return null; } }
3.MD5校驗
項目中是用來計算md5值發送給對方驗證用的。
使用的時候需要加上System。web引用。

class MD5Encrypt { public static string md5 = ""; public static string EncryptMd5(string seq) { string combine = seq + md5; string Enseq = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(combine, "MD5"); return Enseq; } }
4.循環冗余檢驗(Cyclic Redundancy Check)CRC16校驗
它是數據通信領域中最常用的一種差錯校驗碼,其特征是信息字段和校驗字段的長度可以任意選定。

public class CRC16 { public CRC16() { } private readonly byte[] _auchCRCHi = new byte[]//crc高位表 { 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 }; private readonly byte[] _auchCRCLo = new byte[]//crc低位表 { 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 }; public ushort CalculateCrc16(byte[] buffer) { byte crcHi = 0xff; // 高位初始化 byte crcLo = 0xff; // 低位初始化 for (int i = 0; i < buffer.Length; i++) { int crcIndex = crcHi ^ buffer[i]; //查找crc表值 crcHi = (byte)(crcLo ^ _auchCRCHi[crcIndex]); crcLo = _auchCRCLo[crcIndex]; } return (ushort)(crcHi << 8 | crcLo); } }
5.xml文件轉換為字節流的方法:使用XmlSerializer類

class xmlOperator { /// <summary> /// 字節流轉換為XmlDocument類型 /// </summary> /// <param name="data"></param> /// <returns></returns> public static XmlDocument byteToXmlDoc(byte[] data) { MemoryStream Dstream = new MemoryStream(data); XmlSerializer ser = new XmlSerializer(typeof(XmlDocument)); XmlDocument XmlData = (XmlDocument)ser.Deserialize(Dstream); Dstream.Flush(); Dstream.Close(); //關閉內存流,釋放資源 return XmlData; } /// <summary> /// XmlDocument轉換為字節流 /// </summary> /// <param name="XmlData"></param> /// <returns></returns> public static byte[] XmlDocToByte(XmlDocument XmlData) { XmlSerializer ser = new XmlSerializer(typeof(XmlDocument)); MemoryStream stream = new MemoryStream(); ser.Serialize(stream, XmlData); byte[] data = stream.GetBuffer(); return data; } }
6.線程同步之 lock
lock關鍵字可以用來確保代碼塊完整運行,而不會被其他線程中斷,這是通過在代碼塊運行期間為給定對象獲取互斥鎖來實現的。lock語句以關鍵字lock開頭,它有一個作為參數的對象,在該參數的后面還有一個一次只能由一個線程執行的代碼塊。例如:

public class TestThreading { private System.Object lockThis = new System.Object(); public void Function() { lock (lockThis) { // Access thread-sensitive resources. } } }
提供給 lock 關鍵字的參數必須為基於引用類型的對象,該對象用來定義鎖的范圍。在上例中,鎖的范圍限定為此函數,因為函數外不存在任何對該對象的引用。如果確實存在此類引用,鎖的范圍將擴展到該對象。嚴格地說,提供給 lock 的對象只是用來唯一地標識由多個線程共享的資源,所以它可以是任意類實例。然而,實際上,此對象通常表示需要進行線程同步的資源。例如,如果一個容器對象將被多個線程使用,則可以將該容器傳遞給 lock,而 lock 后面的同步代碼塊將訪問該容器。只要其他線程在訪問該容器前先鎖定該容器,則對該對象的訪問將是安全同步的。
通常,最好避免鎖定 public 類型或鎖定不受應用程序控制的對象實例。例如,如果該實例可以被公開訪問,則 lock(this) 可能會有問題,因為不受控制的代碼也可能會鎖定該對象。這可能導致死鎖,即兩個或更多個線程等待釋放同一對象。出於同樣的原因,鎖定公共數據類型(相比於對象)也可能導致問題。鎖定字符串尤其危險,因為字符串被公共語言運行庫 (CLR)“暫留”。 這意味着整個程序中任何給定字符串都只有一個實例,就是這同一個對象表示了所有運行的應用程序域的所有線程中的該文本。因此,只要在應用程序進程中的任何位置處具有相同內容的字符串上放置了鎖,就將鎖定應用程序中該字符串的所有實例。因此,最好鎖定不會被暫留的私有或受保護成員。某些類提供專門用於鎖定的成員。例如,Array 類型提供 SyncRoot。許多集合類型也提供 SyncRoot。
7.線程訪問控件的2種方法
第一種方法:
Control.CheckForIllegalCrossThreadCalls = false;
不檢查跨線程訪問是否有問題,據說不好,我使用的是第二種
第二種方法:
使用委托

public delegate void showmsg(string smg); public void ShowMsg(string msg) { if (textBox_show.InvokeRequired) { showmsg sw = new showmsg(ShowMsg); this.Invoke(sw, msg); } else textBox_show.Text += DateTime.Now.ToString()+" "+msg + "\r\n"; } void client_OnMessage(object sender, MessageArgs args) { ShowMsg(args.EventMsg); }