snakealpha
4/23/2014 - 4:59 AM

A google protobuf message manager in C#. Designed for network game and you shall edit NetworkManager class to fit your own needs. Can be us

A google protobuf message manager in C#. Designed for network game and you shall edit NetworkManager class to fit your own needs. Can be used in Unity3D. Note that most commits are in Chinese. Note that Connection_socket.cs is a version that use Socket instead of TcpClient. While TcpClient may cause some exceptions while compiling in Unity3D using Windows Phone 8 as target platform.

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;

using Elecelf.ProtobufToolkits;

namespace Elecelf.ProtobufToolkits
{
    /// <summary>
    /// A network message manager to 
    /// </summary>
    /// <typeparam name="T">消息的全局类型</typeparam>
    public class NetworkManager<T> where T:ProtoBuf.IExtensible
    {
        protected Connection<T> gameServerConnection;
        protected Connection<T> accountServerConnection;

        protected IPEndPoint gameServerEndPoint;
        protected IPEndPoint accountServerEndPoint;

        protected MessageDispatcher<T> gameServerMessageDispatcher = new MessageDispatcher<T>();
        protected MessageDispatcher<T> accountServerMessageDispatcher = new MessageDispatcher<T>();

        public Connection<T> GameServerConnection
        {
            get
            {
                return gameServerConnection;
            }
        }

        public Connection<T> AccountServerConnnection
        {
            get
            {
                return accountServerConnection;
            }
        }

        public MessageDispatcher<T> GameServerMessageDispatcher
        {
            get
            {
                return gameServerMessageDispatcher;
            }
        }

        public MessageDispatcher<T> AccountServerMessageDispatcher
        {
            get
            {
                return accountServerMessageDispatcher;
            }
        }

        public IPEndPoint GameServerEndPoint
        {
            get
            {
                return gameServerEndPoint;
            }
            set
            {
                gameServerEndPoint = value;
            }
        }

        public IPEndPoint AccountServerEndPoint
        {
            get
            {
                return accountServerEndPoint;
            }
            set
            {
                accountServerEndPoint = value;
            }
        }
    }
}
using System;
using System.Collections.Generic;

using ProtoBuf;

namespace Elecelf.ProtobufToolkits
{
    /// <summary>
    /// This class is to manager the events that will be used in a game.
    /// 
    /// Author: Elecelf. Snake Liu
    /// Log:    Apr.1 2014  Create.
    /// </summary>
    public class MessageDispatcher<T> where T : ProtoBuf.IExtensible
    {
        public delegate void MessageProcesser(T message);

        protected Dictionary<int, MessageProcesser> routeMap = new Dictionary<int, MessageProcesser>();

        public Dictionary<int, MessageProcesser> RouteMap
        {
            get
            {
                return routeMap;
            }
        }

        public void Add(int msgId, MessageProcesser processer)
        {
            MessageProcesser hookee;
            if(!routeMap.TryGetValue(msgId, out hookee))
            {
                routeMap[msgId] = processer;
            }
            else
            {
                hookee += processer;
            }
        }

        public void Remove(int msgId, MessageProcesser processer)
        {
            MessageProcesser hookee;
            if(routeMap.TryGetValue(msgId, out hookee))
            {
                hookee -= processer;
            }
        }

