Bladeren bron

抽出登录模块LoginClient,方便以后重用

tanghai 13 jaren geleden
bovenliggende
commit
c4e57bf1e6

+ 26 - 0
CSharp/App/LoginClient/GateSession.cs

@@ -0,0 +1,26 @@
+using System;
+using ENet;
+
+namespace LoginClient
+{
+	public class GateSession: IDisposable
+	{
+		private readonly Peer peer;
+		public int ID { get; set; }
+
+		public GateSession(Peer peer)
+		{
+			this.peer = peer;
+		}
+
+		public void Dispose()
+		{
+			this.peer.Dispose();
+		}
+
+		public void Login()
+		{
+			
+		}
+	}
+}

+ 58 - 0
CSharp/App/LoginClient/LoginClient.cs

@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Net.Sockets;
+using System.Threading.Tasks;
+using ENet;
+using Log;
+
+namespace LoginClient
+{
+	public class LoginClient : IDisposable
+    {
+		private int sessionId;
+
+		private readonly ClientHost clientHost = new ClientHost();
+		
+		public void Dispose()
+		{
+			this.clientHost.Dispose();
+		}
+
+		public void RunOnce()
+		{
+			this.clientHost.RunOnce();
+		}
+
+		public void Start(int timeout)
+		{
+			this.clientHost.Start(timeout);
+		}
+
+		public async Task<List<Realm_List_Gate>> LoginRealm(
+			string hostName, ushort port, string account, string password)
+	    {
+			using (var tcpClient = new TcpClient())
+			{
+				await tcpClient.ConnectAsync(hostName, port);
+
+				using (var session = new RealmSession(
+					tcpClient.GetStream()) { ID = ++this.sessionId })
+				{
+					var gateList = await session.Login(account, password);
+
+					Logger.Trace("session: {0}, login success!", session.ID);
+					return gateList;
+				}
+			}
+	    }
+
+		public async void LoginGate(string hostName, ushort port)
+		{
+			Peer peer = await this.clientHost.ConnectAsync(hostName, port);
+			using (var session = new GateSession(peer) { ID = ++sessionId })
+			{
+				session.Login();
+			}
+		}
+    }
+}

+ 72 - 0
CSharp/App/LoginClient/LoginClient.csproj

@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{8650195A-7904-4EBC-9D81-B392A7E9B9B3}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>RealmClient</RootNamespace>
+    <AssemblyName>RealmClient</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <RestorePackages>true</RestorePackages>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Numerics" />
+    <Reference Include="System.Runtime.Serialization" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="GateSession.cs" />
+    <Compile Include="Messages.cs" />
+    <Compile Include="LoginClient.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="LoginException.cs" />
+    <Compile Include="RealmSession.cs" />
+    <Compile Include="SRP6Client.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\Platform\ENet\ENet.csproj">
+      <Project>{D0B4CFAC-A368-4742-9863-68776CFA9938}</Project>
+      <Name>ENet</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\Platform\Helper\Helper.csproj">
+      <Project>{24233cd5-a5df-484b-a482-b79cb7a0d9cb}</Project>
+      <Name>Helper</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\Platform\Log\Log.csproj">
+      <Project>{72e16572-fc1f-4a9e-bc96-035417239298}</Project>
+      <Name>Log</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Import Project="$(SolutionDir)\.nuget\nuget.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 11 - 0
CSharp/App/LoginClient/LoginException.cs

@@ -0,0 +1,11 @@
+using System;
+
+namespace LoginClient
+{
+	public class LoginException: Exception
+	{
+		public LoginException(string message): base(message)
+		{
+		}
+	}
+}

+ 63 - 1
CSharp/App/Modules/Robot/Protos/Messages.cs → CSharp/App/LoginClient/Messages.cs

@@ -1,10 +1,11 @@
 using System.Collections.Generic;
 using System.Runtime.Serialization;
 
