ET|02 前后端通讯
登录流程
UILoginComponentSystem
更多框架细节先不讨论,只要知道,当打开登录UI后,会执行UILoginComponentAwakeSystem中的Awake方法
[ObjectSystem]
public class UILoginComponentAwakeSystem : AwakeSystem<UILoginComponent>
{
public override void Awake(UILoginComponent self)
{
ReferenceCollector rc = self.GetParent<UI>().GameObject.GetComponent<ReferenceCollector>();
self.loginBtn = rc.Get<GameObject>("LoginBtn");
self.loginBtn.GetComponent<Button>().onClick.AddListener(()=> { self.OnLogin(); });
self.account = rc.Get<GameObject>("Account");
self.password = rc.Get<GameObject>("Password");
}
}
// 真正的登录逻辑
[FriendClass(typeof(UILoginComponent))]
public static class UILoginComponentSystem
{
public static void OnLogin(this UILoginComponent self)
{
LoginHelper.Login(
self.DomainScene(),
ConstValue.LoginAddress,
self.account.GetComponent<InputField>().text,
self.password.GetComponent<InputField>().text).Coroutine();
}
}
创建Session
// Session可以理解为一个会话
Session session = null;
session = zoneScene.GetComponent<NetKcpComponent>().Create(NetworkHelper.ToIPEndPoint(address));
创建了一个Session,并挂载到NetKcpComponent下,Session也是一个Entity
public static Session Create(this NetKcpComponent self, IPEndPoint realIPEndPoint)
{
long channelId = RandomHelper.RandInt64();
Session session = self.AddChildWithId<Session, AService>(channelId, self.Service);
session.RemoteAddress = realIPEndPoint;
session.AddComponent<SessionIdleCheckerComponent, int>(NetThreadComponent.checkInteral);
self.Service.GetOrCreate(session.Id, realIPEndPoint);
return session;
}
AServer : Abstract Server 服务器基类
TServer : TCP Server TCP连接
KServer : KCP Server KCP连接
基类中有一个GetOrCreate方法,是用于创建连接的,我们可以观察TServer
虽然这个组件叫做NetKcpCompoent,但观察他的Awake方法,添加的连接服务是TCP服务…
public class NetKcpComponentAwakeSystem: AwakeSystem<NetKcpComponent, int> { public override void Awake(NetKcpComponent self, int sessionStreamDispatcherType) { ... self.Service = new TService(NetThreadComponent.Instance.ThreadSynchronizationContext, ServiceType.Outer); } }
创建了一个TChannel(TCP Channel)
private TChannel Create(IPEndPoint ipEndPoint, long id)
{
TChannel channel = new TChannel(id, ipEndPoint, this);
this.idChannels.Add(channel.Id, channel);
return channel;
}
可以查看TChannel的构造函数,确实是TCP + IPV4
public TChannel(long id, IPEndPoint ipEndPoint, TService service)
{
...
this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
...
}
发送登录请求
Call是发送,具体的处理则在服务端
r2CLogin = (R2C_Login) await session.Call(new C2R_Login() { Account = account, Password = password });
服务器代码中有这样一个类,用于处理客户端登录请求
public class C2R_LoginHandler : AMRpcHandler<C2R_Login, R2C_Login>
{
protected override async ETTask Run(Session session, C2R_Login request, R2C_Login response, Action reply)
{
// 随机分配一个Gate
StartSceneConfig config = RealmGateAddressHelper.GetGate(session.DomainZone());
Log.Debug($"gate address: {MongoHelper.ToJson(config)}");
// 向gate请求一个key,客户端可以拿着这个key连接gate
G2R_GetLoginKey g2RGetLoginKey = (G2R_GetLoginKey) await ActorMessageSenderComponent.Instance.Call(
config.InstanceId, new R2G_GetLoginKey() {Account = request.Account});
response.Address = config.OuterIPPort.ToString();
response.Key = g2RGetLoginKey.Key;
response.GateId = g2RGetLoginKey.GateId;
reply();
}
}
可以发现服务器返回了一个GateId给客户端
客户端处理结果
当拿到服务器结果后,客户端首先结束了第一次会话
然后再次创建会话
session?.Dispose();
// 创建一个gate Session,并且保存到SessionComponent中
Session gateSession = zoneScene.GetComponent<NetKcpComponent>().Create(NetworkHelper.ToIPEndPoint(r2CLogin.Address));
gateSession.AddComponent<PingComponent>();
zoneScene.AddComponent<SessionComponent>().Session = gateSession;
服务器架构演变
一代
N个客户端连接1个服务器
服务器直接读写文件IO
二代
N个客户端连接M个服务端(区服)
服务端读写数据库
三代
N个客户端连接M个网关
客户端数据由网关转发给服务器
网关只负责连接(接收/转发)
服务器只负责功能逻辑处理(业务逻辑)
数据库代理只负责DB读写(数据存储)
后续又进行了一次演变
最后,客户端通过网关ID,请求连接网关
G2C_LoginGate g2CLoginGate = (G2C_LoginGate)await gateSession.Call(
new C2G_LoginGate() { Key = r2CLogin.Key, GateId = r2CLogin.GateId});
整个登录流程大概如下