Gameframework(Network初探篇)

前言 

初探篇讲解Socket基础知识,和GF框架如何实现Tcp网络渠道的,后面文章会对接ET框架进行网络测试。

1.Network常用接口

程序员必须掌握的技能:算法、框架、网络,我说的...(小声bb:虽然这三个我也不会),接下来学习框架这么实现网络模块的,首先给出常用的接口表格:

网络模块接口说明
HasNetworkChannel检查是否存在网络频道
GetNetworkChannel获取网络频道
GetAllNetworkChannels获取所有网络频道
CreateNetworkChannel创建网络频道
DestroyNetworkChannel销毁网络频道
网络渠道接口说明
Connect连接到远程主机
Send发送消息包到远程主机

其实网络渠道相当于Socket高级封装,框架主要实现消息接收(通过队列和消息池),消息包的序列化和反序列化需要开发者继承INetworkChannelHelper实现,后面文章会讲述如何实现对接ET框架的类,这里简单讲述网络模块的框架实现原理。

2.框架实现了什么?

先从NetworkChannelBase脚本入手,它存在以下这些变量,具体代码如下:

private const float DefaultHeartBeatInterval = 30;
private readonly string m_Name;
protected readonly Queue<Packet> m_SendPacketPool;
protected readonly EventPool<Packet> m_ReceivePacketPool;
protected readonly INetworkChannelHelper m_NetworkChannelHelper;
protected AddressFamily m_AddressFamily;
protected bool m_ResetHeartBeatElapseSecondsWhenReceivePacket;
protected float m_HeartBeatInterval;
protected Socket m_Socket;
protected readonly SendState m_SendState;
protected readonly ReceiveState m_ReceiveState;
protected readonly HeartBeatState m_HeartBeatState;
protected int m_SentPacketCount;
protected int m_ReceivedPacketCount;
protected bool m_Active;
private bool m_Disposed;

详细代码自行查阅GameFramework,通过变量分析后,可以得到以下几点认知:

1、网络模块内置心跳包功能,但是具体发送心跳包的函数是使用INetworkChannelHelper发送,也就是用户自定义发送方式。

2、发送和接受消息都有队列,唯一不同点是接受消息队列使用EventPool,这样可以确保消息接受时触发对应函数。

3、发送、接受、心跳都封装到对应State类里,都内置MemoryStream对象和封装对应函数。

TcpNetworkChannel继承它,链接、发送、接受消息都是通过异步函数执行的。接下来给出异步函数表格:

BeginAccept开始一个异步操作来接受一个传入的连接尝试
BeginConnect开始一个对远程主机连接的异步请求
BeginDisconnect开始异步请求从远程终结点断开连接
EndAccept 异步接受传入的连接尝试
EndConnect 结束挂起的异步连接请求
EndDisconnect 结束挂起的异步断开连接请求
BeginReceive 开始从连接的Socket中异步接收数据
BeginReceiveFrom 开始从指定网络设备中异步接收数据
BeginReceiveMessageFrom开始使用指定的SocketFlags将指定字节数的数据异步接收到数据缓冲区的指定位置,然后存储终结点和数据包信息
BeginSend将数据异步发送到连接的Socket
BeginSendFile将文件异步发送到连接的Socket对象
BeginSendTo向特定远程主机异步发送数据

发送消息入栈发送队列,出栈通过BeginSend将消息发送出去,发送成功后调用m_SendCallback函数。至于接受队列如何触发对应句柄呢,如果了解事件池原理的,就跳过以下内容。需要调用RegisterHandler将回调消息函数注册到事件池中,在INetworkChannelHelper通过反射将所有继承IPacketHandler进行注册,至于继承IPacketHandler如何进行实现呢?通过心跳包回调句柄分析实现。

namespace GameMain
{
	public partial class TestMessageHandle : IPacketHandler
	{
		public override int Id => OuterOpcode.R2C_Ping;

        public override void Handle(AChannel channel, R2C_Ping message)
		{
			//TODO   发送C2R_Ping
		}
	}
}