-namespace Robot.Protos
+namespace LoginClient
 {
 	public static class MessageOpcode
 	{
+		public const ushort CMSG_REALM_LIST = 16;
 		public const ushort CMSG_AUTH_LOGON_PERMIT = 800;
 		public const ushort CMSG_OTP_PASSWORD = 801;
 		public const ushort CMSG_PPC_PASSWORD = 802;
@@ -15,6 +16,7 @@ namespace Robot.Protos
 		public const ushort SMSG_REALM_LIST = 902;
 		public const ushort SMSG_LOCK_FOR_SAFE_TIME = 903;
 		public const ushort SMSG_PASSWORD_PROTECT_TYPE = 904;
+		public const ushort SMSG_AUTH_LOGON_PROOF_M2 = 905;
 	}
 
 	public static class ErrorCode
@@ -138,4 +140,64 @@ namespace Robot.Protos
 		[DataMember(Order = 2, IsRequired = true)]
 		public byte[] M { get; set; }
 	}
+
+	[DataContract]
+	public class CMSG_Realm_List
+	{
+	}
+
+	[DataContract]
+	public class SMSG_Auth_Logon_Proof_M2
+	{
+		[DataMember(Order = 1, IsRequired = true)]
+		public int ErrorCode { get; set; }
+
+		[DataMember(Order = 2, IsRequired = true)]
+		public byte[] M { get; set; }
+	}
+
+	public class Realm_List_Gate
+	{
+		[DataMember(Order = 1, IsRequired = true)]
+		public byte[] Name { get; set; }
+		[DataMember(Order = 2, IsRequired = true)]
+		public byte[] Address { get; set; }
+		[DataMember(Order = 3, IsRequired = true)]
+		public float CityLoad { get; set; }
+	}
+
+	public class SMSG_Realm_List
+	{
+		public List<Realm_List_Gate> GateList { get; set; } 
+	}
+
+	public class CMSG_Auth_Session 
+	{
+		[DataMember(Order = 1, IsRequired = true)]
+		public uint ClientBuild { get; set; }
+
+		[DataMember(Order = 1, IsRequired = true)]
+		public uint Unk2 { get; set; }
+
+		[DataMember(Order = 1, IsRequired = true)]
+		public byte[] Username { get; set; }
+
+		[DataMember(Order = 1, IsRequired = true)]
+		public uint Unk3 { get; set; }
+
+		[DataMember(Order = 1, IsRequired = true)]
+		public uint ClientSeed { get; set; }
+
+		[DataMember(Order = 1, IsRequired = true)]
+		public uint Unk4 { get; set; }
+
+		[DataMember(Order = 1, IsRequired = true)]
+		public byte[] Digest { get; set; }
+
+		[DataMember(Order = 1, IsRequired = false)]
+		public byte[] Mac { get; set; }
+
+		[DataMember(Order = 1, IsRequired = false)]
+		public byte[] Hd { get; set; }
+	}
 }

