309 lines
9.8 KiB
C#
309 lines
9.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Net;
|
|
using System.Text;
|
|
using UnityEngine;
|
|
|
|
namespace BF
|
|
{
|
|
/// <summary>
|
|
/// Current version only support one connection
|
|
/// </summary>
|
|
internal class RpcService : MonoBehaviour
|
|
{
|
|
internal class ResponseMessage
|
|
{
|
|
/// <summary>
|
|
/// 是否请求成功
|
|
/// </summary>
|
|
public bool isSuccess { get; set; }
|
|
|
|
/// <summary>
|
|
/// 请求失败后的错误消息;成功时,没有此消息
|
|
/// </summary>
|
|
public string errorMsg { get; set; }
|
|
|
|
public string payload { get; set; }
|
|
}
|
|
|
|
|
|
private class RequestMessage
|
|
{
|
|
public byte group;
|
|
public byte cmd;
|
|
public byte[] data;
|
|
public Action<ResponseMessage> callback;
|
|
}
|
|
|
|
public enum RpcState
|
|
{
|
|
None,
|
|
Connecting,
|
|
PrepareSend,
|
|
WaitingRsp,
|
|
}
|
|
|
|
private const string ConnectId = nameof(RpcService);
|
|
|
|
private RpcState rpcState;
|
|
private RequestMessage currentMessage = new RequestMessage();
|
|
private ResponseMessage replyMessage = new ResponseMessage();
|
|
private bool netClientInitialized = false;
|
|
private NetConfiguration configuration;
|
|
private int remainRetryCount;
|
|
private float requestTimeout = 6;
|
|
private string ipAddress;
|
|
private int port;
|
|
|
|
|
|
#region Instance
|
|
|
|
private static RpcService instance;
|
|
public static RpcService Instance
|
|
{
|
|
get
|
|
{
|
|
if (!instance)
|
|
{
|
|
var gameObj = new GameObject(nameof(RpcService));
|
|
instance = gameObj.AddComponent<RpcService>();
|
|
}
|
|
|
|
return instance;
|
|
}
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
DontDestroyOnLoad(gameObject);
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
instance = null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
internal void SendRequest(string address, int port, byte group, byte cmd, string args, Action<ResponseMessage> response,
|
|
int retryCount,float timeout)
|
|
{
|
|
// service is busy directly response error
|
|
if (rpcState != RpcState.None)
|
|
{
|
|
replyMessage.isSuccess = false;
|
|
replyMessage.errorMsg = "Current rpc service is busy";
|
|
replyMessage.payload = string.Empty;
|
|
response?.Invoke(replyMessage);
|
|
return;
|
|
}
|
|
|
|
currentMessage.cmd = cmd;
|
|
currentMessage.@group = group;
|
|
currentMessage.data = Encoding.UTF8.GetBytes(args);
|
|
currentMessage.callback = response;
|
|
|
|
// set state to connecting
|
|
rpcState = RpcState.Connecting;
|
|
|
|
// setup retry
|
|
remainRetryCount = retryCount;
|
|
requestTimeout = timeout;
|
|
ipAddress = address;
|
|
this.port = port;
|
|
|
|
StartRpcService(address, port);
|
|
|
|
// start timeout
|
|
Invoke(nameof(OnRequestTimeout),requestTimeout);
|
|
}
|
|
|
|
private void StartRpcService(string address, int port)
|
|
{
|
|
// lazy initialize
|
|
if (configuration == null)
|
|
{
|
|
configuration = new NetConfiguration
|
|
{
|
|
ServiceType = NetServiceType.TCPService,
|
|
ThreadName = "TcpService"
|
|
};
|
|
}
|
|
|
|
var netClient = NetClient.Instance;
|
|
netClient.Init();
|
|
netClient.SetupService(configuration);
|
|
netClient.Start();
|
|
|
|
Connect(ConnectId, address, port);
|
|
}
|
|
|
|
private void Connect(string connectId, string ip, int port)
|
|
{
|
|
var netClient = NetClient.Instance;
|
|
NetConnectConfiguration configuration =
|
|
new NetConnectConfiguration(NetServiceType.TCPService, connectId);
|
|
|
|
configuration.EnableMessageType(NetIncomingMessageType.DebugMessage);
|
|
configuration.EnableMessageType(NetIncomingMessageType.WarningMessage);
|
|
configuration.EnableMessageType(NetIncomingMessageType.ErrorMessage);
|
|
|
|
// disable reconnect
|
|
// if connection disconnected when sending data
|
|
// we will directly response error and close connection
|
|
configuration.EnableSilenceReconnect = false;
|
|
configuration.HeartBeatInterval = 20;
|
|
configuration.MaxHeartBeatMissCount = 3;
|
|
|
|
// check address is domain or ip address
|
|
if (IPAddress.TryParse(ip, out IPAddress ipAddress))
|
|
{
|
|
netClient.Connect(configuration, ipAddress, port);
|
|
}
|
|
else
|
|
{
|
|
netClient.Connect(configuration, ip, port);
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
// when connected , message send next frame
|
|
if (rpcState == RpcState.PrepareSend)
|
|
{
|
|
// send data
|
|
NetClient.Instance.Send(ConnectId, currentMessage.group, currentMessage.cmd,
|
|
currentMessage.data);
|
|
rpcState = RpcState.WaitingRsp;
|
|
}
|
|
|
|
var netClient = NetClient.Instance;
|
|
INetIncomingMessage incomingMessage = null;
|
|
while ((incomingMessage = netClient.ReadMessage(ConnectId)) != null)
|
|
{
|
|
HandleIncomingMessage(incomingMessage);
|
|
netClient.RecycleMessage(ConnectId, incomingMessage);
|
|
}
|
|
}
|
|
|
|
private void HandleIncomingMessage(INetIncomingMessage message)
|
|
{
|
|
switch (message.MessageType)
|
|
{
|
|
case NetIncomingMessageType.DebugMessage:
|
|
case NetIncomingMessageType.WarningMessage:
|
|
Debug($"Debug Info : {message.ErrorCode} : {message.ErrorMsg}");
|
|
break;
|
|
|
|
case NetIncomingMessageType.ErrorMessage:
|
|
HandleErrorMessage(message);
|
|
break;
|
|
|
|
case NetIncomingMessageType.Data:
|
|
HandleDataMessage(message);
|
|
break;
|
|
|
|
case NetIncomingMessageType.StatusChange:
|
|
HandleStatusMessage(message);
|
|
break;
|
|
|
|
case NetIncomingMessageType.HeartBeatMessage:
|
|
// don't care
|
|
break;
|
|
|
|
default:
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
|
|
private void HandleErrorMessage(INetIncomingMessage message)
|
|
{
|
|
UnityEngine.Debug.LogError($"Debug Info : {message.ErrorCode} : {message.ErrorMsg}");
|
|
replyMessage.isSuccess = false;
|
|
replyMessage.errorMsg = message.ErrorMsg;
|
|
replyMessage.payload = string.Empty;
|
|
currentMessage.callback?.Invoke(replyMessage);
|
|
ResetState();
|
|
}
|
|
|
|
private void HandleDataMessage(INetIncomingMessage message)
|
|
{
|
|
if (currentMessage.cmd != message.CMD || currentMessage.@group != message.Group)
|
|
{
|
|
// ignore not matched data
|
|
replyMessage.isSuccess = false;
|
|
replyMessage.errorMsg = "Message type dis-matched";
|
|
}
|
|
else
|
|
{
|
|
replyMessage.isSuccess = true;
|
|
replyMessage.errorMsg = string.Empty;
|
|
replyMessage.payload = Encoding.UTF8.GetString(message.Data);
|
|
}
|
|
|
|
currentMessage.callback?.Invoke(replyMessage);
|
|
ResetState();
|
|
}
|
|
|
|
private void HandleStatusMessage(INetIncomingMessage message)
|
|
{
|
|
NetConnectStatus status = message.ConnectStatus;
|
|
// connect success
|
|
// will send data in next frame
|
|
if (status == NetConnectStatus.Connected && rpcState == RpcState.Connecting)
|
|
{
|
|
rpcState = RpcState.PrepareSend;
|
|
}
|
|
// waiting server reply but the connection disconnected
|
|
// reply error
|
|
else if (status == NetConnectStatus.Disconnected && rpcState == RpcState.WaitingRsp)
|
|
{
|
|
replyMessage.isSuccess = false;
|
|
replyMessage.errorMsg = "Connection disconnected when waiting for reply;Maybe server not support the request";
|
|
replyMessage.payload = string.Empty;
|
|
currentMessage.callback?.Invoke(replyMessage);
|
|
ResetState();
|
|
}
|
|
|
|
}
|
|
|
|
private void ResetState()
|
|
{
|
|
CancelInvoke(nameof(OnRequestTimeout));
|
|
currentMessage.callback = null;
|
|
rpcState = RpcState.None;
|
|
NetClient.Instance.Close(ConnectId);
|
|
NetClient.Instance.Shutdown();
|
|
}
|
|
|
|
private void OnRequestTimeout()
|
|
{
|
|
replyMessage.isSuccess = false;
|
|
replyMessage.errorMsg = "Waiting reply timeout";
|
|
replyMessage.payload = string.Empty;
|
|
remainRetryCount--;
|
|
if (remainRetryCount < 0)
|
|
{
|
|
currentMessage?.callback.Invoke(replyMessage);
|
|
ResetState();
|
|
}
|
|
else
|
|
{
|
|
CancelInvoke(nameof(OnRequestTimeout));
|
|
// start timeout
|
|
Invoke(nameof(OnRequestTimeout),requestTimeout);
|
|
rpcState = RpcState.None;
|
|
NetClient.Instance.Close(ConnectId);
|
|
// try reconnect
|
|
Connect(ConnectId, ipAddress, port);
|
|
}
|
|
}
|
|
|
|
[Conditional("BF_DEBUG")]
|
|
private static void Debug(string message)
|
|
{
|
|
UnityEngine.Debug.Log(message);
|
|
}
|
|
}
|
|
} |