static void HandleNativeNetworkIoEvent(int kind, int status, int channel, IntPtr sid, IntPtr opaque) { var nsm = NetworkServiceManager.Instance; Debug.LogFormat("The channel connect_id={0}, bytes_transferred={1}", YASIO_NI.yasio_connect_id(nsm._service, channel), YASIO_NI.yasio_bytes_transferred(nsm._service, channel)); switch ((YASIO_NI.YEnums)kind) { case YASIO_NI.YEnums.YEK_PACKET: (int cmd, NativeDataView ud, Stream hold) = nsm._packeter.DecodePDU(YASIO_NI.yasio_unwrap_ptr(opaque, 0), YASIO_NI.yasio_unwrap_len(opaque, 0)); nsm._packeter.HandleEvent(NetworkEvent.PACKET, cmd, ud, channel); hold?.Dispose(); break; case YASIO_NI.YEnums.YEK_CONNECT_RESPONSE: if (status == 0) { nsm.UpdateSession(channel, sid); nsm.BroadcastEventToListeners(NetworkEventType.CONNECT_SUCCESS, 0, channel); nsm._packeter.HandleEvent(NetworkEvent.CONNECT_SUCCESS, -1, NativeDataView.NullValue, channel); } else { nsm.BroadcastEventToListeners(NetworkEventType.CONNECT_FAILED, status, channel); nsm._packeter.HandleEvent(NetworkEvent.CONNECT_FAILED, -1, NativeDataView.NullValue, channel); } break; case YASIO_NI.YEnums.YEK_CONNECTION_LOST: nsm.BroadcastEventToListeners(NetworkEventType.CONNECTION_LOST, status, channel); nsm._packeter.HandleEvent(NetworkEvent.CONNECTION_LOST, -1, NativeDataView.NullValue, channel); break; } }
unsafe public void EncodePDU(int cmd, NativeDataView ud, IntPtr ob) { YASIO_NI.yasio_ob_write_short(ob, (short)cmd); YASIO_NI.yasio_ob_write_int(ob, ud.len); // packet size YASIO_NI.yasio_ob_write_int(ob, VERIFIED_MAGIC_NUM); // magic number YASIO_NI.yasio_ob_write_bytes(ob, ud.ptr, ud.len); // ud }
unsafe public void SendSerializedMsg(int cmd, NativeDataView ud, int channel = 0) { // The whole packet is sizeof(body) + sizeof(header), so no memory realloc IntPtr pdu = YASIO_NI.yasio_ob_new(ud.len + _packeter.GetHeaderSize()); _packeter.EncodePDU(cmd, ud, pdu); if (channel < _sessions.Length) { IntPtr sid = _sessions[channel]; if (sid != IntPtr.Zero) { YASIO_NI.yasio_write_ob(_service, sid, pdu); } else { Debug.LogFormat("Can't send message to the channel: {0}, the session not ok!", channel); } } else { Debug.LogFormat("Can't send message to the channel: {0}, the index is overflow, max allow value is:{1}", channel, _sessions.Length); } YASIO_NI.yasio_ob_release(pdu); }
/// <summary> /// /// </summary> public void Stop() { if (isRunning()) { YASIO_NI.yasio_destroy_service(_service); _service = IntPtr.Zero; } }
/// <summary> /// 获取链接状态的tcp.rtt, 如果未连接,则返回0 /// </summary> /// <param name="channel"></param> /// <returns></returns> uint GetRTT(int channel = 0) { if (channel < _sessions.Length) { return(YASIO_NI.yasio_tcp_rtt(_sessions[channel])); } return(0); }
/// <summary> /// /// </summary> /// <param name="address"></param> /// <param name="port"></param> /// <param name="channel"></param> public void ListenAt(string address, ushort port, int channel) { string strParam = string.Format("{0};{1};{2}", channel, address, port); YASIO_NI.yasio_set_option(_service, (int)YASIO_NI.YEnums.YOPT_C_REMOTE_ENDPOINT, strParam); var reuseaddr = (int)YASIO_NI.YEnums.YCF_REUSEADDR; YASIO_NI.yasio_set_option(_service, (int)YASIO_NI.YEnums.YOPT_C_MOD_FLAGS, $"{channel};{reuseaddr};0"); ListenAt(channel); }
unsafe public (int, NativeDataView, Stream) DecodePDU(IntPtr bytes, int len) { Stream dataStream = new UnmanagedMemoryStream((byte *)bytes, len); try { using (var reader = new BinaryReader(dataStream, Encoding.ASCII, true)) { // 读取包头信息,magic number检验(这里也可以用hash摘要算法,计算消息体数据摘要) int cmd = IPAddress.NetworkToHostOrder(reader.ReadInt16()); int udLen = IPAddress.NetworkToHostOrder(reader.ReadInt32()); int magicNum = IPAddress.NetworkToHostOrder(reader.ReadInt32()); reader.Dispose(); if (magicNum == VERIFIED_MAGIC_NUM) { // magic number校验正确 #if UNITY_EDITOR if (DUMP_RECV_HEX) { int bodyLen = len - PROTO_HEADER_SIZE; // the udLen===bodyLen string wholeHexs = YASIO_NI.DumpHex(bytes, len); UnityEngine.Debug.LogFormat("cmd={0}, udLen/bodyLen={1}/{2}, wholeHexs: {3}\n", cmd, udLen, bodyLen, wholeHexs); string bodyHexs = YASIO_NI.DumpHex(bytes + PROTO_HEADER_SIZE, bodyLen); UnityEngine.Debug.LogFormat("cmd={0}, bodyHexs: {1}\n", cmd, bodyHexs); } #endif NativeDataView ud = new NativeDataView { ptr = bytes + PROTO_HEADER_SIZE, len = len - PROTO_HEADER_SIZE }; return(cmd, ud, dataStream); } else { UnityEngine.Debug.LogErrorFormat("SampleNetworkPacketHandler.DecodePDU: check magic number failed, magicNum={0}, VERIFIED_MAGIC_NUM={1}", magicNum, VERIFIED_MAGIC_NUM); } } } finally { dataStream?.Dispose(); } return(-1, NativeDataView.NullValue, null); }
/// <summary> /// 向远端发送数据 /// </summary> /// <param name="bytes"></param> public void SendRaw(NativeDataView data, int channel) { if (channel < _sessions.Length) { IntPtr sid = _sessions[channel]; if (sid != IntPtr.Zero) { YASIO_NI.yasio_write(_service, sid, data.ptr, data.len); } else { Debug.LogFormat("Can't send message to the channel: {0}, the session not ok!", channel); } } else { Debug.LogFormat("Can't send message to the channel: {0}, the index is overflow, max allow value is:{1}", channel, _sessions.Length); } }
/// <param name="maxChannels">支持并发连接数</param> /// <param name="packeter">数据包处理器</param> public void Start(int maxChannels, INetworkPacketHandler packeter) { if (isRunning()) { return; } _packeter = packeter; _maxChannels = Math.Max(maxChannels, 1); _sessions = new IntPtr[_maxChannels]; YASIO_NI.yasio_init_globals(HandleNativeConsolePrint); _service = YASIO_NI.yasio_create_service(_maxChannels, HandleNativeNetworkIoEvent); YASIO_NI.yasio_set_print_fn(_service, HandleNativeConsolePrint); for (int i = 0; i < _maxChannels; ++i) { SetOption(YASIO_NI.YEnums.YOPT_C_LFBFD_PARAMS, _packeter.GetOptions(i)); } }
/// <summary> /// 分派底层网络事件, tick调用 /// </summary> public void OnUpdate() { YASIO_NI.yasio_dispatch(_service, 128); }
/// <summary> /// /// </summary> /// <param name="channel"></param> public void ListenAt(int channel) { YASIO_NI.yasio_open(_service, channel, (int)YASIO_NI.YEnums.YCK_TCP_SERVER); }
/// <summary> /// 建立连接 /// </summary> /// <param name="channel">连接信道</param> public void Connect(int channel = 0) { YASIO_NI.yasio_open(_service, channel, (int)YASIO_NI.YEnums.YCK_TCP_CLIENT); }
public void Disconnect(int channel = 0) { YASIO_NI.yasio_close(_service, channel); }
/// <summary> /// 设置网络选项 /// </summary> /// <param name="opt"> /// /// </param> /// <param name="strParam">use ';' to split parameters, such as "0;ip138.com;80</param> public void SetOption(YASIO_NI.YEnums opt, string strParam) { YASIO_NI.yasio_set_option(_service, (int)opt, strParam); }