数据上传项目总结
第一个项目就这样快结束了,收获挺多。感触最深的是需求分析的重要性,需求分析目的就是为了让程序员知道自己要干什么,在需求分析的时候一定要充分考虑项目上的难点提出来和对方一起协商解决,不然到时候编码的时候才考虑到这个问题你就惨了,好的需求分析可以使你在接下的是编程中顺风顺水,至少不会遇到太大的困难。这次项目中遇到最大的问题就是在历史数据边界的问题上卡死,主要原因还是没有协商好,最后搞得我们好恼火。。。
项目完成之后进行连调由于施工现场在别的地方所以就把程序交给对方调试。。。结果对方好几次都被小问题卡主浪费好多时间,都怪我们没有写好开发文档。。。
这次项目的传输协议是遵循“可再生能源建筑应用示范项目数据监测系统技术导则”来写的,据说是专家写的但是感觉有点搓。
下面总结一下编码中使用到的技术
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); }