网络游戏客户端通信模块简单实现

网络游戏客户端通信模块的简单实现如下,未经充分测试,功能也不完善,纯实学习,积累之用。

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 }

SendingPacket

2. 收到的包的封装,代码如下:

 1 using Cmd;
 2 using System;
 3
 4 namespace Network
 5 {
 6     /// <summary>
 7     /// 接收到的包,结构如下:
 8     /// 1. 包体长度(4字节)
 9     /// 2. protoId(2字节)
10     /// 3. 包体
11     /// </summary>
12     public class ReturnPacket
13     {
14         ushort m_protoId;
15         byte[] m_body;
16
17         // Properties
18         public EProtoId ProtoId { get { return (EProtoId)m_protoId; } }
19         public byte[] Body { get { return m_body; } }
20
21         public ReturnPacket(ushort protoId, byte[] body)
22         {
23             if (body == null)
24                 throw new ArgumentNullException("body");
25
26             m_protoId = protoId;
27             m_body = body;
28         }
29     }
30 }

ReturnPacket

3. 解包工具,代码如下:

 1 using System;
 2 using UnityEngine;
 3
 4 namespace Network
 5 {
 6     /// <summary>
 7     /// 解包工具
 8     /// </summary>
 9     class UnpackTool
10     {
11         const int HeadLength = 6;
12
13         byte[] m_readBuffer = new byte[256 * 1024];
14         int m_beginOffset = 0;
15         int m_endOffset = 0;
16
17         public void Reset()
18         {
19             m_beginOffset = 0;
20             m_endOffset = 0;
21         }
22
23         public void UnpackData(byte[] receivedBuffer, int receivedLength, Action<ReturnPacket> unpackCallback)
24         {
25             if (receivedLength <= 0)
26                 return;
27
28             Array.Copy(receivedBuffer, 0, m_readBuffer, m_endOffset, receivedLength);
29             m_endOffset += receivedLength;
30
31             while (m_endOffset - m_beginOffset >= HeadLength)
32             {
33                 int bodyLen = BitConverter.ToInt32(m_readBuffer, m_beginOffset) - 2;
34                 int packetLen = bodyLen + 4 + 2;
35
36                 if (m_endOffset - m_beginOffset < packetLen)
37                 {
38                     Debug.LogError(string.Format("Data not enought, bodyLen={0}, m_beginOffset={1}, m_endOffset={2}", bodyLen, m_beginOffset, m_endOffset));
39                     break;
40                 }
41
42                 UInt16 protoId = BitConverter.ToUInt16(m_readBuffer, m_beginOffset + 4);
43                 if (protoId == 256)//特殊处理,这个包如果id是256,那其实是服务端对于我发给他的心跳包的原样返回,由于小根转换的原因会反一反,其实真实id是1,256这个数后端说是不会主动发过来的。
44                 {
45                     // [dev] ??? 16位的数最大值为255,怎么可能到256,逗我?
46                     Debug.LogError("protoId is: " + protoId);
47                     protoId = 1;
48                 }
49
50                 //Debug.Log("protoId:0x" + Convert.ToString(protoId, 16) + ",len:" + bodyLen);
51                 byte[] cmdBytes = new byte[bodyLen];
52                 Array.Copy(m_readBuffer, m_beginOffset + 4 + 2, cmdBytes, 0, bodyLen);
53                 m_beginOffset += packetLen;
54
55                 if (m_beginOffset >= (m_readBuffer.Length / 2))
56                 {
57                     int offset = m_endOffset - m_beginOffset;
58                     Array.Copy(m_readBuffer, m_beginOffset, m_readBuffer, 0, offset);
59                     m_endOffset = offset;
60                     m_beginOffset = 0;
61                 }
62
63                 if (unpackCallback != null)
64                 {
65                     var p = new ReturnPacket(protoId, cmdBytes);
66                     unpackCallback(p);
67                 }
68             }
69         }
70     }
71 }

UnpackTool

