Просмотр исходного кода

1.ConfigAttribute ServerType改成或操作的形式
2.实现异步Rpc功能

tanghai 10 лет назад
Родитель
Сommit
fdf13d0cbf
29 измененных файлов с 333 добавлено и 53 удалено
  1. 1 1
      CSharp/Game/Controller/ConfigCategory/BuffCategory.cs
  2. 1 1
      CSharp/Game/Controller/ConfigCategory/GlobalCategory.cs
  3. 3 4
      CSharp/Game/Controller/Controller.csproj
  4. 0 12
      CSharp/Game/Controller/Event/AfterAddBuff.cs
  5. 2 2
      CSharp/Game/Controller/Event/BuffTimeoutEvent.cs
  6. 12 0
      CSharp/Game/Controller/Event/GateMessageEvent.cs
  7. 2 2
      CSharp/Game/Controller/Event/MessageEvent.cs
  8. 1 1
      CSharp/Game/Controller/Message/CMsgLoginEvent.cs
  9. 4 8
      CSharp/Game/Model/AEventAttribute.cs
  10. 2 2
      CSharp/Game/Model/Buff.cs
  11. 76 0
      CSharp/Game/Model/Component/GateNetworkComponent.cs
  12. 11 1
      CSharp/Game/Model/Component/NetworkComponent.cs
  13. 10 0
      CSharp/Game/Model/Component/RpcComponent.cs
  14. 4 7
      CSharp/Game/Model/ConfigAttribute.cs
  15. 1 1
      CSharp/Game/Model/EventAttribute.cs
  16. 4 2
      CSharp/Game/Model/EventType.cs
  17. 1 1
      CSharp/Game/Model/MessageAttribute.cs
  18. 1 1
      CSharp/Game/Model/MessageType.cs
  19. 4 0
      CSharp/Game/Model/Model.csproj
  20. 12 0
      CSharp/Game/Model/NumDefine.cs
  21. 6 0
      CSharp/Game/Model/Options.cs
  22. 10 0
      CSharp/Platform/Common/Helper/MongoHelper.cs
  23. 1 1
      CSharp/Platform/Common/Helper/TimeHelper.cs
  24. 90 0
      CSharp/Platform/Common/Network/AChannel.cs
  25. 5 0
      CSharp/Platform/Common/Network/IService.cs
  26. 22 3
      CSharp/Platform/TNet/TChannel.cs
  27. 8 1
      CSharp/Platform/TNet/TService.cs
  28. 21 2
      CSharp/Platform/UNet/UChannel.cs
  29. 18 0
      CSharp/Platform/UNet/UService.cs

+ 1 - 1
CSharp/Game/Controller/ConfigCategory/BuffCategory.cs