+ 36 - 0
CSharp/App/LoginClient/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 有关程序集的常规信息通过以下
+// 特性集控制。更改这些特性值可修改
+// 与程序集关联的信息。
+[assembly: AssemblyTitle("RealmClient")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("RealmClient")]
+[assembly: AssemblyCopyright("Copyright ©  2013")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 将 ComVisible 设置为 false 使此程序集中的类型
+// 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型,
+// 则将该类型上的 ComVisible 特性设置为 true。
+[assembly: ComVisible(false)]
+
+// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
+[assembly: Guid("46a9e699-b647-4fde-bcc1-5bb0aead64ff")]
+
+// 程序集的版本信息由下面四个值组成:
+//
+//      主版本
+//      次版本 
+//      生成号
+//      修订号
+//
+// 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
+// 方法是按如下所示使用“*”:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 73 - 32
CSharp/App/Modules/Robot/RealmSession.cs → CSharp/App/LoginClient/RealmSession.cs

@@ -1,24 +1,25 @@
 using System;
-using System.Linq;
+using System.Collections.Generic;
 using System.Net.Sockets;
-using System.Numerics;
 using System.Security.Cryptography;
 using System.Threading.Tasks;
 using Helper;
 using Log;
-using Robot.Protos;
 
-namespace Robot
+namespace LoginClient
 {
 	public class RealmSession: IDisposable
 	{
-		private readonly TcpClient tcpClient = new TcpClient();
-		private NetworkStream networkStream;
-		private readonly RealmInfo realmInfo = new RealmInfo();
+		private readonly NetworkStream networkStream;
+		public int ID { get; set; }
+
+		public RealmSession(NetworkStream networkStream)
+		{
+			this.networkStream = networkStream;
+		}
 
 		public void Dispose()
 		{
-			this.tcpClient.Close();
 			this.networkStream.Dispose();
 		}
 
@@ -48,8 +49,8 @@ namespace Robot
 
 			if (opcode != MessageOpcode.SMSG_PASSWORD_PROTECT_TYPE)
 			{
-				Logger.Trace("opcode: {0}", opcode);
-				throw new RealmException(string.Format("error opcode: {0}", opcode));
+				throw new LoginException(string.Format(
+					"session: {0}, opcode: {1}", this.ID, opcode));
 			}
 
 			var smsgPasswordProtectType = 
@@ -59,9 +60,9 @@ namespace Robot
 
 			if (smsgPasswordProtectType.Code != 200)
 			{
-				throw new RealmException(string.Format(
-					"SMSG_Lock_For_Safe_Time: {0}", 
-					JsonHelper.ToString(smsgPasswordProtectType)));
+				throw new LoginException(string.Format(
+					"session: {0}, SMSG_Lock_For_Safe_Time: {1}", 
+					this.ID, JsonHelper.ToString(smsgPasswordProtectType)));
 			}
 
 			return smsgPasswordProtectType;
@@ -77,19 +78,19 @@ namespace Robot
 			if (opcode != MessageOpcode.SMSG_AUTH_LOGON_CHALLENGE_RESPONSE)
 			{
 				Logger.Trace("opcode: {0}", opcode);
+				throw new LoginException(string.Format(
+					"session: {0}, opcode: {1}", this.ID, opcode));
 			}
-
-			Logger.Trace("message: {0}", message.ToHex());
-
+			
 			var smsgAuthLogonChallengeResponse =
 				ProtobufHelper.FromBytes<SMSG_Auth_Logon_Challenge_Response>(message);
 
 			if (smsgAuthLogonChallengeResponse.ErrorCode != ErrorCode.REALM_AUTH_SUCCESS)
 			{
 				Logger.Trace("error code: {0}", smsgAuthLogonChallengeResponse.ErrorCode);
-				throw new RealmException(
-					string.Format("SMSG_Auth_Logon_Challenge_Response ErrorCode: {0}", 
-					JsonHelper.ToString(smsgAuthLogonChallengeResponse)));
+				throw new LoginException(
+					string.Format("session: {0}, SMSG_Auth_Logon_Challenge_Response: {1}",
+					this.ID, JsonHelper.ToString(smsgAuthLogonChallengeResponse)));
 			}
 
 			Logger.Debug("SMSG_Auth_Logon_Challenge_Response: \n{0}", 
@@ -98,6 +99,47 @@ namespace Robot
 			return smsgAuthLogonChallengeResponse;
 		}
 
+		public async Task<SMSG_Auth_Logon_Proof_M2> Handle_SMSG_Auth_Logon_Proof_M2()
+		{
+			var result = await this.RecvMessage();
+			ushort opcode = result.Item1;
+			byte[] message = result.Item2;
+
+			if (opcode != MessageOpcode.SMSG_AUTH_LOGON_PROOF_M2)
+			{
+				throw new LoginException(string.Format(
+					"session: {0}, error opcode: {1}", this.ID, opcode));
+			}
+
+			var smsgAuthLogonProofM2 = ProtobufHelper.FromBytes<SMSG_Auth_Logon_Proof_M2>(message);
+
+			if (smsgAuthLogonProofM2.ErrorCode != ErrorCode.REALM_AUTH_SUCCESS)
+			{
+				throw new LoginException(string.Format(
+					"session: {0}, SMSG_Auth_Logon_Proof_M2: {1}", 
+					this.ID, JsonHelper.ToString(smsgAuthLogonProofM2)));
+			}
+
+			return smsgAuthLogonProofM2;
+		}
+
+		public async Task<SMSG_Realm_List> Handle_SMSG_Realm_List()
+		{
+			var result = await this.RecvMessage();
+			ushort opcode = result.Item1;
+			byte[] message = result.Item2;
+
+			if (opcode != MessageOpcode.SMSG_REALM_LIST)
+			{
+				throw new LoginException(string.Format(
+					"session: {0}, error opcode: {1}", this.ID, opcode));
+			}
+
+			var smsgRealmList = ProtobufHelper.FromBytes<SMSG_Realm_List>(message);
+
+			return smsgRealmList;
+		}
+
 		public async Task<Tuple<ushort, byte[]>> RecvMessage()
 		{
 			int totalReadSize = 0;
@@ -109,15 +151,14 @@ namespace Robot
 					packetBytes, totalReadSize, packetBytes.Length);
 				if (readSize == 0)
 				{
-					throw new RealmException("connection closed!");
+					throw new LoginException(string.Format(
+						"session: {0}, connection closed", this.ID));
 				}
 				totalReadSize += readSize;
 			}
 
 			int packetSize = BitConverter.ToInt32(packetBytes, 0);
 
-			Logger.Debug("packet size: {0}", packetSize);
-
 			// 读opcode和message
 			totalReadSize = 0;
 			needReadSize = packetSize;
@@ -128,28 +169,21 @@ namespace Robot
 					contentBytes, totalReadSize, contentBytes.Length);
 				if (readSize == 0)
 				{
-					throw new RealmException("connection closed!");
+					throw new LoginException(string.Format(
+						"session: {0}, connection closed", this.ID));
 				}
 				totalReadSize += readSize;
 			}
 
 			ushort opcode = BitConverter.ToUInt16(contentBytes, 0);
 