4. Socket类,代码如下:

  1 using Cmd;
  2 using ProtoBuf;
  3 using System;
  4 using System.Collections.Generic;
  5 using System.ComponentModel;
  6 using System.IO;
  7 using System.Linq;
  8 using System.Net;
  9 using System.Net.Sockets;
 10 using System.Threading;
 11 using UnityEngine;
 12 using SystemThreadPriority = System.Threading.ThreadPriority;
 13
 14 namespace Network
 15 {
 16     // 为兼容老接口
 17     public enum NetworkDataType
 18     {
 19         SendWait,               // 发送,并等待返回
 20         SendOnly,               // 只发送
 21     }
 22
 23     public class NetworkSocket
 24     {
 25         const float DurationToTurnFlower = 1.2f;                    // 发包时,从锁屏到转菊花的时间
 26         const float MaxSendingDuration = 3f;                        // 发送的最大时间,超过此时间将会重连,或断开连接
 27         const int MaxResendTimes = 3;                               // 最大重发次数
 28
 29         // 阻塞
 30         float m_blockTimer;
 31         BlockState m_blockState = BlockState.Idle;
 32         public event Action EventLockScreen;
 33         public event Action EventTurnFlower;
 34         public event Action EventEndBlock;
 35
 36         // 事件
 37         public event Action<NetworkSocket> EventDisconnect;
 38         public event Action<Action> EventRelogin;
 39         Action m_eventConnectedSucceed;
 40         Action m_eventConnectedFailed;
 41         Action m_eventReloginSucceed;
 42
 43         TcpClient m_tcpClient;
 44         string m_host;
 45         int m_port;
 46         NetworkState m_state = NetworkState.Disconnecting;
 47
 48         // 发包
 49         Queue<SendingPacket> m_forSendingPackets = new Queue<SendingPacket>();                      // 要发送的包的队列
 50         SendingPacket m_sendingPacket;                                                              // 当前正在发送的包
 51         float m_sendingTimer;
 52
 53         // 重发
 54         SendingPacket m_resendPacket;                                                               // 重发的包
 55         float m_resendTimes;                                                                        // 重发次数
 56
 57         // 收包
 58         UnpackTool m_unpackTool = new UnpackTool();                                                 // 解包工具
 59         byte[] m_receivedBuffer = new byte[256 * 1024];                                             // 收包临时缓存
 60         Thread m_listenInThread;                                                                    // 监听收包线程
 61         Queue<ReturnPacket> m_receivedPackets = new Queue<ReturnPacket>();                          // 收到的包的缓存
 62
 63         #region Properties
 64
 65         public bool Connected
 66         {
 67             get { return m_tcpClient != null && m_tcpClient.Connected; }
 68         }
 69
 70         NetworkState State
 71         {
 72             get { return m_state; }
 73             set
 74             {
 75                 //Debug.Log(string.Format("#[Network]Change network state from {0} to {1}", m_state, value));
 76                 m_state = value;
 77             }
 78         }
 79
 80         #endregion
 81
 82         #region State
 83
 84         enum NetworkState
 85         {
 86             Connecting,             // 连接中
 87             ConnectedSucceed,       // 连接成功
 88             ConnectedFailed,        // 连接失败
 89
 90             Idle,                   // 空闲
 91
 92             Sending,                // 发送中
 93
 94             Resend,                 // 重发
 95             Resending,              // 重发中
 96
 97             Disconnect,             // 断开连接
 98             Disconnecting,          // 连接断开中
 99         }
100
101         // 阻塞状态管理
102         enum BlockState
103         {
104             Idle,
105             LockScreen,
106             ScreenLocking,
107             TurnFlower,
108             FlowerTurning,
109             EndBlock,
110         }
111
112         #endregion
113
114         #region static
115
116         public static EProtoId? GetProtoId(Type type)
117         {
118             var idProp = type.GetProperty("id");
119             if (idProp == null)
120             {
121                 Debug.LogError("#[Network]idProp==null, type: " + type);
122                 return null;
123             }
124
125             var attributes = idProp.GetCustomAttributes(typeof(DefaultValueAttribute), false);
126             if (attributes == null || attributes.Length <= 0)
127             {
128                 Debug.LogError("#[Network]attributes == null || attributes.Length <= 0, type: " + type);
129                 return null;
130             }
131
132             var attribute = attributes[0] as DefaultValueAttribute;
133             if (attribute == null)
134             {
135                 Debug.LogError("#[Network]attribute==null, type: " + type);
136                 return null;
137             }
138
139             return (EProtoId)attribute.Value;
140         }
141
142         #endregion
143
144         #region 状态机 & 监听回包
145
146         public void Update()
147         {
148             #region 阻塞
149
150             bool blocking = m_forSendingPackets.Count > 0 && State != NetworkState.Disconnecting;
151
152             switch (m_blockState)
153             {
154                 case BlockState.Idle:
155                     if (blocking)
156                         m_blockState = BlockState.LockScreen;
157                     break;
158
159                 case BlockState.LockScreen:
160                     m_blockState = BlockState.ScreenLocking;
161                     m_blockTimer = 0;
162                     if (EventLockScreen != null)
163                         EventLockScreen();
164                     break;
165
166                 case BlockState.ScreenLocking:
167                     if (blocking)
168                     {
169                         m_blockTimer += Time.deltaTime;
170                         if (m_blockTimer >= DurationToTurnFlower)
171                             m_blockState = BlockState.TurnFlower;
172                     }
173                     else
174                         m_blockState = BlockState.EndBlock;
175                     break;
176
177                 case BlockState.TurnFlower:
178                     m_blockState = BlockState.FlowerTurning;
179                     if (EventTurnFlower != null)
180                         EventTurnFlower();
181                     break;
182
183                 case BlockState.FlowerTurning:
184                     if (!blocking)
185                         m_blockState = BlockState.EndBlock;
186                     break;
187
188                 case BlockState.EndBlock:
189                     m_blockState = BlockState.Idle;
190                     if (EventEndBlock != null)
191                         EventEndBlock();
192                     break;
193
194                 default:
195                     throw new InvalidOperationException("Unknown block state: " + m_blockState);
196             }
197
198             #endregion
199
200             #region 状态机
201
202             switch (m_state)
203             {
204                 // 连接
205                 case NetworkState.Connecting:
206                     if (Connected)
207                         State = NetworkState.ConnectedSucceed;
208                     break;
209
210                 case NetworkState.ConnectedSucceed:
211                     {
212                         State = NetworkState.Idle;
213                         if (m_eventConnectedSucceed != null)
214                         {
215                             m_eventConnectedSucceed();
216                             m_eventConnectedSucceed = null;
217                             m_eventConnectedFailed = null;
218                         }
219                     }
220                     break;
221
222                 case NetworkState.ConnectedFailed:
223                     {
224                         State = NetworkState.Disconnect;
225                         if (m_eventConnectedFailed != null)
226                         {
227                             m_eventConnectedFailed();
228                             m_eventConnectedSucceed = null;
229                             m_eventConnectedFailed = null;
230                         }
231                     }
232                     break;
233
234                 // 空闲
235                 case NetworkState.Idle:
236                     {
237                         // 发包
238                         if (m_forSendingPackets.Count > 0)
239                         {
240                             m_sendingTimer = 0;
241                             ReallySend();
242                         }
243
244                         // 重发成功的时机
245                         if (m_resendPacket != null)
246                         {
247                             if (!m_forSendingPackets.Contains(m_resendPacket))      // 表示重发成功
248                             {
249                                 m_resendPacket = null;
250                                 m_resendTimes = 0;
251
252                                 if (m_eventReloginSucceed != null)
253                                 {
254                                     m_eventReloginSucceed();
255                                     m_eventReloginSucceed = null;
256                                 }
257                             }
258                         }
259                     }
260                     break;
261
262                 case NetworkState.Sending:
263                     {
264                         m_sendingTimer += Time.deltaTime;
265                         if (m_sendingTimer > MaxSendingDuration)
266                             State = m_resendTimes < MaxResendTimes ? NetworkState.Resend : NetworkState.Disconnect;
267                     }
268                     break;
269
270                 // 重发
271                 case NetworkState.Resend:
272                     {
273                         State = NetworkState.Resending;
274
275                         m_resendTimes++;
276
277                         // 第一次重连
278                         if (m_resendPacket == null)
279                         {
280                             // 为什么要这样做?因为重连操作,连接成功之后要优先发重登录的包。
281                             m_resendPacket = m_forSendingPackets.FirstOrDefault(p => p.SendWait);
282                             var tempQueue = new Queue<SendingPacket>(m_forSendingPackets);     // 先出队
283                             m_forSendingPackets.Clear();
284                             EventRelogin(() =>
285                             {
286                                 for (int i = 0; i < tempQueue.Count; i++)
287                                     m_forSendingPackets.Enqueue(tempQueue.Dequeue());           // 再入队
288                             });
289                         }
290                         else
291                         {
292                             EventRelogin(null);
293                         }
294
295                         Debug.Log("#[Network]断线重连,重连次数: " + m_resendTimes);
296                     }
297                     break;
298
299                 case NetworkState.Resending:
300                     break;
301
302                 // 断开连接
303                 case NetworkState.Disconnect:
304                     {
305                         m_resendTimes = 0;
306                         State = NetworkState.Disconnecting;
307                         if (EventDisconnect != null)
308                             EventDisconnect(this);
309                     }
310                     break;
311
312                 case NetworkState.Disconnecting:
313                     // 此时应该在弹框中
314                     break;
315
316                 default:
317                     throw new InvalidOperationException("Unknown state: " + m_state);
318             }
319
320             #endregion
321
322             #region 监听处理回包
323
324             // 监听
325             if (Connected)
326             {
327                 if (m_listenInThread == null)
328                     m_listenInThread = new Thread(obj => ListenInReturnPackets());
329
330                 if (m_listenInThread.ThreadState == ThreadState.Unstarted)
331                 {
332                     m_listenInThread.Priority = SystemThreadPriority.AboveNormal;
333                     m_listenInThread.Start();
334                 }
335             }
336             else
337             {
338                 if (m_listenInThread != null && m_listenInThread.IsAlive)
339                     m_listenInThread.Abort();
340             }
341
342             // 处理回包
343             if (m_receivedPackets.Count > 0)
344                 ProcessReturnPackets();
345
346             #endregion
347         }
348
349         #endregion
350
351         #region 连接
352
353         public void Connect(string host, int port)
354         {
355             Connect(host, port, null, null);
356         }
357
358         public void Connect(string host, int port, Action onSucceed, Action onFailed)
359         {
360             if (string.IsNullOrEmpty(host))
361                 throw new ArgumentException("host");
362
363             if (State == NetworkState.Connecting)
364                 return;
365
366             State = NetworkState.Connecting;
367
368             // 断开原有连接
369             Disconnect();
370
371             m_eventConnectedSucceed = onSucceed;
372             m_eventConnectedFailed = onFailed;
373             m_host = host;
374             m_port = port;
375
376             IPAddress[] addresses = null;
377             try
378             {
379                 addresses = Dns.GetHostAddresses(host);
380             }
381             catch (Exception ex)
382             {
383                 Debug.LogError("#[Network]Dns.GetHostAddresses(ip) failed, ex: " + ex);
384             }
385
386             // 是否是ipv6
387             bool isIpv6 = addresses != null && addresses.Length > 0 && addresses[0].AddressFamily == AddressFamily.InterNetworkV6;
388
389             try
390             {
391                 m_tcpClient = isIpv6 ? new TcpClient(AddressFamily.InterNetworkV6) : new TcpClient();
392                 m_tcpClient.BeginConnect(host, port, r =>
393                 {
394                     if (Connected)
395                     {
396                         Debug.Log(string.Format("#[Network]Connect to server succeed, host: {0}, port: {1}, ipv6: {2}", host, port, isIpv6));
397                         State = NetworkState.ConnectedSucceed;
398                     }
399                     else
400                     {
401                         Debug.Log(string.Format("#[Network]Connect to server failed, host: {0}, port: {1}, ipv6: {2}", host, port, isIpv6));
402                         State = NetworkState.ConnectedFailed;
403                     }
404                 }, null);
405             }
406             catch (Exception ex)
407             {
408                 Debug.Log(string.Format("#[Network]Connect to server failed, host: {0}, port: {1}, ipv6: {2}, ex: {2}", host, port, isIpv6, ex));
409                 State = NetworkState.ConnectedFailed;
410             }
411         }
412
413         public void Reconnect()
414         {
415             Reconnect(null, null);
416         }
417
418         public void Reconnect(Action onSucceed, Action onFailed)
419         {
420             if (!string.IsNullOrEmpty(m_host))      // 是否有连接过
421                 Connect(m_host, m_port, onSucceed, onFailed);
422         }
423
424         /// <summary>
425         /// 断线重连
426         /// </summary>
427         public void Relogin(Action callback)
428         {
429             State = NetworkState.Resend;
430             m_eventReloginSucceed = callback;
431         }
432
433         /// <summary>
434         /// 断开连接
435         /// </summary>
436         public void Disconnect()
437         {
438             // close socket
439             if (Connected)
440             {
441                 try
442                 {
443                     m_tcpClient.GetStream().Close();
444                     m_tcpClient.Close();
445                 }
446                 catch (Exception ex)
447                 {
448                     Debug.LogError("#[Network]Error when disconnect socket, ex: " + ex);
449                     m_tcpClient.Close();
450                 }
451             }
452             m_tcpClient = null;
453
454             if (m_listenInThread != null && m_listenInThread.IsAlive)
455                 m_listenInThread.Abort();
456             m_listenInThread = null;
457         }
458
459         public void SetToNoDelayMode()
460         {
461             if (m_tcpClient != null)
462             {
463                 m_tcpClient.NoDelay = true;
464                 m_tcpClient.ReceiveBufferSize = 32768;
465                 m_tcpClient.SendBufferSize = 32768;
466             }
467         }
468
469         #endregion
470
471         #region 发包
472
473         // 发包,为兼容老接口。
474         public bool Send<T>(T obj, NetworkDataType option = NetworkDataType.SendOnly, EProtoId? returnId = null)
475             where T : class
476         {
477             if (option == NetworkDataType.SendWait)
478             {
479                 var sendId = GetProtoId(typeof(T));
480                 if (sendId == null)
481                 {
482                     Debug.LogError(string.Format("#[Network]Send failed, cann‘t get EProtoId, send type: {0}.", typeof(T)));
483                     return false;
484                 }
485
486                 Debug.Log("#[Network]SendWait: " + sendId.Value);
487
488                 var p = new SendingPacket(sendId.Value, obj)
489                 {
490                     SendWait = true,
491                     ExpectedReturnId = returnId,
492                     OnPacketReturn = ret =>
493                     {
494                         NCConfig.OnReceiveMessageHandler callback;
495                         if (NCConfig.dictCallback.TryGetValue(ret.ProtoId, out callback))
496                         {
497                             var retObj = NCConfigUnpack.GetProtocolObject(ret.ProtoId, ret.Body);
498                             callback(retObj);
499                         }
500                     }
501                 };
502
503                 m_forSendingPackets.Enqueue(p);
504                 return true;
505             }
506             else
507             {
508                 return SendOnly<T>(obj);
509             }
510         }
511
512         // 发包,等待回包模式
513         public bool SendWait<T1, T2>(T1 obj, Action<T2> onReturn)
514             where T1 : class
515             where T2 : class
516         {
517             var sendId = GetProtoId(typeof(T1));
518             var returnId = GetProtoId(typeof(T2));
519             if (sendId == null || returnId == null)
520             {
521                 Debug.LogError(string.Format("#[Network]Send wait failed, cann‘t get EProtoId, send type: {0}, return type: {1}", typeof(T1), typeof(T2)));
522                 return false;
523             }
524
525             Debug.Log("#[Network]SendWait: " + sendId.Value);
526
527             var p = new SendingPacket(sendId.Value, obj);
528             p.SendWait = true;
529             p.ExpectedReturnId = returnId.Value;
530             p.OnPacketReturn = (ret) =>
531             {
532                 var stream = new MemoryStream(ret.Body);
533                 var t2Obj = Serializer.Deserialize<T2>(stream);
534                 if (t2Obj != null)
535                     onReturn(t2Obj);
536                 else
537                     Debug.LogError("#[Network]Cann‘t convert return packet to type: " + typeof(T2));
538             };
539             m_forSendingPackets.Enqueue(p);
540             return true;
541         }
542
543         // 发包,只发送,不等待回包模式
544         public bool SendOnly<T>(T obj) where T : class
545         {
546             var protoId = GetProtoId(typeof(T));
547             if (protoId == null)
548             {
549                 Debug.LogError("#[Network]Send only failed, cann‘t get EProtoId, type: " + typeof(T));
550                 return false;
551             }
552
553             Debug.Log("#[Network]SendOnly: " + protoId.Value);
554
555             var p = new SendingPacket(protoId.Value, obj);
556             p.SendWait = false;
557             m_forSendingPackets.Enqueue(p);
558             return true;
559         }
560
561         void ReallySend()
562         {
563             if (!Connected)
564             {
565                 Reconnect();
566                 return;
567             }
568
569             var p = m_forSendingPackets.Peek();
570             if (p == null)
571                 throw new ArgumentNullException("p");
572
573             try
574             {
575                 var buffer = m_tcpClient.GetStream();
576                 if (buffer.CanWrite)
577                 {
578                     p.UpdateSequenceId();
579                     var bytes = p.GetBytes();
580                     buffer.Write(bytes, 0, bytes.Length);
581                     Debug.Log(string.Format("#[Network]Really send data, protoId: {0}, send wait: {1}.", p.ProtoId, p.SendWait));
582
583                     if (p.SendWait)
584                     {
585                         m_sendingPacket = p;
586                         State = NetworkState.Sending;
587                     }
588                     else
589                     {
590                         m_sendingPacket = null;
591                         m_forSendingPackets.Dequeue();
592                         State = NetworkState.Idle;
593                     }
594                 }
595                 else
596                 {
597                     Debug.LogError("#[Network]!buffer.CanWrite");
598                 }
599             }
600             catch (Exception ex)
601             {
602                 Debug.LogError("#[Network]Send failed!!! ex: " + ex);
603             }
604         }
605
606         #endregion
607
608         #region 收包
609
610         // 监听回包
611         void ListenInReturnPackets()
612         {
613             while (Connected)
614             {
615                 try
616                 {
617                     var buffer = m_tcpClient.GetStream();
618                     if (buffer.CanRead)
619                     {
620                         int len = buffer.Read(m_receivedBuffer, 0, m_receivedBuffer.Length);
621                         if (len > 0)
622                         {
623                             m_unpackTool.UnpackData(m_receivedBuffer, len, p =>
624                             {
625                                 if (p == null)
626                                     throw new ArgumentNullException("p");
627
628                                 Debug.Log("#[Network]Received packet: " + p.ProtoId);
629
630                                 lock (this)
631                                     m_receivedPackets.Enqueue(p);
632                             });
633                         }
634                     }
635                     else
636                         Debug.LogError("#[Network]!buffer.CanRead");
637
638                     Thread.Sleep(100);
639                 }
640                 catch (Exception ex)
641                 {
642                     Debug.LogError("#[Network]Read return packet failed, Exception:\n" + ex);
643                 }
644             }
645         }
646
647         void ProcessReturnPackets()
648         {
649             if (m_receivedPackets.Count <= 0)
650                 return;
651
652             ReturnPacket ret;
653             lock (this)
654                 ret = m_receivedPackets.Dequeue();
655
656             if (ret == null)
657                 return;
658
659             if (ret.ProtoId == 0)                                 // 心跳包
660                 ProcessHeartBeat(ret);
661             else if (ret.ProtoId == EProtoId.ERROR_CODE_S)        // 错误码
662             {
663                 // [dev]
664                 //MemoryStream buffer = new MemoryStream(ret.Body);
665                 //var errorCode = Serializer.Deserialize<MessageErrorCode>(buffer);
666                 //Debug.Log(string.Format("#[Network]Error code: {0}", errorCode.code));
667             }
668             else                                                // 正常的包
669             {
670                 bool isSendWait = m_sendingPacket != null && (m_sendingPacket.ExpectedReturnId == null || m_sendingPacket.ExpectedReturnId == ret.ProtoId);
671                 if (isSendWait)                                                                         // 为客户端主动发,等待反馈的情况
672                 {
673                     if (m_sendingPacket.OnPacketReturn != null)
674                         m_sendingPacket.OnPacketReturn(ret);
675                     m_sendingPacket = null;
676                     m_forSendingPackets.Dequeue();
677                     State = NetworkState.Idle;
678                 }
679                 else if (NCConfig.dictCallback.ContainsKey(ret.ProtoId))                                  // 可能为服务器主动推的情况
680                 {
681                     var callback = NCConfig.dictCallback[ret.ProtoId];
682                     var retObj = NCConfigUnpack.GetProtocolObject(ret.ProtoId, ret.Body);
683                     callback(retObj);
684                 }
685                 else
686                 {
687                     Debug.LogError("#[Network]Unprocessed return packet, protoId: " + ret.ProtoId);
688                 }
689             }
690         }
691
692         void ProcessHeartBeat(ReturnPacket p)
693         {
694             if (p.ProtoId != 0)
695                 throw new ArgumentException("p.ProtoId != 0");
696
697             byte[] bytes = new byte[6] { 0x2, 0x0, 0x0, 0x0, 0x0, 0x0 };
698
699             try
700             {
701                 var buffer = m_tcpClient.GetStream();
702                 if (buffer.CanWrite)
703                 {
704                     Debug.Log("#[Network]Send heart beat");
705                     buffer.Write(bytes, 0, 6);
706                 }
707                 else
708                 {
709                     Debug.LogError("#[Network]!buffer.CanWrite");
710                 }
711             }
712             catch (Exception ex)
713             {
714                 Debug.LogError("#[Network]Send heart beat failed, ex: " + ex);
715             }
716         }
717
718         #endregion
719     }
720 }

