网络编程理论基础

主机间通信概述

在这里插入图片描述

ip地址和端口号

​ 生活中任何通信方式的前提,都必须知晓对方的一些标志性信息,进而唯一地确定目标进行通信。例如寄快递时的地址,打电话时的手机号等等。主机间的通信也需要一些标志性信息用以唯一的确定通信的双方,这些标志信息即为ip地址和端口号。

​ 主机间通信的本质,其实是两台主机中程序的通信,例如主机A的微信应用发送一条消息给主机B中的微信应用,首先就要知晓主机B的ip地址,其次发送到B主机时还需要知道应用B在主机B中的位置,这就是端口号的作用,用来标识应用。

ip地址是主机的唯一标识

端口号可以唯一标识进程(不考虑端口复用)

ip分为公网ip和内网ip,内网ip主机间无法直接访问,需要通过访问公网ip,依靠公网ip主机进行转发,这也就是服务器的意义

Socket概述

Socket(套接字)是应用层与TCP/IP协议族通信的中间软件抽象层。简单来讲,它就是一组抽象接口,提供了一套供应用层调用的API,隔离了应用层和其下面的三层。

Socket把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,只需要使用Socket提供的API接口,无需关心底层如何传输符合相应网络协议传输要求的数据。

Socket通信模型

  1. 游戏服务器端创建Socket对象绑定和监听IP地址,端口号
  2. 游戏服务器端调用Accept方去监听和等待客户端的连接请求
  3. 游戏客户端创建Socket对象,通过游戏服务器的IP地址与端
    口号,调用Connect方法,向游戏服务器端发起连接请求
  4. 游戏服务器端收到游戏客户端的连接请求,创建出一个专门与客户端进行相连接的Socket
  5. 游戏服务器端和游戏客户端都必须按照协议对Socket句柄进行读/写操作,也就是通过Socket完成网络数据的相互通信
  6. 游戏服务器端与游戏客户端,都关闭数据输入输出流,关闭Socket句柄,并释放相关资源

游戏服务器架构的演变过程

第一代游戏服务器架构

在这里插入图片描述

​ 最早的文字类网络游戏,使用单台物理机,单个进程作为游戏服务器程序。单线程无阻塞的处理客户端请求,每隔一秒与所有客户端进行更新同步数据。

存在的问题

  1. 服务器的处理速度快,与客户端直接交互间的网络通信速度较慢,每次处理都需要等待网络通信的结果,效率大大降低。
  2. 数据库还未诞生,数据均是I/O读写,安全性低且速度缓慢使得服务器等待I/O读写时间较长,效率底下。
  3. 采用单个进程处理,速度受限无法同时迅速处理大量玩家的请求,不适用于大型商业游戏开发。

第二代游戏服务器架构

在这里插入图片描述

​ 随着游戏玩家数量和数据的增加,一代服务器无法负载更多用户,第二代服务器架构采用分区分服的模式,同时引入了数据库软件存储,实现了多个游戏服务器进程同时独立地运行。

存在的问题

  1. 仍未解决服务器进程直接与客户端交互速度不匹配的弊端。
  2. 解决了I/O读写慢的问题,但数据库的读写速度仍和服务器处理速度不匹配。
  3. 服务器进程相互独立,无法满足玩家跨服联机和跨服对战的需求

第三代游戏服务器架构

在这里插入图片描述

​ 第三代服务器架构分解了原服务器的职责,加入了两类代理进程,负责完成数据的读写和与客户端的交互操作。让第三代服务器进程可以专注逻辑的处理,不会出现因速度不匹配造成的效率低下。

存在的问题

  1. 仍未实现跨服需求,提升的效率方式仍是横向增加服务器进程的个数,功能一致,不同服务器进程之间相互独立。

第三代游戏服务器架构的拓展

在这里插入图片描述

​ 此架构是针对跨服需求所作的拓展,将原来把众多玩家分到若干个相同功能的服务器的行为,改为了根据场景,功能等业务逻辑分成多个服务器,让处于相同场景下的玩家均可以进行通信,实现了大型多人同屏网络游戏的开发。

用户登录逻辑详解

在这里插入图片描述