-			Logger.Debug("opcode: {0}", opcode);
-
 			var messageBytes = new byte[needReadSize - sizeof (ushort)];
 			Array.Copy(contentBytes, sizeof (ushort), messageBytes, 0, messageBytes.Length);
 
 			return new Tuple<ushort, byte[]>(opcode, messageBytes);
 		}
 
-		public async Task ConnectAsync(string hostName, ushort port)
-		{
-			await this.tcpClient.ConnectAsync(hostName, port);
-			this.networkStream = this.tcpClient.GetStream();
-		}
-
-		public async void Login(string account, string password)
+		public async Task<List<Realm_List_Gate>> Login(string account, string password)
 		{
 			byte[] passwordBytes = password.ToByteArray();
 			MD5 md5 = MD5.Create();
@@ -200,6 +234,13 @@ namespace Robot
 				M = srp6Client.M.ToUBigIntegerArray()
 			};
 			this.SendMessage(MessageOpcode.CMSG_AUTH_LOGON_PROOF, cmsgAuthLogonProof);
+			await this.Handle_SMSG_Auth_Logon_Proof_M2();
+
+			// 请求realm list
+			var cmsgRealmList = new CMSG_Realm_List();
+			this.SendMessage(MessageOpcode.CMSG_REALM_LIST, cmsgRealmList);
+			var smsgRealmList = await this.Handle_SMSG_Realm_List();
+			return smsgRealmList.GateList;
 		}
 	}
 }