NetworkSocket

转载请注明出处: http://www.cnblogs.com/jietian331/p/5726332.html

时间: 2024-10-14 12:07:34

网络游戏客户端通信模块简单实现的相关文章

RabbitMQ的安装与客户端的简单实用

本文主要内容是RabbitMQ的安装步骤[Windows系统与linux上的安装]及客户端的简单使用. 1.下载 下载地址:http://www.rabbitmq.com/download.html 2.Windows上安装 2.1 安装安装Erlang 下载erlang:http://www.erlang.org/download/otp_win64_17.3.exe 安装: erlang安装完成. 2.2 安装安装RabbitMQ RabbitMQ安装完成. 启动.停止.重新安装等. 2.3

redis的java客户端Jedis简单封装

经过我们团队的一番讨论,最终决定使用redis来进行我们的业务缓存.redis会将数据缓存到内存中,运行效率会很快.同时异步将数据写入到磁盘中,进行持久化. 且redis支持主从同步,支持分布式部署,支持N多数据结构,这对于我们有着莫大的吸引力. 参见:http://blog.csdn.net/yichenlian/article/details/27207383 我们团队讨论的焦点是在于redis的灾备恢复问题.由于redis的持久化是异步的,总会有一点时间内存中数据和磁盘数据不同步的情况(当