@@ -2,7 +2,7 @@
 
 namespace Controller
 {
-	[Config(ServerType.Realm, ServerType.Gate, ServerType.City)]
+	[Config(ServerType.Realm | ServerType.Gate | ServerType.City)]
 	public class BuffCategory: ACategory<BuffConfig>
 	{
 	}

+ 1 - 1
CSharp/Game/Controller/ConfigCategory/GlobalCategory.cs

@@ -2,7 +2,7 @@
 
 namespace Controller
 {
-	[Config]
+	[Config(ServerType.All)]
 	public class GlobalCategory: ACategory<GlobalConfig>
 	{
 	}

+ 3 - 4
CSharp/Game/Controller/Controller.csproj

@@ -38,8 +38,9 @@
     <Reference Include="System.Core" />
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="Event\BuffTimeoutAction.cs" />
-    <Compile Include="Event\MessageAction.cs" />
+    <Compile Include="Event\BuffTimeoutEvent.cs" />
+    <Compile Include="Event\GateMessageEvent.cs" />
+    <Compile Include="Event\MessageEvent.cs" />
     <Compile Include="BehaviorTreeNode\Not.cs" />
     <Compile Include="BehaviorTreeNode\Selector.cs" />
     <Compile Include="BehaviorTreeNode\Sequence.cs" />
@@ -48,9 +49,7 @@
     <Compile Include="ConfigCategory\NodeCategory.cs" />
     <Compile Include="ConfigCategory\ServerInfoCategory.cs" />
     <Compile Include="ConfigCategory\UnitCategory.cs" />
-    <Compile Include="Event\AfterAddBuff.cs" />
     <Compile Include="Factory\UnitFactory.cs" />
-    <Compile Include="MessageType.cs" />
     <Compile Include="Message\CMsgLoginEvent.cs" />
     <Compile Include="NodeType.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />

+ 0 - 12
CSharp/Game/Controller/Event/AfterAddBuff.cs

@@ -1,12 +0,0 @@
-using Model;
-
-namespace Controller
-{
-	[Event(EventType.AfterAddBuff)]
-	public class AddBuffToTimer: IEventSync
-	{
-		public void Run(Env env)
-		{
-		}
-	}
-}

+ 2 - 2
CSharp/Game/Controller/Event/BuffTimeoutAction.cs → CSharp/Game/Controller/Event/BuffTimeoutEvent.cs

@@ -3,8 +3,8 @@ using MongoDB.Bson;
 
 namespace Controller
 {
-	[Event(EventType.BuffTimeoutAction)]
-	public class BuffTimeoutAction: IEventSync
+	[Event(EventType.BuffTimeout, ServerType.City)]
+	public class BuffTimeoutEvent: IEventSync
 	{
 		public void Run(Env env)
 		{

+ 12 - 0
CSharp/Game/Controller/Event/GateMessageEvent.cs

@@ -0,0 +1,12 @@
+using Model;
+
+namespace Controller
+{
+	[Event(EventType.GateMessage, ServerType.Gate)]
+	public class GateMessageEvent : IEventSync
+	{
+		public void Run(Env env)
+		{
+		}
+	}
+}

+ 2 - 2
CSharp/Game/Controller/Event/MessageAction.cs → CSharp/Game/Controller/Event/MessageEvent.cs

@@ -5,8 +5,8 @@ using Model;
 
 namespace Controller
 {
-	[Event(EventType.MessageAction)]
-	public class MessageAction: IEventAsync
+	[Event(EventType.Message, ServerType.All)]
+	public class MessageEvent: IEventAsync
 	{
 		public async Task RunAsync(Env env)
 		{

+ 1 - 1
CSharp/Game/Controller/Message/CMsgLoginEvent.cs

@@ -10,7 +10,7 @@ namespace Controller.Message
 		public byte[] PassMd5 { get; set; }
 	}
 
-	[Message(MessageType.CMsgLogin)]
+	[Message(MessageType.CMsgLogin, ServerType.Realm)]
 	internal class CMsgLoginEvent: IEventSync
 	{
 		public void Run(Env env)

+ 4 - 8
CSharp/Game/Model/AEventAttribute.cs

@@ -7,21 +7,17 @@ namespace Model
 	{
 		public int Type { get; private set; }
 
-		private int ServerType { get; set; }
+		private ServerType ServerType { get; set; }
 
-		protected AEventAttribute(int type, params ServerType[] serverTypes)
+		protected AEventAttribute(int type, ServerType serverType)
 		{
 			this.Type = type;
-
-			foreach (ServerType serverType in serverTypes)
-			{
-				this.ServerType |= (int) serverType;
-			}
+			this.ServerType = serverType;
 		}
 
 		public bool Contains(ServerType serverType)
 		{
-			if ((this.ServerType & (int) serverType) == 0)
+			if ((this.ServerType & serverType) == 0)
 			{
 				return false;
 			}

+ 2 - 2
CSharp/Game/Model/Buff.cs

@@ -62,7 +62,7 @@ namespace Model
 				env[EnvKey.OwnerId] = this.OwnerId;
 				env[EnvKey.BuffId] = this.Id;
 				this.TimerId = World.Instance.GetComponent<TimerComponent>()
-						.Add(this.Expiration, EventType.BuffTimeoutAction, env);
+						.Add(this.Expiration, EventType.BuffTimeout, env);
 			}
 		}
 
@@ -100,7 +100,7 @@ namespace Model
 				env[EnvKey.OwnerId] = this.OwnerId;
 				env[EnvKey.BuffId] = this.Id;
 				this.TimerId = World.Instance.GetComponent<TimerComponent>()
-						.Add(this.Expiration, EventType.BuffTimeoutAction, env);
+						.Add(this.Expiration, EventType.BuffTimeout, env);
 			}
 		}
 

+ 76 - 0
CSharp/Game/Model/Component/GateNetworkComponent.cs

@@ -0,0 +1,76 @@
+using System;
+using Common.Base;
+using Common.Network;
+using TNet;
+using UNet;
+
+namespace Model
+{
+	/// <summary>
+	/// gate对外连接使用
+	/// </summary>
+	public class GateNetworkComponent: Component<World>, IUpdate, IStart
+	{
+		private IService service;
+
+		private void Accept(string host, int port, NetworkProtocol protocol = NetworkProtocol.TCP)
+		{
+			switch (protocol)
+			{
+				case NetworkProtocol.TCP:
+					this.service = new TService(host, port);
+					break;
+				case NetworkProtocol.UDP:
+					this.service = new UService(host, port);
+					break;
+				default:
+					throw new ArgumentOutOfRangeException("protocol");
+			}
+
+			this.AcceptChannel();
+		}
+
+		public void Start()
+		{
+			this.Accept(World.Instance.Options.GateHost, World.Instance.Options.GatePort,
+					World.Instance.Options.Protocol);
+		}
+
+		public void Update()
+		{
+			this.service.Update();
+		}
+
+		/// <summary>
+		/// 接收连接
+		/// </summary>
+		private async void AcceptChannel()
+		{
+			while (true)
+			{
+				AChannel channel = await this.service.GetChannel();
+				ProcessChannel(channel);
+			}
+		}
+
+		/// <summary>
+		/// 接收分发封包
+		/// </summary>
+		/// <param name="channel"></param>
+		private static async void ProcessChannel(AChannel channel)
+		{
+			while (true)
+			{
+				byte[] message = await channel.RecvAsync();
+				Env env = new Env();
+				env[EnvKey.Channel] = channel;
+				env[EnvKey.Message] = message;
+				// 进行消息解析分发
+#pragma warning disable 4014
+				World.Instance.GetComponent<EventComponent<EventAttribute>>()
+						.RunAsync(EventType.GateMessage, env);
+#pragma warning restore 4014
+			}
+		}
+	}
+}

+ 11 - 1
CSharp/Game/Model/Component/NetworkComponent.cs

@@ -62,9 +62,19 @@ namespace Model
 				Env env = new Env();
 				env[EnvKey.Channel] = channel;
 				env[EnvKey.Message] = message;
+				int opcode = BitConverter.ToUInt16(message, 0);
+				// 这个区间表示消息是rpc响应消息
+				if (opcode >= 40000 && opcode < 50000)
+				{
+					int id = BitConverter.ToInt32(message, 2);
+					channel.RequestCallback(id, message, true);
+					continue;
+				}
+
+				// 进行消息解析分发
 #pragma warning disable 4014
 				World.Instance.GetComponent<EventComponent<EventAttribute>>()
-						.RunAsync(EventType.MessageAction, env);
+						.RunAsync(EventType.Message, env);
 #pragma warning restore 4014
 			}
 		}

+ 10 - 0
CSharp/Game/Model/Component/RpcComponent.cs

@@ -0,0 +1,10 @@
+using System.Threading.Tasks;
+using Common.Base;
+
+namespace Model
+{
+	public class RpcComponent: Component<World>
+	{
+
+	}
+}

+ 4 - 7
CSharp/Game/Model/ConfigAttribute.cs

@@ -5,19 +5,16 @@ namespace Model
 	[AttributeUsage(AttributeTargets.Class)]
 	public class ConfigAttribute: Attribute
 	{
-		private int ServerType { get; set; }
+		private ServerType ServerType { get; set; }
 
-		public ConfigAttribute(params ServerType[] serverTypes)
+		public ConfigAttribute(ServerType serverType)
 		{
-			foreach (ServerType serverType in serverTypes)
-			{
-				this.ServerType |= (int) serverType;
-			}
+			this.ServerType = serverType;
 		}
 
 		public bool Contains(ServerType serverType)
 		{
-			if ((this.ServerType & (int) serverType) == 0)
+			if ((this.ServerType &  serverType) == 0)
 			{
 				return false;
 			}

+ 1 - 1
CSharp/Game/Model/EventAttribute.cs

@@ -2,7 +2,7 @@
 {
 	public class EventAttribute: AEventAttribute
 	{
-		public EventAttribute(int type): base(type)
+		public EventAttribute(int type, ServerType serverType): base(type, serverType)
 		{
 		}
 	}

+ 4 - 2
CSharp/Game/Model/EventType.cs

@@ -6,7 +6,9 @@
 		public const int AfterAddBuff = 1;
 		public const int BeforeRemoveBuff = 2;
 		public const int AfterRemoveBuff = 3;
-		public const int BuffTimeoutAction = 4;
-		public const int MessageAction = 5;
+		public const int BuffTimeout = 4;
+		public const int Message = 5;
+
+		public const int GateMessage = 6;
 	}
 }

+ 1 - 1
CSharp/Game/Model/MessageAttribute.cs

@@ -5,7 +5,7 @@
 	/// </summary>
 	public class MessageAttribute: AEventAttribute
 	{
-		public MessageAttribute(int type, params ServerType[] serverTypes): base(type, serverTypes)
+		public MessageAttribute(int type, ServerType serverType): base(type, serverType)
 		{
 		}
 	}

+ 1 - 1
CSharp/Game/Controller/MessageType.cs → CSharp/Game/Model/MessageType.cs

@@ -1,4 +1,4 @@
-namespace Controller
+namespace Model
 {
 	public static class MessageType
 	{

+ 4 - 0
CSharp/Game/Model/Model.csproj

@@ -60,6 +60,8 @@
     <Compile Include="Component\ConfigComponent.cs" />
     <Compile Include="Component\EventComponent.cs" />
     <Compile Include="Component\FactoryComponent.cs" />
+    <Compile Include="Component\GateNetworkComponent.cs" />
+    <Compile Include="Component\RpcComponent.cs" />
     <Compile Include="Component\NetworkComponent.cs" />
     <Compile Include="Component\TimerComponent.cs" />
     <Compile Include="Component\UnitComponent.cs" />
@@ -83,6 +85,8 @@
     <Compile Include="IStart.cs" />
     <Compile Include="IUpdate.cs" />
     <Compile Include="MessageAttribute.cs" />
+    <Compile Include="MessageType.cs" />
+    <Compile Include="NumDefine.cs" />
     <Compile Include="Options.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="ServerType.cs" />

+ 12 - 0
CSharp/Game/Model/NumDefine.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Model
+{
+	public enum NumDefine
+	{
+	}
+}

+ 6 - 0
CSharp/Game/Model/Options.cs

@@ -12,6 +12,12 @@ namespace Model
 		[Option('n', "name", Required = true, HelpText = "Name.")]
 		public string Name { get; set; }
 
+		[Option('h', "gateHost", Required = false, HelpText = "GateHost.")]
+		public string GateHost { get; set; }
+
+		[Option('p', "gatePort", Required = false, HelpText = "GatePort.")]
+		public int GatePort { get; set; }
+
 		[Option('h', "host", Required = true, HelpText = "Host.")]
 		public string Host { get; set; }
 

+ 10 - 0
CSharp/Platform/Common/Helper/MongoHelper.cs

@@ -41,5 +41,15 @@ namespace Common.Helper
 				return (T) BsonSerializer.Deserialize(memoryStream, typeof (T));
 			}
 		}
+
+		public static T FromBson<T>(byte[] bytes, int index, int count)
+		{
+			using (MemoryStream memoryStream = new MemoryStream(bytes))
+			{
+				memoryStream.Seek(index, SeekOrigin.Begin);
+				memoryStream.Seek(index + count, SeekOrigin.End);
+				return (T)BsonSerializer.Deserialize(memoryStream, typeof(T));
+			}
+		}
 	}
 }

+ 1 - 1
CSharp/Platform/Common/Helper/TimeHelper.cs

@@ -6,7 +6,7 @@ namespace Common.Helper
 	{
 		public static long Now()
 		{
-			var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+			DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
 			return Convert.ToInt64((DateTime.UtcNow - epoch).TotalMilliseconds);
 		}
 	}

+ 90 - 0
CSharp/Platform/Common/Network/AChannel.cs

@@ -1,6 +1,8 @@
 using System;
+using System.Collections.Generic;
 using System.Threading.Tasks;
 using Common.Base;
+using Common.Helper;
 
 namespace Common.Network
 {
@@ -15,12 +17,25 @@ namespace Common.Network
 
 	public abstract class AChannel: Entity<AChannel>, IDisposable
 	{
+		protected IService service;
+		private int requestId;
+		protected Action<AChannel> onDispose = channel => { };
+		private readonly Dictionary<int, Action<byte[], bool>> requestCallback = new Dictionary<int, Action<byte[], bool>>();
+
+		protected AChannel(IService service)
+		{
+			this.service = service;
+		}
+
 		/// <summary>
 		/// 发送消息
 		/// </summary>
 		public abstract void SendAsync(
 				byte[] buffer, byte channelID = 0, PacketFlags flags = PacketFlags.Reliable);
 
+		public abstract void SendAsync(
+				List<byte[]> buffers, byte channelID = 0, PacketFlags flags = PacketFlags.Reliable);
+
 		/// <summary>
 		/// 接收消息
 		/// </summary>
@@ -30,6 +45,81 @@ namespace Common.Network
 
 		public abstract string RemoteAddress { get; }
 
+		public event Action<AChannel> OnDispose
+		{
+			add
+			{
+				this.onDispose += value;
+			}
+			remove
+			{
+				this.onDispose -= value;
+			}
+		}
+
 		public abstract void Dispose();
+
+		// 消息回调或者超时回调
+		public void RequestCallback(int id, byte[] buffer, bool isOK)
+		{
+			Action<byte[], bool> action;
+			if (this.requestCallback.TryGetValue(id, out action))
+			{
+				action(buffer, isOK);
+			}
+			this.requestCallback.Remove(id);
+		}
+
+		/// <summary>
+		/// Rpc请求
+		/// </summary>
+		/// <typeparam name="T"></typeparam>
+		/// <typeparam name="K"></typeparam>
+		/// <param name="type"></param>
+		/// <param name="request"></param>
+		/// <param name="waitTime"></param>
+		/// <returns></returns>
+		public Task<T> Request<T, K>(short type, K request, int waitTime = 0)
+		{
+			++this.requestId;
+			byte[] requestBuffer = MongoHelper.ToBson(request);
+			byte[] typeBuffer = BitConverter.GetBytes(type);
+			byte[] idBuffer = BitConverter.GetBytes(this.requestId);
+			this.SendAsync(new List<byte[]> { typeBuffer, idBuffer, requestBuffer });
+			var tcs = new TaskCompletionSource<T>();
+			this.requestCallback[this.requestId] = (e, b) =>
+			{
+				if (b)
+				{
+					T response = MongoHelper.FromBson<T>(e, 6);
+					tcs.SetResult(response);
+				}
+				else
+				{
+					tcs.SetException(new Exception(string.Format("rpc timeout {0} {1}", type, MongoHelper.ToJson(request))));
+				}
+			};
+
+			if (waitTime > 0)
+			{
+				this.service.Timer.Add(TimeHelper.Now() + waitTime, () => { this.RequestCallback(this.requestId, null, false); });
+			}
+			return tcs.Task;
+		}
+
+		/// <summary>
+		/// Rpc响应
+		/// </summary>
+		/// <typeparam name="T"></typeparam>
+		/// <param name="type"></param>
+		/// <param name="id"></param>
+		/// <param name="response"></param>
+		public void Response<T>(short type, int id, T response)
+		{
+			byte[] responseBuffer = MongoHelper.ToBson(response);
+			byte[] typeBuffer = BitConverter.GetBytes(type);
+			byte[] idBuffer = BitConverter.GetBytes(id);
+			this.SendAsync(new List<byte[]> { typeBuffer, idBuffer, responseBuffer });
+		}
 	}
 }

+ 5 - 0
CSharp/Platform/Common/Network/IService.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Threading.Tasks;
+using Common.Base;
 using MongoDB.Bson;
 
 namespace Common.Network
@@ -22,10 +23,14 @@ namespace Common.Network
 
 		Task<AChannel> GetChannel(string host, int port);
 
+		Task<AChannel> GetChannel(string address);
+
 		Task<AChannel> GetChannel();
 
 		void Remove(AChannel channel);
 
 		void Update();
+
+		TimerManager Timer { get; }
 	}
 }

+ 22 - 3
CSharp/Platform/TNet/TChannel.cs

@@ -1,4 +1,6 @@
 using System;
+using System.Collections.Generic;
+using System.Linq;
 using System.Threading.Tasks;
 using Common.Helper;
 using Common.Logger;
@@ -10,8 +12,6 @@ namespace TNet
 	internal class TChannel: AChannel
 	{
 		private const int SendInterval = 0;
-
-		private readonly TService service;
 		private TSocket socket;
 
 		private readonly TBuffer recvBuffer = new TBuffer();
@@ -22,7 +22,7 @@ namespace TNet
 		private readonly PacketParser parser;
 		private readonly string remoteAddress;
 
-		public TChannel(TSocket socket, TService service)
+		public TChannel(TSocket socket, TService service): base(service)
 		{
 			this.socket = socket;
 			this.service = service;
@@ -39,6 +39,8 @@ namespace TNet
 				return;
 			}
 
+			this.onDispose(this);
+
 			if (disposing)
 			{
 				// 释放托管的资源
@@ -73,6 +75,23 @@ namespace TNet
 			}
 		}
 
+		public override void SendAsync(
+			List<byte[]> buffers, byte channelID = 0, PacketFlags flags = PacketFlags.Reliable)
+		{
+			int size = buffers.Select(b => b.Length).Sum();
+			byte[] sizeBuffer = BitConverter.GetBytes(size);
+			this.sendBuffer.SendTo(sizeBuffer);
+			foreach (byte[] buffer in buffers)
+			{
+				this.sendBuffer.SendTo(buffer);	
+			}
+			
+			if (this.sendTimer == ObjectId.Empty)
+			{
+				this.sendTimer = this.service.Timer.Add(TimeHelper.Now() + SendInterval, this.StartSend);
+			}
+		}
+
 		public ObjectId SendTimer
 		{
 			get

+ 8 - 1
CSharp/Platform/TNet/TService.cs

@@ -127,13 +127,20 @@ namespace TNet
 			return await this.ConnectAsync(host, port);
 		}
 
+		public async Task<AChannel> GetChannel(string address)
+		{
+			string[] ss = address.Split(':');
+			int port = int.Parse(ss[1]);
+			return await this.GetChannel(ss[0], port);
+		}
+
 		public void Update()
 		{
 			this.poller.Update();
 			this.timerManager.Refresh();
 		}
 
-		internal TimerManager Timer
+		public TimerManager Timer
 		{
 			get
 			{

+ 21 - 2
CSharp/Platform/UNet/UChannel.cs

@@ -1,16 +1,19 @@
 using System;
+using System.Collections.Generic;
+using System.Linq;
 using System.Threading.Tasks;
+using Common.Helper;
 using Common.Network;
+using MongoDB.Bson;
 
 namespace UNet
 {
 	internal class UChannel: AChannel
 	{
-		private readonly UService service;
 		private USocket socket;
 		private readonly string remoteAddress;
 
-		public UChannel(USocket socket, UService service)
+		public UChannel(USocket socket, UService service): base(service)
 		{
 			this.socket = socket;
 			this.service = service;
@@ -24,6 +27,8 @@ namespace UNet
 				return;
 			}
 
+			this.onDispose(this);
+
 			if (disposing)
 			{
 				this.socket.Dispose();
@@ -51,6 +56,20 @@ namespace UNet
 			this.socket.SendAsync(buffer, channelID, flags);
 		}
 
+		public override void SendAsync(
+			List<byte[]> buffers, byte channelID = 0, PacketFlags flags = PacketFlags.Reliable)
+		{
+			int size = buffers.Select(b => b.Length).Sum();
+			byte[] buffer = new byte[size];
+			int index = 0;
+			foreach (byte[] bytes in buffers)
+			{
+				Array.Copy(bytes, 0, buffer, index, bytes.Length);
+				index += bytes.Length;
+			}
+			this.socket.SendAsync(buffer, channelID, flags);
+		}
+
 		public override async Task<byte[]> RecvAsync()
 		{
 			return await this.socket.RecvAsync();

+ 18 - 0
CSharp/Platform/UNet/UService.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Linq;
 using System.Threading.Tasks;
+using Common.Base;
 using Common.Network;
 using MongoDB.Bson;
 
@@ -15,6 +16,8 @@ namespace UNet
 
 		private readonly Dictionary<ObjectId, UChannel> idChannels = new Dictionary<ObjectId, UChannel>();
 
+		private readonly TimerManager timerManager = new TimerManager();
+
 		/// <summary>
 		/// 即可做client也可做server
 		/// </summary>
@@ -88,6 +91,13 @@ namespace UNet
 			return await this.ConnectAsync(host, port);
 		}
 
+		public async Task<AChannel> GetChannel(string address)
+		{
+			string[] ss = address.Split(':');
+			int port = int.Parse(ss[1]);
+			return await this.GetChannel(ss[0], port);
+		}
+
 		public async Task<AChannel> GetChannel()
 		{
 			USocket socket = await this.poller.AcceptAsync();
@@ -119,5 +129,13 @@ namespace UNet
 		{
 			this.poller.Update();
 		}
+
+		public TimerManager Timer
+		{
+			get
+			{
+				return this.timerManager;
+			}
+		}
 	}
 }