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;
}
}
}