webservice用wsdl2java来生成客户端(操作简单方便)

打开Eclipse,Run--> Run Configurations,在Main class框里 输入 WSDL2Java 进行搜索,前提是你的工程里已加入axis的jar包, 会搜索到org.apache.axis.wsdl.WSDL2Java,在arguments标签栏里输入参数,默认基础目录为当前工程,参数如下所示: src\\cfg\\test.wsdl -p com.xxx.xxx.xxx.client -t 这是wsdl文件在本地的情况,如果不是本地的wsdl文件,那么而是网址方式

EJB对象的部署及客户端调用简单示例

一,EJB对象的写法及部署 1,新建一个EJB Project,在包里加入接口及实现类: 实现类通常以Bean结尾,并且通过注解方式指定EJB类型: 之后,部署到JBoss服务器上. 二,客户端调用 1,将EJB项目中的接口类打包,并把这个jar包加入到client项目中. 2,将JBOSS安装目录下的client目录里面的所有jar包加到客户端项目中 3,加入jndi.properties文件,将地址配置为EJB项目的发布地址 注意:这个properties文件最好反正根目录下,不然可能会产生

webService服务端和客户端开发 简单实例

这几天一直在看webService相关的知识. webService是一个跨平台访问的第三方插件,方便易用,多平台使用. 开发中我们可能自己是服务端,提供webService接口给别人访问我们的系统:也有可能我们调用别人的webService接口去访问别人的系统(比如查询天气预报). 下面是服务端和客户端的开发,实际开发中我们只是其中一方. 服务端开发: ①创建一个服务类,运行main方法发布即可,服务端就开发完成了. package com.lijianbo.service; import j

