private bool Reconnect(string handle, HoxisProtocolArgs args) { long uid = FF.StringToLong(args["uid"]); List <HoxisConnection> workers = HoxisServer.Ins.GetWorkingConnections(); foreach (HoxisConnection w in workers) { // If already signed in, response the state to let user choose if reconnecting if (w.user == this) { continue; } if (w.user.userID <= 0) { continue; } if (w.user.userID == uid) { userID = w.user.userID; realtimeData = w.user.realtimeData; Continue(); if (DebugRecorder.LogEnable(_logger)) { _logger.LogInfo("reconnect", ""); } HoxisServer.Ins.AffairEntry(C.AFFAIR_RELEASE_CONNECTION, w); return(ResponseSuccess(handle, "ReconnectCb")); } } return(Response(handle, "ReconnectCb", new KVString("code", C.RESP_NO_USER_INFO))); }
/// <summary> /// **WITHIN THREAD** /// The entrance of protocol bytes /// Called by HoxisClient /// </summary> /// <param name="data"></param> public void ProtocolEntry(byte[] data) { string json = FF.BytesToString(data); HoxisProtocol proto = FF.JsonToObject <HoxisProtocol>(json); _protoQueue.Enqueue(proto); }
/// <summary> /// Fill this HoxisUser with an connected user /// </summary> /// <param name="handle"></param> /// <param name="args"></param> /// <returns></returns> private bool SignIn(string handle, HoxisProtocolArgs args) { Ret ret; long uid = FF.StringToLong(args["uid"]); if (uid <= 0) { return(ResponseError(handle, C.RESP_ILLEGAL_ARGUMENT, FF.StringFormat("illegal argument: {0}", args["uid"]))); } userID = uid; connectionState = UserConnectionState.Default; if (DebugRecorder.LogEnable(_logger)) { _logger.LogInfo("sign in", ""); } else { _logger = new DebugRecorder(FF.StringAppend(HoxisServer.basicPath, @"logs\users\", NewUserLogName(uid)), out ret); if (ret.code != 0) { Console.WriteLine(ret.desc); } else { _logger.Begin(); _logger.LogInfo("sign in", ""); } } return(ResponseSuccess(handle, "SignInCb")); }
/// <summary> /// Post a protocol to client /// </summary> /// <param name="proto"></param> public void ProtocolPost(HoxisProtocol proto) { string json = FF.ObjectToJson(proto); byte[] data = FF.StringToBytes(json); OnPost(data); }
private void ProcessProtocol(object state) { HoxisProtocol proto = (HoxisProtocol)state; OnProtocolEntry(proto); switch (proto.type) { case ProtocolType.Response: ReqHandle handle = FF.JsonToObject <ReqHandle>(proto.handle); // todo 消除等待 if (proto.err != C.RESP_SUCCESS) { OnResponseError(proto.err, proto.desc); return; } respCbTable[proto.action.method](proto.action.args); break; case ProtocolType.Synchronization: HoxisAgent agent = GetAgent(proto.sender.aid); if (agent != null) { agent.CallBehaviour(proto.action); } break; case ProtocolType.Proclamation: break; } }
public void SendProtocol() { Dictionary <string, string> args = new Dictionary <string, string>(); foreach (KVString kv in arguments) { args.Add(kv.key, kv.val); } HoxisProtocol proto = new HoxisProtocol { type = protocolType, handle = FF.ObjectToJson(new ReqHandle { req = method, ts = SF.GetTimeStamp(TimeUnit.Millisecond) }), err = error, receiver = new HoxisProtocolReceiver { type = receiverType, uid = receiverUID }, sender = new HoxisProtocolSender { uid = senderUID, aid = senderAgentID, loopback = loopback }, action = new HoxisProtocolAction(method, new HoxisProtocolArgs(args)), desc = description }; HoxisDirector.Ins.ProtocolPost(proto); }
public HoxisServer(string projectArg, string versionArg, bool autoStart = false) { if (Ins == null) { Ins = this; } Ret ret; project = projectArg; version = versionArg; // Init and begin log recording _logger = new DebugRecorder(FF.StringAppend(basicPath, @"logs\server.log"), out ret); if (ret.code != 0) { Quit(); } _logger.Begin(); _logger.LogTitle("David.Claude", project, version); // Auto start if (autoStart) { InitializeConfig(out ret); if (ret.code != 0) { Quit(); } Listen(); BeginAccept(); BeginProcess(); BeginHeartbeatUpdate(); } }
/// <summary> /// Get config of long type /// </summary> /// <param name="section"></param> /// <param name="key"></param> /// <param name="ret"></param> /// <returns></returns> public long GetLong(string section, string key, out Ret ret) { if (!ContainItem(section, key, out ret)) { return(0); } return(FF.StringToLong(_config[section][key], out ret)); }
/// <summary> /// Get config of float type /// </summary> /// <returns>The float.</returns> /// <param name="section">Section.</param> /// <param name="key">Key.</param> /// <param name="ret">Ret.</param> public float GetFloat(string section, string key, out Ret ret) { if (!ContainItem(section, key, out ret)) { return(0f); } return(FF.StringToFloat(_config[section][key], out ret)); }
/// <summary> /// Get config of bool type /// </summary> /// <returns><c>true</c>, if bool was gotten, <c>false</c> otherwise.</returns> /// <param name="section">Section.</param> /// <param name="key">Key.</param> /// <param name="ret">Ret.</param> public bool GetBool(string section, string key, out Ret ret) { if (!ContainItem(section, key, out ret)) { return(false); } return(FF.StringToBool(_config[section][key], out ret)); }
/// <summary> /// Get config of short type /// </summary> /// <param name="section"></param> /// <param name="key"></param> /// <param name="ret"></param> /// <returns></returns> public short GetShort(string section, string key, out Ret ret) { if (!ContainItem(section, key, out ret)) { return(0); } return(FF.StringToShort(_config[section][key], out ret)); }
/// <summary> /// Read the .toml file into _config /// </summary> /// <param name="path"></param> /// <param name="ret"></param> public void ReadFile(string path, out Ret ret) { _config = new Dictionary <string, Dictionary <string, string> >(); // Is toml file ? string[] paths = FF.StringSplit(path, '/'); string[] name = FF.StringSplit(FF.LastOfArray(paths), '.'); if (FF.LastOfArray(name) != "toml") { ret = new Ret(LogLevel.Error, RET_NOT_TOML, "File:" + path + " isn't .toml"); return; } // Does file exist ? if (!File.Exists(path)) { ret = new Ret(LogLevel.Error, RET_NO_FILE, "File:" + path + " doesn't exist"); return; } // Push toml to config string curSec = ""; string[] lines = File.ReadAllLines(path); foreach (string line in lines) { string lineTrim = line.Trim(); if (FF.RegexMatch(lineTrim, @"^#.*")) { continue; } string[] vals = FF.RegexGetValue(lineTrim, @"^\[.+\]$", "[", "]"); if (vals != null) { if (vals.Length == 1) { string section = vals[0]; if (!_config.ContainsKey(section)) { _config.Add(section, new Dictionary <string, string>()); } curSec = section; continue; } } if (curSec == "") { continue; } string[] kv = lineTrim.Split('='); if (kv.Length != 2) { continue; } _config[curSec].Add(kv[0].Trim(), kv[1].Trim()); } ret = Ret.ok; }
private bool SetDefaultConnectionState(string handle, HoxisProtocolArgs args) { if (connectionState == UserConnectionState.Active) { connectionState = UserConnectionState.Default; return(ResponseSuccess(handle, "SetDefaultConnectionStateCb")); } else if (connectionState == UserConnectionState.Default) { return(Response(handle, "SetDefaultConnectionStateCb", new KVString("code", C.RESP_SET_DEFAULT_ALREADY))); } return(ResponseError(handle, C.RESP_SET_STATE_UNABLE, FF.StringFormat("current connection state is {0}", connectionState.ToString()))); }
// Use this for initialization void Start() { HoxisDirector.Ins.onResponseError += (err, desc) => { LogAffairEntry(FF.StringFormat("response err: {0}, {1}", err, desc), LogLevel.Error); }; HoxisDirector.Ins.onProtocolEntry += (proto) => { LogAffairEntry(FF.StringFormat("protocol entry: {0}", FF.ObjectToJson(proto))); }; HoxisDirector.Ins.onProtocolPost += (proto) => { LogAffairEntry(FF.StringFormat("protocol post: {0}", FF.ObjectToJson(proto))); }; HoxisDirector.Ins.onAffairInitError += (ret) => { LogAffairEntry(ret.desc, LogLevel.Error); }; HoxisDirector.Ins.onAffairConnected += () => { LogAffairEntry(FF.StringFormat("connect to {0}", HoxisClient.Ins.serverIP)); }; HoxisDirector.Ins.onAffairConnectError += (ret) => { LogAffairEntry(ret.desc, LogLevel.Error); }; HoxisDirector.Ins.onAffairClosed += () => { LogAffairEntry("close success"); }; HoxisDirector.Ins.onAffairClosedError += (ret) => { LogAffairEntry(ret.desc, LogLevel.Error); }; HoxisDirector.Ins.onAffairNetworkAnomaly += (ret) => { LogAffairEntry(ret.desc, LogLevel.Error); }; _logPanel.GetComponent <RectTransform>().localPosition = _logPanelOffPosition; _logPanelOn = false; HoxisDirector.Ins.AwakeIns(); }
//public static void TestRelease(HoxisConnection conn) { Ret ret; lock (conn) _connReception.Release(conn, out ret); } #region management //public static bool ManageCluster(ManageOperation op, HoxisUser sponsor) //{ // switch (op) // { // case ManageOperation.Create: // string cid = FF.StringAppend(sponsor.userID.ToString(), "@", SF.GetTimeStamp().ToString()); // if (_clusters.ContainsKey(cid)) { Console.WriteLine("[error]Create cluster: cluster {0} already exists", cid); return false; } // lock (_clusters) // { // HoxisCluster hc = new HoxisCluster(cid); // _clusters.Add(cid, hc); // Ret ret; // hc.UserJoin(sponsor, out ret); // if (ret.code != 0) { Console.WriteLine("[warning]Create cluster: {0}", ret.desc); return false; } // } // break; // case ManageOperation.Join: // // todo Call matching sdk, get a cluster // break; // case ManageOperation.Leave: // break; // case ManageOperation.Destroy: // break; // } // return true; //} #endregion private void ProcessAffair(object state) { KV <int, object> affair = (KV <int, object>)state; switch (affair.key) { case C.AFFAIR_RELEASE_CONNECTION: Ret ret; HoxisConnection conn = (HoxisConnection)affair.val; lock (conn) { _connReception.Release(conn, out ret); } if (ret.code != 0) { _logger.LogWarning(ret.desc, "Affair"); return; } break; } _logger.LogInfo(FF.StringFormat("{0} processed", affair.key), "Affair", true); }
public void LogTag(string content, string tag = "", string speaker = "", bool console = false) { string s; if (speaker == "") { s = FF.StringFormat("[{0}]{1}: {2}", SF.GetDateTime(), tag, content); } else { s = FF.StringFormat("[{0}]{1}: {2}--->{3}", SF.GetDateTime(), tag, speaker, content); } _writer.WriteLine(s); if (console) { Console.WriteLine(s); } }
/// <summary> /// Rapidly send a request protocol /// </summary> /// <param name="method"></param> /// <param name="kvs"></param> public void Request(string method, params KVString[] kvs) { HoxisProtocol proto = new HoxisProtocol { type = ProtocolType.Request, handle = FF.ObjectToJson(new ReqHandle { req = method, ts = SF.GetTimeStamp(TimeUnit.Millisecond) }), err = "", receiver = HoxisProtocolReceiver.undef, sender = HoxisProtocolSender.undef, action = new HoxisProtocolAction(method, kvs), desc = "" }; ProtocolPost(proto); // todo wait for response }
public void Continue() { Ret ret; connectionState = UserConnectionState.Active; if (!DebugRecorder.LogEnable(_logger)) { _logger = new DebugRecorder(FF.StringAppend(HoxisServer.basicPath, @"logs\users\", NewUserLogName(userID)), out ret); if (ret.code != 0) { Console.WriteLine(ret.desc); } else { _logger.Begin(); } } }
/// <summary> /// Begin accepting socket connection within thread /// </summary> public void BeginAccept() { _acceptThread = new Thread(() => { while (true) { Socket socket = _socket.Accept(); _logger.LogInfo(FF.StringAppend("accept new client: ", socket.RemoteEndPoint.ToString()), "Server", true); Ret ret; HoxisConnection conn = _connReception.Request(socket, out ret); if (ret.code != 0) { _logger.LogWarning(ret.desc, socket.RemoteEndPoint.ToString()); continue; } _logger.LogInfo("request successful", conn.clientIP); } }); _acceptThread.Start(); _logger.LogInfo("accept begin...", "Server", true); }
/// <summary> /// Get the connection state if this user has already signed in /// </summary> /// <param name="handle"></param> /// <param name="args"></param> /// <returns></returns> private bool QueryConnectionState(string handle, HoxisProtocolArgs args) { long uid = FF.StringToLong(args["uid"]); if (uid <= 0) { return(ResponseError(handle, C.RESP_ILLEGAL_ARGUMENT, FF.StringFormat("illegal argument: {0}", args["uid"]))); } HoxisUser user = HoxisServer.Ins.GetUser(uid); if (user == null) { return(Response(handle, "QueryConnectionStateCb", new KVString("code", C.RESP_NO_USER_INFO))); } if (user == this) { return(Response(handle, "QueryConnectionStateCb", new KVString("code", C.RESP_NO_USER_INFO))); } return(ResponseSuccess(handle, "QueryConnectionStateCb", new KVString("state", user.connectionState.ToString()))); }
/// <summary> /// Check the request name and time stamp /// </summary> /// <param name="proto"></param> /// <param name="ret"></param> public void CheckRequest(HoxisProtocol proto, out Ret ret) { ReqHandle handle = FF.JsonToObject <ReqHandle>(proto.handle, out ret); if (ret.code != 0) { return; } // Check if request name matches method name if (handle.req != proto.action.method) { ret = new Ret(LogLevel.Info, 1, "request name doesn't match method name"); return; } // Check if expired long ts = handle.ts; long intv = Math.Abs(SF.GetTimeStamp(TimeUnit.Millisecond) - ts); if (intv > requestTTL) { ret = new Ret(LogLevel.Info, 1, "request is expired"); return; } ret = Ret.ok; }
/// <summary> /// New log name of an user /// </summary> /// <param name="uid"></param> /// <returns></returns> public static string NewUserLogName(long uid) { return(FF.StringAppend(uid.ToString(), "@", SF.GetTimeStamp().ToString(), ".log")); }
/// <summary> /// **WITHIN THREAD** /// The entrance of protocol bytes /// </summary> /// <param name="data"></param> public void ProtocolEntry(byte[] data) { string json = FF.BytesToString(data); HoxisProtocol proto = FF.JsonToObject <HoxisProtocol>(json); switch (proto.type) { case ProtocolType.Synchronization: switch (proto.receiver.type) { case ReceiverType.Cluster: if (realtimeData.parentCluster == null) { return; } realtimeData.parentCluster.ProtocolBroadcast(proto); break; case ReceiverType.Team: if (realtimeData.parentTeam == null) { return; } realtimeData.parentTeam.ProtocolBroadcast(proto); break; case ReceiverType.User: HoxisUser user = HoxisServer.Ins.GetUser(proto.receiver.uid); // todo send break; } break; case ProtocolType.Request: // Request check Ret ret; CheckRequest(proto, out ret); if (ret.code != 0) { ResponseError(proto.handle, C.RESP_CHECK_FAILED, ret.desc); return; } // Check ok if (!respTable.ContainsKey(proto.action.method)) { if (DebugRecorder.LogEnable(_logger)) { _logger.LogError(FF.StringFormat("invalid request: {0}", proto.action.method), "", true); } ResponseError(proto.handle, C.RESP_CHECK_FAILED, FF.StringFormat("invalid request: {0}", proto.action.method)); return; } respTable[proto.action.method](proto.handle, proto.action.args); break; default: if (DebugRecorder.LogEnable(_logger)) { _logger.LogError(FF.StringFormat("invalid protocol type: {0}", proto.type), ""); } break; } }
/// <summary> /// Init the configuration, such as the ip, port, socket and arguments /// </summary> /// <param name="configPath"></param> public void InitializeConfig(out Ret ret, string configPath = "") { // Init config string path; if (configPath != "") { path = configPath; } else { path = FF.StringAppend(basicPath, @"..\..\DacLib\Hoxis\Configs\hoxis_server.toml"); } config = new TomlConfiguration(path, out ret); if (ret.code != 0) { _logger.LogFatal(ret.desc, "Server", true); return; } _logger.LogInfo("read configuration success", "Server"); // Assign ip, port and init the sokcet localIP = SF.GetLocalIP(out ret); if (ret.code != 0) { _logger.LogFatal(ret.desc, "Server"); return; } port = config.GetInt("server", "port", out ret); if (ret.code != 0) { _logger.LogFatal(ret.desc, "Server"); return; } _logger.LogInfo(FF.StringFormat("ip is {0}, port is {1}", localIP, port.ToString()), "Server"); // Init connection reception maxConn = config.GetInt("server", "max_conn", out ret); if (ret.code != 0) { _logger.LogFatal(ret.desc, "Server"); return; } _connReception = new CriticalPreformPool <HoxisConnection>(maxConn); _logger.LogInfo(FF.StringFormat("max connections is {0}", maxConn), "Server"); // Init affair queue affairQueueCapacity = config.GetInt("server", "affair_queue_capacity", out ret); if (ret.code != 0) { _logger.LogFatal(ret.desc, "Server"); return; } _logger.LogInfo(FF.StringFormat("affair queue capacity is {0}", affairQueueCapacity), "Server"); affairQueueProcessQuantity = config.GetShort("server", "affair_queue_process_quantity", out ret); if (ret.code != 0) { _logger.LogFatal(ret.desc, "Server"); return; } _logger.LogInfo(FF.StringFormat("affair queue process quantity is {0}", affairQueueProcessQuantity), "Server"); _affairQueue = new FiniteProcessQueue <KV <int, object> >(affairQueueCapacity, affairQueueProcessQuantity); _affairQueue.onProcess += ProcessAffair; affairQueueProcessInterval = config.GetInt("server", "affair_queue_process_interval", out ret); if (ret.code != 0) { _logger.LogFatal(ret.desc, "Server"); return; } _logger.LogInfo(FF.StringFormat("affair queue process interval is {0}ms", affairQueueProcessInterval), "Server"); // Init heartbeat update heartbeatUpdateInterval = config.GetInt("server", "heartbeat_update_interval", out ret); if (ret.code != 0) { _logger.LogFatal(ret.desc, "Server"); return; } _logger.LogInfo(FF.StringFormat("heartbeat update interval is {0}ms", heartbeatUpdateInterval), "Server"); // Init connection HoxisConnection.readBufferSize = config.GetInt("conn", "read_buffer_size", out ret); if (ret.code != 0) { _logger.LogFatal(ret.desc, "Server"); return; } _logger.LogInfo(FF.StringFormat("read buffer size of connection is {0}", HoxisConnection.readBufferSize), "Server"); // Init cluster _clusters = new Dictionary <string, HoxisCluster>(); HoxisCluster.maxUser = config.GetInt("cluster", "max_user", out ret); if (ret.code != 0) { _logger.LogFatal(ret.desc, "Server"); return; } _logger.LogInfo(FF.StringFormat("max users of cluster is {0}", HoxisCluster.maxUser), "Server"); // Init team HoxisTeam.maxUser = config.GetInt("team", "max_user", out ret); if (ret.code != 0) { _logger.LogFatal(ret.desc, "Server"); return; } _logger.LogInfo(FF.StringFormat("max users of team is {0}", HoxisTeam.maxUser), "Server"); // Init user HoxisUser.requestTTL = config.GetLong("user", "request_ttl", out ret); if (ret.code != 0) { _logger.LogFatal(ret.desc, "Server"); return; } _logger.LogInfo(FF.StringFormat("request time to live is {0}ms", HoxisUser.requestTTL), "Server"); HoxisUser.heartbeatTimeout = config.GetInt("user", "heartbeat_timeout", out ret); if (ret.code != 0) { _logger.LogFatal(ret.desc, "Server"); return; } _logger.LogInfo(FF.StringFormat("heartbeat timeout is {0}ms", HoxisUser.heartbeatTimeout), "Server"); _logger.LogInfo("init success", "Server", true); }