        public void Trigger(T msg, int msgId)
        {
            MessageProcesser hookee;
            if(routeMap.TryGetValue(msgId, out hookee))
            {
                hookee.Invoke(msg);
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;

using ProtoBuf;

namespace Elecelf.ProtobufToolkits
{
    /// <summary>
    /// Connection类是一个Socket的封装,用于管理服务器连接及序列化/反序列化Protobuf-net消息。
    /// 因该类设计为适用于Unity3D环境,故依据.Net Framework 3.5环境建造。
    /// </summary>
    /// <typeparam name="T">该连接的载体类型。所有收到或发送的Protobuf消息都将依据该类型进行序列化或反序列化。</typeparam>
    public class Connection<T> where T : ProtoBuf.IExtensible
    {
        /// <summary>
        /// 完成建立连接工作之后触发此事件。
        /// </summary>
        public event EventHandler OnConnected;

        /// <summary>
        /// 连接因任何原因被断开后触发此事件。
        /// </summary>
        public event EventHandler OnDisconnected;

        /// <summary>
        /// 连接获得服务器消息之后该事件将会被触发。
        /// </summary>
        public event EventHandler<ProtobufEventArgs<T>> OnMessageReceived;

        /// <summary>
        /// 消息发送完成之后该事件将会被触发。
        /// </summary>
        public event EventHandler<ProtobufEventArgs<T>> OnMessageSent;

        protected Socket client/* = new TcpClient()*/;

        protected IPEndPoint endPoint;

        protected ProtocolType protocolType = ProtocolType.Tcp;

        /// <summary>
        /// 网络流对象。
        /// </summary>
        protected NetworkStream stream;

        /// <summary>
        /// 接收消息的缓存
        /// </summary>
        protected byte[] receiveBuff = new byte[1024];

        /// <summary>
        /// 连接的对象。
        /// </summary>
        public IPEndPoint EndPoint
        {
            get
            {
                return endPoint;
            }
        }

        /// <summary>
        /// 指示连接关联的套接字是否已连接
        /// </summary>
        public bool Connected
        {
            get
            {
                return client != null && client.Connected;
            }
        }

        /// <summary>
        /// 初始化Connection对象。注意,初始化的过程中Connection对象不会连接target参数指定的服务器。
        /// </summary>
        /// <param name="target">指定提供服务的服务器地址及端口号。</param>
        public Connection(IPEndPoint target, ProtocolType protocolType = ProtocolType.Tcp)
        {
            client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, protocolType);

            this.protocolType = protocolType;

            endPoint = target;
        }

        /// <summary>
        /// 与服务器建立连接。
        /// 连接建立之后将会初始化网络流对象。
        /// </summary>
        public virtual void Connect()
        {
            if (client == null)
            {
                client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, protocolType);
            }

            client.BeginConnect(endPoint.Address, endPoint.Port, OnClientConnected, client);
        }

        protected void OnClientConnected(IAsyncResult ar)
        {
            stream = new NetworkStream(client);

            if (OnConnected != null)
            {
                OnConnected(this, new EventArgs());
            }

            StartListen();
        }

        #region 监听服务端消息的方法链

        /// <summary>
        /// 开始对protobuf消息的监听,首先监听长度。
        /// </summary>
        protected void StartListen()
        {
            byte[] lengthBuff = new byte[2];
            stream.BeginRead(lengthBuff, 0, 2, OnMessageLengthRead, lengthBuff);
        }

        /// <summary>
        /// 收到长度之后,依据长度开始监听包体
        /// </summary>
        /// <param name="ar"></param>
        protected void OnMessageLengthRead(IAsyncResult ar)
        {
            byte[] lengthBuff = ar.AsyncState as byte[];

            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(lengthBuff);
            }

            if (stream == null)
            {
                return;
            }

            int messageLength = BitConverter.ToUInt16(lengthBuff, 0);
            stream.BeginRead(receiveBuff, 0, messageLength - 2, OnMessageRead, messageLength - 2);
        }

        /// <summary>
        /// 收到消息体之后,将消息体反序列化。
        /// </summary>
        /// <param name="ar"></param>
        protected void OnMessageRead(IAsyncResult ar)
        {
            using (MemoryStream outputStream = new MemoryStream(receiveBuff))
            {
                outputStream.SetLength((int)ar.AsyncState);

                T message = Serializer.Deserialize<T>(outputStream);

                if (OnMessageReceived != null)
                {
                    OnMessageReceived(this, new ProtobufEventArgs<T>(message));
                }
            }

            if (stream == null)
            {
                return;
            }

            //开启下一个监听
            StartListen();
        }

        #endregion

        /// <summary>
        /// 断开服务器连接。
        /// 将导致套接字被回收。
        /// </summary>
        public virtual void Disconnnect()
        {
            client.Close();
            client = null;

            stream.Close();
            stream.Dispose();
            stream = null;

            if (OnDisconnected != null)
            {
                OnDisconnected(this, new EventArgs());
            }
            OnDisconnected = null;
        }

        /// <summary>
        /// 发送消息。
        /// </summary>
        /// <param name="message">需要发送的消息。</param>
        public virtual void Send(T message)
        {
            using (MemoryStream messageStream = new MemoryStream())
            using (MemoryStream sendStream = new MemoryStream())
            {
                Serializer.Serialize<T>(messageStream, message);

                // Length should be send as Big-Endian
                byte[] lengthBuff = BitConverter.GetBytes(Convert.ToUInt16(messageStream.Length + 2));
                if (BitConverter.IsLittleEndian)
                {
                    Array.Reverse(lengthBuff);
                }

                sendStream.Write(lengthBuff, 0, lengthBuff.Length);
                messageStream.WriteTo(sendStream);
                sendStream.WriteTo(stream);
            }

            if (OnMessageSent != null)
                OnMessageSent(this, new ProtobufEventArgs<T>(message));
        }
    }