thrift服务端到客户端开发简单示例

(1)首先我们在服务器端写个helloworld.thrift文件,如下所示: service HelloWorld{ string ping(1: string name), string getpng(), } (2)在服务器端编译helloworld.thrift编译helloworld.thrift文件,会产生服务器端和客户端相应语言的接口源码./usr/local/thrift/bin/thrift -r --gen py helloworld.thrift /usr/local/th

基于C/S架构的3D对战网络游戏C++框架 _01服务器端与客户端需求分析

本系列博客主要是以对战游戏为背景介绍3D对战网络游戏常用的开发技术以及C++高级编程技巧,有了这些知识,就可以开发出中小型游戏项目或3D工业仿真项目. 笔者将分为以下三个部分向大家介绍(每日更新): 1.实现基本通信框架,包括对游戏的需求分析.设计及开发环境和通信框架的搭建: 2.实现网络底层操作,包括创建线程池.序列化网络包等: 3.实战演练,实现类似于CS反恐精英的3D对战网络游戏: 技术要点:C++面向对象思想.网络编程.Qt界面开发.Qt控件知识.Boost智能指针.STL算法.STL.

IdentityServer3——入门教程:创建简单的OAuth2.0服务器,客户端和API

本教程的目的在于创造尽可能简单的identityserver安装作为一个oauth2授权服务器.这应该能够让你了解一些基本功能和配置选项(完整的源代码可以发现在这里).在后面的文档中会介绍更多的高级功能.本教程包括: 创建一个自托管identityserver 设置为使用一个应用程序的帐户以及用户对通信应用的客户服务代表 注册一个API 请求访问令牌 调用API 验证一个访问令牌 创建一个授权服务器(IdentityServer3) 创建一个控制台应用程序,并且在程序包管理器控制台中输入 ins

C# 编写WCF简单的服务端与客户端

http://www.wxzzz.com/1860.html Windows Communication Foundation(WCF)是由微软开发的一系列支持数据通信的应用程序框架,可以翻译为Windows 通讯开发平台.整合了原有的windows通讯的 .net Remoting,WebService,Socket的机制,并融合有HTTP和FTP的相关技术.是Windows平台上开发分布式应用最佳的实践方式. 今天带如何一步一步实现WCF服务端与客户端的开发及基础讲解. 一.在Visual