网络游戏客户端通信模块的简单实现如下,未经充分测试,功能也不完善,纯实学习,积累之用。
1. 首先是发送的包的封装,与服务端约定好的协议,代码如下:
1 using Cmd; 2 using ProtoBuf.Meta; 3 using System; 4 using System.Collections.Generic; 5 using System.IO; 6 7 namespace Network 8 { 9 /// <summary> 10 /// 发送的包,包体结构如下: 11 /// 1. 包体长度(4字节) 12 /// 2. 签名(4字节) 13 /// 3. protoId(2字节) 14 /// 4. 序列id(4字节) 15 /// 5. 包体 16 /// </summary> 17 public class SendingPacket 18 { 19 static uint s_lastSequenceId; // 上一次的序列id 20 21 uint m_length; // 包长度(4字节) 22 uint m_sign; // 签名(4字节) 23 ushort m_protoId; // protoId(2字节) 24 uint m_sequenceId; // 序列id(4字节) 25 MemoryStream m_body = new MemoryStream(); // 包体 26 27 // Properties 28 public Action<ReturnPacket> OnPacketReturn { get; set; } 29 public EProtoId? ExpectedReturnId { get; set; } 30 public bool SendWait { get; set; } 31 public EProtoId ProtoId { get { return (EProtoId)m_protoId; } } 32 33 #region static 34 35 // 序列id会不断自增 36 static uint GetSequenceId() 37 { 38 return ++s_lastSequenceId; 39 } 40 41 // 计算签名 42 static uint CalcSign(byte[] bytes) 43 { 44 if (bytes == null) 45 throw new ArgumentNullException("bytes"); 46 47 const string str = "45ddk124k55k3l9djdssk9gk1zc6bn9cpo4afcx4322121ddafadfasdfazctewq"; 48 uint[] tempArray = new uint[256]; 49 50 for (int i = 0; i < tempArray.Length; i++) 51 { 52 if (i < str.Length) 53 tempArray[i] = str[i]; 54 } 55 56 int clampI = 0; 57 uint sign = 0; 58 59 for (int i = 0; i < tempArray.Length; i++) 60 { 61 if (clampI >= bytes.Length) 62 clampI %= bytes.Length; 63 64 byte b = bytes[clampI]; 65 uint r = 0; 66 67 switch ((clampI + i) % 4) 68 { 69 case 0: 70 r = (uint)(b | tempArray[i]); 71 break; 72 case 1: 73 r = (uint)(b + tempArray[i]); 74 break; 75 case 2: 76 r = (uint)(b * 2); 77 break; 78 case 3: 79 r = (uint)(b > tempArray[i] ? (b - tempArray[i]) : (tempArray[i] - b)); 80 break; 81 default: 82 throw new InvalidOperationException("Unexpected result: " + ((clampI + i) % 4)); 83 } 84 r %= 128; 85 86 sign += r; 87 88 clampI += (int)tempArray[i]; 89 } 90 91 return sign % 1024; 92 } 93 94 #endregion 95 96 public SendingPacket(EProtoId protoId, object body) 97 { 98 if (body == null) 99 throw new ArgumentNullException("body"); 100 101 m_protoId = (ushort)protoId; 102 103 // 序列化 104 RuntimeTypeModel.Default.Serialize(m_body, body); 105 106 // length 107 m_length = (uint)(m_body.Length + 4 + 4 + 2); 108 m_length |= 0x20000000; // 与服务端的约定,最高位为1,此时服务端会验证签名 109 } 110 111 /// <summary> 112 /// 每次发包都需要更新序列id和签名 113 /// </summary> 114 public void UpdateSequenceId() 115 { 116 m_sequenceId = GetSequenceId(); 117 118 // sign 119 var forSign = new List<byte>(); 120 { 121 forSign.AddRange(BitConverter.GetBytes(m_protoId)); 122 forSign.AddRange(BitConverter.GetBytes(m_sequenceId)); 123 forSign.AddRange(m_body.ToArray()); 124 } 125 m_sign = CalcSign(forSign.ToArray()); 126 } 127 128 public byte[] GetBytes() 129 { 130 var list = new List<byte>(); 131 { 132 list.AddRange(BitConverter.GetBytes(m_length)); 133 list.AddRange(BitConverter.GetBytes(m_sign)); 134 list.AddRange(BitConverter.GetBytes(m_protoId)); 135 list.AddRange(BitConverter.GetBytes(m_sequenceId)); 136 list.AddRange(m_body.ToArray()); 137 } 138 139 return list.ToArray(); 140 } 141 } 142 }