+ 2 - 5
CSharp/App/Modules/Robot/SRP6Client.cs → CSharp/App/LoginClient/SRP6Client.cs

@@ -1,12 +1,9 @@
-using System;
-using System.Globalization;
-using System.Linq;
+using System.Linq;
 using System.Numerics;
 using System.Security.Cryptography;
 using Helper;
-using Org.BouncyCastle.Crypto.Digests;
 
-namespace Robot
+namespace LoginClient
 {
 	public class SRP6Client
 	{

+ 0 - 1
CSharp/App/Modules/Robot/Packages.config

@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="BouncyCastle" version="1.7.0" targetFramework="net45" />
   <package id="CommonServiceLocator" version="1.0" targetFramework="net45" />
   <package id="Prism" version="4.1.0.0" targetFramework="net45" />
   <package id="Prism.MEFExtensions" version="4.1.0.0" targetFramework="net45" />

+ 0 - 11
CSharp/App/Modules/Robot/RealmException.cs

@@ -1,11 +0,0 @@
-using System;
-
-namespace Robot
-{
-	public class RealmException: Exception
-	{
-		public RealmException(string message): base(message)
-		{
-		}
-	}
-}

+ 0 - 10
CSharp/App/Modules/Robot/RealmInfo.cs

@@ -1,10 +0,0 @@
-using Robot.Protos;
-
-namespace Robot
-{
-	public class RealmInfo
-	{
-		public SMSG_Password_Protect_Type SmsgPasswordProtectType { get; set; }
-		public SMSG_Auth_Logon_Challenge_Response SmsgAuthLogonChallengeResponse { get; set; }
-	}
-}

+ 4 - 15
CSharp/App/Modules/Robot/Robot.csproj

@@ -32,10 +32,6 @@
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9283, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\..\..\packages\BouncyCastle.1.7.0\lib\Net20\BouncyCastle.Crypto.dll</HintPath>
-    </Reference>
     <Reference Include="Microsoft.Practices.Prism">
       <HintPath>..\..\..\packages\Prism.4.1.0.0\lib\NET40\Microsoft.Practices.Prism.dll</HintPath>
     </Reference>
@@ -47,24 +43,17 @@
     <Reference Include="System" />
     <Reference Include="System.ComponentModel.Composition" />
     <Reference Include="System.Core" />
-    <Reference Include="System.Numerics" />
-    <Reference Include="System.Runtime.Serialization" />
     <Reference Include="System.Xaml" />
     <Reference Include="System.Xml" />
     <Reference Include="WindowsBase" />
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="Protos\Messages.cs" />
-    <Compile Include="RealmException.cs" />
-    <Compile Include="RealmInfo.cs" />
-    <Compile Include="RealmSession.cs" />
     <Compile Include="Robot.cs" />
     <Compile Include="RobotModule.cs" />
     <Compile Include="RobotViewModel.cs" />
     <Compile Include="RobotView.xaml.cs">
       <DependentUpon>RobotView.xaml</DependentUpon>
     </Compile>
-    <Compile Include="SRP6Client.cs" />
   </ItemGroup>
   <ItemGroup>
     <Page Include="RobotView.xaml">
@@ -77,10 +66,6 @@
       <Project>{d0b4cfac-a368-4742-9863-68776cfa9938}</Project>
       <Name>ENet</Name>
     </ProjectReference>
-    <ProjectReference Include="..\..\..\Platform\Helper\Helper.csproj">
-      <Project>{24233cd5-a5df-484b-a482-b79cb7a0d9cb}</Project>
-      <Name>Helper</Name>
-    </ProjectReference>
     <ProjectReference Include="..\..\..\Platform\Log\Log.csproj">
       <Project>{72e16572-fc1f-4a9e-bc96-035417239298}</Project>
       <Name>Log</Name>
@@ -89,6 +74,10 @@
       <Project>{48a2e149-0dac-41b4-bb54-dfbccd6d42b3}</Project>
       <Name>Infrastructure</Name>
     </ProjectReference>
+    <ProjectReference Include="..\..\LoginClient\LoginClient.csproj">
+      <Project>{8650195A-7904-4EBC-9D81-B392A7E9B9B3}</Project>
+      <Name>LoginClient</Name>
+    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
     <Folder Include="Properties\" />

+ 10 - 13
CSharp/App/Modules/Robot/RobotViewModel.cs

@@ -1,10 +1,11 @@
 using System;
+using System.Collections.Generic;
 using System.ComponentModel.Composition;
 using System.Windows.Threading;
 using ENet;
 using Log;
 using Microsoft.Practices.Prism.ViewModel;
-using Robot;
+using LoginClient;
 
 namespace Modules.Robot
 {
@@ -12,11 +13,11 @@ namespace Modules.Robot
 		PartCreationPolicy(creationPolicy: CreationPolicy.NonShared)]
 	internal sealed class RobotViewModel: NotificationObject, IDisposable
 	{
-		private readonly ClientHost clientHost;
 		private string loginIP = "192.168.11.95";
 		private ushort loginPort = 8888;
 		private string account = "egametang@163.com";
 		private string password = "163bio1";
+		private readonly LoginClient.LoginClient realmClient = new LoginClient.LoginClient();
 
 		private readonly DispatcherTimer timer = new DispatcherTimer(DispatcherPriority.Normal)
 		{ Interval = new TimeSpan(0, 0, 0, 0, 50) };
@@ -91,10 +92,8 @@ namespace Modules.Robot
 
 		public RobotViewModel()
 		{
-			this.clientHost = new ClientHost();
-
-			this.timer.Tick += delegate { this.clientHost.RunOnce(); };
-			this.timer.Start();
+			//this.timer.Tick += delegate { this.clientHost.RunOnce(); };
+			//this.timer.Start();
 		}
 
 		~RobotViewModel()
@@ -110,24 +109,22 @@ namespace Modules.Robot
 
 		private void Disposing(bool disposing)
 		{
-			this.clientHost.Dispose();
 		}
 
 		public async void Login()
 		{
 			try
 			{
-				var session = new RealmSession();
-				await session.ConnectAsync(this.LoginIP, this.LoginPort);
-				session.Login(this.Account, this.Password);
+				// 登录realm
+				List<Realm_List_Gate> gateList = await this.realmClient.LoginRealm(
+					this.LoginIP, this.LoginPort, this.Account, this.Password);
+
+				// 登录gate
 			}
 			catch (Exception e)
 			{
 				Logger.Trace("realm exception: {0}, {1}", e.Message, e.StackTrace);
-				return;
 			}
-
-			Logger.Trace("session login success!");
 		}
 	}
 }

+ 15 - 0
CSharp/CSharp.sln

@@ -48,6 +48,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ENet", "Platform\ENet\ENet.
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ENetTest", "Platform\ENetTest\ENetTest.csproj", "{901A8E5C-C4C6-4C3C-8E18-068D75119F5D}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoginClient", "App\LoginClient\LoginClient.csproj", "{8650195A-7904-4EBC-9D81-B392A7E9B9B3}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -232,6 +234,18 @@ Global
 		{901A8E5C-C4C6-4C3C-8E18-068D75119F5D}.Release|Mixed Platforms.Build.0 = Release|Any CPU
 		{901A8E5C-C4C6-4C3C-8E18-068D75119F5D}.Release|Win32.ActiveCfg = Release|Any CPU
 		{901A8E5C-C4C6-4C3C-8E18-068D75119F5D}.Release|x86.ActiveCfg = Release|Any CPU
+		{8650195A-7904-4EBC-9D81-B392A7E9B9B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{8650195A-7904-4EBC-9D81-B392A7E9B9B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{8650195A-7904-4EBC-9D81-B392A7E9B9B3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{8650195A-7904-4EBC-9D81-B392A7E9B9B3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{8650195A-7904-4EBC-9D81-B392A7E9B9B3}.Debug|Win32.ActiveCfg = Debug|Any CPU
+		{8650195A-7904-4EBC-9D81-B392A7E9B9B3}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{8650195A-7904-4EBC-9D81-B392A7E9B9B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{8650195A-7904-4EBC-9D81-B392A7E9B9B3}.Release|Any CPU.Build.0 = Release|Any CPU
+		{8650195A-7904-4EBC-9D81-B392A7E9B9B3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{8650195A-7904-4EBC-9D81-B392A7E9B9B3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{8650195A-7904-4EBC-9D81-B392A7E9B9B3}.Release|Win32.ActiveCfg = Release|Any CPU
+		{8650195A-7904-4EBC-9D81-B392A7E9B9B3}.Release|x86.ActiveCfg = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -240,6 +254,7 @@ Global
 		{C4C64188-4FAE-4CC3-A9E6-D9D4AF7429B6} = {6E9D97F0-4243-452E-B832-1A855B8118EB}
 		{C46F3337-0F48-4A72-84AD-8FDD1F159BB0} = {6E9D97F0-4243-452E-B832-1A855B8118EB}
 		{48A2E149-0DAC-41B4-BB54-DFBCCD6D42B3} = {6E9D97F0-4243-452E-B832-1A855B8118EB}
+		{8650195A-7904-4EBC-9D81-B392A7E9B9B3} = {6E9D97F0-4243-452E-B832-1A855B8118EB}
 		{3A98B35C-DEA8-489C-9203-263FFB6B065D} = {ADBF5F67-B480-4A93-9D50-C81856FC61A9}
 		{72E16572-FC1F-4A9E-BC96-035417239298} = {ADBF5F67-B480-4A93-9D50-C81856FC61A9}
 		{24233CD5-A5DF-484B-A482-B79CB7A0D9CB} = {ADBF5F67-B480-4A93-9D50-C81856FC61A9}

+ 2 - 0
CSharp/CSharp.sln.DotSettings

@@ -11,6 +11,8 @@
 	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=LoopCanBeConvertedToQuery/@EntryIndexedValue">DO_NOT_SHOW</s:String>
 	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantAssignment/@EntryIndexedValue">DO_NOT_SHOW</s:String>
 	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestUseVarKeywordEverywhere/@EntryIndexedValue">DO_NOT_SHOW</s:String>
+	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedAutoPropertyAccessor_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
+	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedAutoPropertyAccessor_002ELocal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
 	<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=tanghai/@EntryIndexedValue">&lt;?xml version="1.0" encoding="utf-16"?&gt;&lt;Profile name="tanghai"&gt;&lt;CSArrangeThisQualifier&gt;True&lt;/CSArrangeThisQualifier&gt;&lt;CSRemoveCodeRedundancies&gt;True&lt;/CSRemoveCodeRedundancies&gt;&lt;CSMakeFieldReadonly&gt;True&lt;/CSMakeFieldReadonly&gt;&lt;CSUseVar&gt;&lt;BehavourStyle&gt;DISABLED&lt;/BehavourStyle&gt;&lt;LocalVariableStyle&gt;IMPLICIT_WHEN_INITIALIZER_HAS_TYPE&lt;/LocalVariableStyle&gt;&lt;ForeachVariableStyle&gt;IMPLICIT_EXCEPT_SIMPLE_TYPES&lt;/ForeachVariableStyle&gt;&lt;/CSUseVar&gt;&lt;CSOptimizeUsings&gt;&lt;OptimizeUsings&gt;True&lt;/OptimizeUsings&gt;&lt;EmbraceInRegion&gt;False&lt;/EmbraceInRegion&gt;&lt;RegionName&gt;&lt;/RegionName&gt;&lt;/CSOptimizeUsings&gt;&lt;CSShortenReferences&gt;True&lt;/CSShortenReferences&gt;&lt;CSReformatCode&gt;True&lt;/CSReformatCode&gt;&lt;/Profile&gt;</s:String>
 	<s:String x:Key="/Default/CodeStyle/CodeCleanup/RecentlyUsedProfile/@EntryValue">tanghai</s:String>
 	<s:String x:Key="/Default/CodeStyle/CodeCleanup/SilentCleanupProfile/@EntryValue">tanghai</s:String>

+ 2 - 1
CSharp/Platform/ENet/ClientHost.cs

@@ -29,13 +29,14 @@ namespace ENet
 			}
 		}
 
-		public Task<Peer> ConnectAsync(Address address, 
+		public Task<Peer> ConnectAsync(string hostName, ushort port, 
 				uint channelLimit = NativeMethods.ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT,
 				uint data = 0)
 		{
 			CheckChannelLimit(channelLimit);
 
 			var tcs = new TaskCompletionSource<Peer>();
+			var address = new Address {HostName = hostName, Port = port};
 			ENetAddress nativeAddress = address.Struct;
 			IntPtr peerPtr = NativeMethods.enet_host_connect(
 				this.host, ref nativeAddress, channelLimit, data);

+ 3 - 2
CSharp/Platform/ENet/ServerHost.cs

@@ -7,8 +7,8 @@ namespace ENet
 	{
 		private Action<Peer> acceptEvent;
 
-		public ServerHost(
-			Address address, uint peerLimit = NativeMethods.ENET_PROTOCOL_MAXIMUM_PEER_ID,
+		public ServerHost(string hostName, ushort port, 
+			uint peerLimit = NativeMethods.ENET_PROTOCOL_MAXIMUM_PEER_ID,
 			uint channelLimit = 0, uint incomingBandwidth = 0, uint outgoingBandwidth = 0,
 			bool enableCrc = true)
 		{
@@ -18,6 +18,7 @@ namespace ENet
 			}
 			CheckChannelLimit(channelLimit);
 
+			var address = new Address { HostName = hostName, Port = port };
 			ENetAddress nativeAddress = address.Struct;
 			this.host = NativeMethods.enet_host_create(
 				ref nativeAddress, peerLimit, channelLimit, incomingBandwidth, 

+ 6 - 5
CSharp/Platform/ENetTest/ENetClientServerTest.cs

@@ -9,9 +9,9 @@ namespace ENetCSTest
 	[TestClass]
 	public class ENetClientServerTest
 	{
-		private static async void ClientEvent(ClientHost host, Address address)
+		private static async void ClientEvent(ClientHost host, string hostName, ushort port)
 		{
-			var peer = await host.ConnectAsync(address);
+			var peer = await host.ConnectAsync(hostName, port);
 			using (var sPacket = new Packet("0123456789".ToByteArray(), PacketFlags.Reliable))
 			{
 				peer.Send(0, sPacket);
@@ -51,9 +51,10 @@ namespace ENetCSTest
 		[TestMethod]
 		public void ClientSendToServer()
 		{
-			var address = new Address { HostName = "127.0.0.1", Port = 8888 };
+			const string hostName = "127.0.0.1";
+			const ushort port = 8888;
 			var clientHost = new ClientHost();
-			var serverHost = new ServerHost(address);
+			var serverHost = new ServerHost(hostName, port);
 
 			var serverThread = new Thread(() => serverHost.Start(10));
 			var clientThread = new Thread(() => clientHost.Start(10));
@@ -69,7 +70,7 @@ namespace ENetCSTest
 			barrier.SignalAndWait();
 
 			// 往client host线程增加事件,client线程连接server
-			clientHost.Events += () => ClientEvent(clientHost, address);
+			clientHost.Events += () => ClientEvent(clientHost, hostName, port);
 
 			serverThread.Join();
 			clientThread.Join();