关键词:
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;
Android 中的谷歌云消息服务如何工作?
】Android中的谷歌云消息服务如何工作?【英文标题】:HowGoogleCloudMessagingServiceinAndroidWorks?【发布时间】:2012-06-2809:32:31【问题描述】:我想在我的应用程序中使用GCM服务。我参考了以下链接(AndroidDeveloper:GoogleGCM),但无法在我的E... 查看详情
在 C++ 中更改现有 protobuf 消息的元素
】在C++中更改现有protobuf消息的元素【英文标题】:ChanginganelementofanexistingprotobufmessageinC++【发布时间】:2017-02-1510:53:12【问题描述】:我只是想知道,为什么没有人解决我最近遇到的与googleprotobufs相关的问题,但是经过广泛的谷... 查看详情
带有谷歌时间戳的 Protobuf C++ 消息导致段错误 [重复]
】带有谷歌时间戳的ProtobufC++消息导致段错误[重复]【英文标题】:ProtobufC++messagewithgoogletimestampleadstosegfault[duplicate]【发布时间】:2019-07-1515:10:04【问题描述】:我是使用googleprotobuffers的新手,我创建了一条基本消息:messagemsguint... 查看详情
iOS 中的谷歌分析(不工作)
】iOS中的谷歌分析(不工作)【英文标题】:GoogleAnalyticsiniOS(NotWorking)【发布时间】:2013-09-1613:44:11【问题描述】:我正在尝试实施谷歌分析..你们能帮帮我吗-(void)setGoogleAnalytics//Initializetracker.self.tracker=[[GAIsharedInstance]trackerWithNam... 查看详情
Android 中的谷歌翻译
】Android中的谷歌翻译【英文标题】:GoogleTranslatorinAndroid【发布时间】:2011-02-1216:15:45【问题描述】:我代码遵守http://android-er.blogspot.com/2009/10/multi-language-translate.html它会强制关闭privatevoidshowLang()db.open();cursor=db.getLang();cursor.moveT 查看详情
Wordpress 中的谷歌广告
】Wordpress中的谷歌广告【英文标题】:GoogleadsinWordpress【发布时间】:2013-07-2206:47:26【问题描述】:一年前我在Wordpress中创建了一个新主题。现在我正在根据要求进行更新。我在这个系统中使用了Navayan订阅。我在那个地方(前端... 查看详情
谷歌标签管理器中的 cookiebot 同意模式
】谷歌标签管理器中的cookiebot同意模式【英文标题】:cookiebotingoogletagmanagerwithconsentmode【发布时间】:2021-09-3004:09:30【问题描述】:我正在尝试使用Google跟踪代码管理器和Google的同意模式来实施CookiebotCMP。我按照https://support.cooki... 查看详情
安卓中的谷歌 AppEngine
】安卓中的谷歌AppEngine【英文标题】:GoogleAppEngineinandroid【发布时间】:2014-02-1810:51:08【问题描述】:我是GCM新手,请指导我使用GoogleAppEngine。我想创建一个注册页面,在Google服务器上注册用户的详细信息注册并登录帐户有可能... 查看详情
wordpress 中的谷歌地图
】wordpress中的谷歌地图【英文标题】:Googlemapinwordpress【发布时间】:2015-05-0118:12:35【问题描述】:我想做这样的网站。http://sf.eater.com/maps/the-38-essential-san-francisco-restaurants-january-2015我没有任何Google地图API经验。我在哪里可以获... 查看详情
jquery mobile中的谷歌地图
】jquerymobile中的谷歌地图【英文标题】:googlemapsinjquerymobile【发布时间】:2011-06-0205:25:03【问题描述】:当在jquerymobile中显示谷歌地图时(阅读论坛后),需要如下代码:<divdata-role="page"data-theme="b"class="page-map"style="width:100%;heig... 查看详情
BigQuery 中的谷歌分析实时数据
】BigQuery中的谷歌分析实时数据【英文标题】:GoogleanalyticsrealtimedatainBigQuery【发布时间】:2018-06-0711:12:48【问题描述】:我们启用了continuousexportofGoogleAnalyticsdatatoBigQuery,这意味着我们会得到ga_realtime_sessions_YYYYMMDD全天带有数据... 查看详情
安卓中的谷歌广告词
】安卓中的谷歌广告词【英文标题】:GoogleAdwordsinandroid【发布时间】:2011-02-2810:00:38【问题描述】:您好..我需要在android中做GoogleAdwords。请参阅链接:https://adwords.google.com/o/Targeting/Explorer?_u=1355887856&_c=6610511936&ideaRequestType=... 查看详情
谷歌标签管理器中的adwords转换代码
】谷歌标签管理器中的adwords转换代码【英文标题】:adwordsconversioncodeingoogletagmanager【发布时间】:2014-09-0809:27:33【问题描述】:我有这样的广告词转换代码:<!--GoogleCodeforXXXXRegistrationFormConversionPage--><scripttype="text/javascript"... 查看详情
将谷歌表格加载到仪表板中的谷歌图表
】将谷歌表格加载到仪表板中的谷歌图表【英文标题】:LoadinggooglesheetstogooglechartsinDashboard【发布时间】:2018-03-0521:06:12【问题描述】:我在仪表板上显示Google表格信息时遇到了问题。x和y轴标签显示为Generalxxx。数据来自here。var... 查看详情
sencha 中的谷歌地图处理
】sencha中的谷歌地图处理【英文标题】:Googlemaphandlinginsencha【发布时间】:2013-09-3014:05:59【问题描述】:我已经在基于sencha的应用程序中实现了地图功能。地图正在显示,但无法放大或缩小,也无法向其他方向移动。它在图像... 查看详情
如何从ios中的谷歌帐户注销?
】如何从ios中的谷歌帐户注销?【英文标题】:Howtologoutfromgoogleaccountinios?【发布时间】:2016-11-2915:23:51【问题描述】:在我的应用程序中,我可以选择使用谷歌登录来登录应用程序。登录工作正常。单击注销按钮后,我无法从... 查看详情
Flutter 中的谷歌地图导航
】Flutter中的谷歌地图导航【英文标题】:GoogleMapNavigationinFlutter【发布时间】:2018-12-2214:16:38【问题描述】:我是Flutter新手。我发现Flutter不支持内联地图,只支持静态地图视图。我想构建一个应用程序,我需要在地图上显示行... 查看详情
wordpress中的谷歌分析
Codeindiefunctions.phpeinfügen<?phpadd_action('wp_footer','add_googleanalytics');functionadd_googleanalytics(){?>//PasteyourGoogleAnalyticscodefromStep6here<?php}?> 查看详情