    /// <summary>
    /// 需要传递消息内容的事件参数类型。
    /// </summary>
    /// <typeparam name="T">消息载体类型。所有收到或发送的Protobuf消息都将依据该类型进行序列化或反序列化。注意,C# 3.5尚不支持逆变,因此不应继承此类。</typeparam>
    public sealed class ProtobufEventArgs<T> : EventArgs where T : ProtoBuf.IExtensible
    {
        private T message;

        /// <summary>
        /// 与该事件相关的消息内容。
        /// </summary>
        public T Message
        {
            get
            {
                return message;
            }
        }

        /// <summary>
        /// 使用消息对象初始化示例。
        /// </summary>
        /// <param name="message">相关的消息内容。</param>
        public ProtobufEventArgs(T message)
        {
            this.message = message;
        }
    }
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;

using ProtoBuf;

namespace Elecelf.ProtobufToolkits
{
    /// <summary>
    /// Connection类是一个Socket的封装,用于管理服务器连接及序列化/反序列化Protobuf-net消息。
    /// 因该类设计为适用于Unity3D环境,故依据.Net Framework 3.5环境建造。
    /// </summary>
    /// <typeparam name="T">该连接的载体类型。所有收到或发送的Protobuf消息都将依据该类型进行序列化或反序列化。</typeparam>
    public class Connection<T> where T : ProtoBuf.IExtensible
    {
        /// <summary>
        /// 完成建立连接工作之后触发此事件。
        /// </summary>
        public event EventHandler OnConnected;

        /// <summary>
        /// 连接因任何原因被断开后触发此事件。
        /// </summary>
        public event EventHandler OnDisconnected;

        /// <summary>
        /// 连接获得服务器消息之后该事件将会被触发。
        /// </summary>
        public event EventHandler<ProtobufEventArgs<T>> OnMessageReceived;

        /// <summary>
        /// 消息发送完成之后该事件将会被触发。
        /// </summary>
        public event EventHandler<ProtobufEventArgs<T>> OnMessageSent;

        protected TcpClient client = new TcpClient();

        protected IPEndPoint endPoint;

        /// <summary>
        /// 网络流对象。
        /// </summary>
        protected NetworkStream stream;

        /// <summary>
        /// 接收消息的缓存
        /// </summary>
        protected byte[] receiveBuff = new byte[1024];

        /// <summary>
        /// 连接的对象。
        /// </summary>
        public IPEndPoint EndPoint
        {
            get
            {
                return endPoint;
            }
        }

        /// <summary>
        /// 指示连接关联的套接字是否已连接
        /// </summary>
        public bool Connected
        {
            get
            {
                return client != null && client.Connected;
            }
        }

        /// <summary>
        /// 初始化Connection对象。注意,初始化的过程中Connection对象不会连接target参数指定的服务器。
        /// </summary>
        /// <param name="target">指定提供服务的服务器地址及端口号。</param>
        public Connection(IPEndPoint target)
        {
            endPoint = target;
        }