3.对接ET框架

ET版本5.0讲解Tcp消息包头,首先展示出ET消息包的格式。

所以GF框架去实现INetworkChannelHelper也需要对应上格式,具体代码如下:

        public IMessage DeserializeMessage(IMHeader header, MemoryStream source, out object customErrorData)
        {
            customErrorData = null;
            THeader tcpheader = header as THeader;
            if (tcpheader == null)
            {
                Log.Warning("tcp header is invalid.");
                return null;
            }

            IMessage message = null;
            if (tcpheader.IsValid)
            {
                message = m_Responses[header.Id];
                if (message != null)
                {
                    ProtobufHelper.FromStream(message, source);
                }
                else
                {
                    Log.Warning("Can not deserialize message for message id '{0}'.", header.Id.ToString());
                }
            }
            else
            {
                Log.Warning("tcp header is invalid.");
            }
            ReferencePool.Release(tcpheader);
            return message;
        }

        public IMHeader DeserializeMessageHeader(MemoryStream source, out object customErrorData)
        {
            customErrorData = null;
            THeader header = ReferencePool.Acquire<THeader>();
            if (source != null)
            {
                byte[] bytes = source.GetBuffer();
                header.Len = BitConverter.ToInt32(bytes, 0) - 2;
                header.Id = BitConverter.ToUInt16(bytes, NetworkParam.TcpOpcodeIndex);
                return header;
            }

            return null;
        }


        public bool Serialize<T>(T message, MemoryStream destination) where T : IMessage
        {
            memoryStream.Seek(HeaderLen, SeekOrigin.Begin);
            memoryStream.SetLength(HeaderLen);
            ProtobufHelper.ToStream(message, memoryStream);

            THeader header = ReferencePool.Acquire<THeader>();
            header.Id = message.Id;
            header.Len = (int)(memoryStream.Length - NetworkParam.TcpOpcodeIndex);

            memoryStream.Position = 0;
            this.cache.WriteTo(0, header.Len);
            this.cache.WriteTo(4, header.Id);
            Array.Copy(cache, 0, memoryStream.GetBuffer(), 0, cache.Length);
            memoryStream.WriteTo(destination);

            ReferencePool.Release(header);
            return true;
        }

序列化消息时长度需要减去4字节,反序列化消息时需要指定大小,不然GoogleProtobuf无法成功解析。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
在Android中,Settings模块可以通过注册BroadcastReceiver来获取系统广播。其中,可以通过注册ACTION_NETWORK_STATE_CHANGED广播来获取网络状态变化的事件,包括NETWORK_NOT_FOUND_EVENT状态。 具体来说,可以在Settings模块中定义一个类继承BroadcastReceiver,然后在onReceive()方法中处理ACTION_NETWORK_STATE_CHANGED广播。在处理过程中,可以检查网络状态变化的extra数据,并根据其中包含的状态信息判断是否为NETWORK_NOT_FOUND_EVENT状态。 下面是一个示例代码: ``` public class NetworkStateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { NetworkInfo networkInfo = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); if (networkInfo != null && networkInfo.getState() == NetworkInfo.State.DISCONNECTED) { // 网络连接断开 } else if (networkInfo != null && networkInfo.getState() == NetworkInfo.State.CONNECTED) { // 网络连接成功 } } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { int event = intent.getIntExtra(NetworkAgent.EVENT_NETWORK_STATUS_CHANGED, -1); if (event == NetworkAgent.EVENT_NETWORK_NOT_FOUND) { // 网络连接未找到 } } } } ``` 在以上代码中,可以通过判断intent的action来确定接收到的广播类型,然后根据extra数据中包含的信息来判断网络状态变化的类型。其中,如果接收到的广播类型为ACTION_NETWORK_STATE_CHANGED,则可以通过获取extra数据中的NetworkAgent.EVENT_NETWORK_STATUS_CHANGED来判断网络状态是否为NETWORK_NOT_FOUND_EVENT状态。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值