​ 本文主要是在使用ET框架前补充一下网络编程的相关理论知识,这里结合ET6.0框架的demo源码,分析用户登录的一般逻辑。

  1. 请求分配空闲(低负载)的网关

    ​ 在新游戏服务器架构中,客户端不会直接与服务器相连,而是通过与网关的连接和转发,达到和服务器相互通信的目的。在与网关相连之前,首先会与Realm网关负载均衡服务器相连,其会实时监控网关服务器,选择负载最小的网关服务器分配给客户端;

  2. 通知分配的网关服务器

    ​ 网关负载均衡服务器会记录所有,分配的过程其会生成Token令牌(加密字符串),同时发送给Gate和客户端,用以客户端连接网关时的身份认证,这个过程也就是“通知”。

  3. 连接网关服务器

    ​ 网关服务器的连接,除了基本的网络通信协议和Socket对象的使用外,还需要增加一步Token令牌的验证,以确保是之前确认的客户端和分配的网关服务器。

  4. 登录进Map服务器

    ​ 网关服务器只是中转站,客户端的目的是登录进游戏服务器,连接上网关服务器后,在网关服务器的帮助下,客户端会登录进其应该登录进的游戏服务器(例如新玩家会进入登录服务器)。

  5. 客户端与服务器的通信

    ​ 由于客户端的对象实体可能穿梭于多个Map服务器(大型MMORPG游戏一般会有多个大地图),网关服务器在做转发时需要得知其具体的位置,Location定位服务器即建立了客户端和服务器的Unit映射关系,供网关服务器正常转发双端消息。

ET6.0框架下登录源码解释

源码分析 LoginHelper.cs

namespace ET
{
    public static class LoginHelper
    {
        //ET6.0 Demo提供的登录方法
        public static async ETTask Login(Scene zoneScene, string address, string account, string password)
        {
            try
            {
                //R2C   Realm To Client  网关均衡负载服务器到客户端的消息实体类
                R2C_Login r2CLogin;
                // 创建一个ETModel层的Session
                Session session = null;
                try
                {
                    //session对话,即为封装好的连接对象,session -> channel -> socket
                    //获取session对话,即为与目标进行连接
                    session = zoneScene.GetComponent<NetKcpComponent>().Create(NetworkHelper.ToIPEndPoint(address));
                    {
                        //客户端向目标发送登录请求(Client to Realm),监听并等待接收目标的回复消息
                        r2CLogin = (R2C_Login) await session.Call(new C2R_Login() { Account = account, Password = password });
                    }
                }
                finally
                {
                    //释放与Realm的会话,即释放连接
                    session?.Dispose();
                }
                //================华丽的分割线=====================
                //以上过程即为上述步骤的1,2步,客户端和Realm完成交互
                //接收到了r2c消息实体中包含了后续用到的Key即为Token令牌
                
                
                
                //建立和网关服务器的连接

                // 创建一个gate Session,并且保存到SessionComponent中
                Session gateSession = zoneScene.GetComponent<NetKcpComponent>().Create(NetworkHelper.ToIPEndPoint(r2CLogin.Address));
                //做相应的配置工作  设置心跳
                gateSession.AddComponent<PingComponent>();
                zoneScene.AddComponent<SessionComponent>().Session = gateSession;
				
                //客户端向Gate网关发送Token令牌消息,请求进行验证,并接收反馈消息
                G2C_LoginGate g2CLoginGate = (G2C_LoginGate)await gateSession.Call(
                    new C2G_LoginGate() { Key = r2CLogin.Key, GateId = r2CLogin.GateId});

                Log.Debug("登陆gate成功!");
                
                await Game.EventSystem.PublishAsync(new EventType.LoginFinish() {ZoneScene = zoneScene});
            }
            catch (Exception e)
            {
                Log.Error(e);
            }
        } 
    }
}

上述源码清楚的解释了如何用代码完成登录的前三步,第四步在连接上网关服务器后会由网关服务器自动创建映射连接游戏服务器等等。

用户下线或封禁等操作,通过标记和强制释放其session即可实现,具体逻辑随着需求变化,笔者在此不再赘述。

感谢大家的阅读,希望文章对大家有所帮助,后续笔者会继续针对ET6.0网络框架开发的内容进行更新!

更多推荐