        /// <summary>
        /// 与服务器建立连接。
        /// 连接建立之后将会初始化网络流对象。
        /// </summary>
        public virtual void Connect()
        {
            if (client == null)
            {
                client = new TcpClient();
            }

            client.BeginConnect(endPoint.Address, endPoint.Port, OnClientConnected, client);
        }

        protected void OnClientConnected(IAsyncResult ar)
        {
            stream = client.GetStream();

            if (OnConnected != null)
            {
                OnConnected(this, new EventArgs());
            }

            StartListen();
        }

        #region 监听服务端消息的方法链

        /// <summary>
        /// 开始对protobuf消息的监听,首先监听长度。
        /// </summary>
        protected void StartListen()
        {
            byte[] lengthBuff = new byte[2];
            stream.BeginRead(lengthBuff, 0, 2, OnMessageLengthRead, lengthBuff);
        }

        /// <summary>
        /// 收到长度之后,依据长度开始监听包体
        /// </summary>
        /// <param name="ar"></param>
        protected void OnMessageLengthRead(IAsyncResult ar)
        {
            byte[] lengthBuff = ar.AsyncState as byte[];

            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(lengthBuff);
            }

            if(stream == null)
            {
                return;
            }

            int messageLength = BitConverter.ToUInt16(lengthBuff, 0);
            stream.BeginRead(receiveBuff, 0, messageLength - 2, OnMessageRead, messageLength - 2);
        }

        /// <summary>
        /// 收到消息体之后,将消息体反序列化。
        /// </summary>
        /// <param name="ar"></param>
        protected void OnMessageRead(IAsyncResult ar)
        {
            using (MemoryStream outputStream = new MemoryStream(receiveBuff))
            {
                outputStream.SetLength((int)ar.AsyncState);

                T message = Serializer.Deserialize<T>(outputStream);

                if (OnMessageReceived != null)
                {
                    OnMessageReceived(this, new ProtobufEventArgs<T>(message));
                }
            }

            if(stream == null)
            {
                return;
            }

            //开启下一个监听
            StartListen();
        }

        #endregion

        /// <summary>
        /// 断开服务器连接。
        /// 将导致套接字被回收。
        /// </summary>
        public virtual void Disconnnect()
        {
            client.Close();
            client = null;

            stream.Close();
            stream.Dispose();
            stream = null;

            if (OnDisconnected != null)
            {
                OnDisconnected(this, new EventArgs());
            }
            OnDisconnected = null;
        }

        /// <summary>
        /// 发送消息。
        /// </summary>
        /// <param name="message">需要发送的消息。</param>
        public virtual void Send(T message)
        {
            using (MemoryStream messageStream = new MemoryStream())
            using (MemoryStream sendStream = new MemoryStream())
            {
                Serializer.Serialize<T>(messageStream, message);

                // Length should be send as Big-Endian
                byte[] lengthBuff = BitConverter.GetBytes(Convert.ToUInt16(messageStream.Length + 2));
                if (BitConverter.IsLittleEndian)
                {
                    Array.Reverse(lengthBuff);
                }

                sendStream.Write(lengthBuff, 0, lengthBuff.Length);
                messageStream.WriteTo(sendStream);
                sendStream.WriteTo(stream);
            }

            if (OnMessageSent != null)
                OnMessageSent(this, new ProtobufEventArgs<T>(message));
        }
    }

    /// <summary>
    /// 需要传递消息内容的事件参数类型。
    /// </summary>
    /// <typeparam name="T">消息载体类型。所有收到或发送的Protobuf消息都将依据该类型进行序列化或反序列化。注意,C# 3.5尚不支持逆变,因此不应继承此类。</typeparam>
    public sealed class ProtobufEventArgs<T> : EventArgs where T : ProtoBuf.IExtensible
    {
        private T message;

        /// <summary>
        /// 与该事件相关的消息内容。
        /// </summary>
        public T Message
        {
            get
            {
                return message;
            }
        }

        /// <summary>
        /// 使用消息对象初始化示例。
        /// </summary>
        /// <param name="message">相关的消息内容。</param>
        public ProtobufEventArgs(T message)
        {
            this.message = message;
        }
    }
}