/// <summary> /// Gets the queue of the given client. /// If the client is unknown yet, it is created with the given backlog. /// </summary> public RpcPeerCache GetClient(string clientID, IRpcCommandBacklog?commandBacklog) { if (false == queues.TryGetValue(clientID, out var queue)) { queues[clientID] = queue = new RpcPeerCache(clientID, commandBacklog); } return(queue); }
/// <summary> /// Call this method at the beginning to enable the communication to the server, so that this client /// can both receive commands from the server and send commands to the server. /// </summary> /// <param name="clientMethods">A function which returns new instances of the client's RPC method implementations</param> /// <param name="clientConfig">The settings of this client</param> /// <param name="authAction">An action which authenticates the used HTTP client, e.g. by adding HTTP Basic Auth /// information according to the client.</param> /// <param name="commandBacklog">The backlog for storing failed commands to retry them later. May be null, /// when only <see cref="RpcRetryStrategy.None"/> will be used.</param> public void Start(Func <IEnumerable <RpcFunctions> > clientMethods, RpcClientConfig clientConfig, Action <HttpClient> authAction, IRpcCommandBacklog?commandBacklog) { if (isRunning) { return; } isRunning = true; // Remember client factory and settings this.clientMethods = clientMethods; this.clientConfig = clientConfig; serverCache = new RpcPeerCache(clientID: "", commandBacklog); // Create and authorize HTTP clients httpPull = new HttpClient(); httpPull.Timeout = TimeSpan.FromSeconds(RpcServerEngine.longPollingSeconds + 10); // Give some more seconds for timeout authAction(httpPull); httpPush = new HttpClient(); httpPush.Timeout = TimeSpan.FromMilliseconds(RpcCommand.defaultTimeoutMs); authAction(httpPush); // Loop to pull the next command for this client from the server, execute it (if not already executed before) // and send the response together with the next pull. _ = Task.Run(async() => { RpcCommandResult?lastResult = null; while (isRunning) { RpcCommand?next = null; try { next = await PullFromServer(lastResult); } catch { // Could not reach server. Try the same result report again. await Task.Delay(1000); // Wait a second before trying again } if (next != null) { lastResult = await ExecuteLocallyNow(next); } } }); // Loop to execute (i.e. send to server) the next command in the queue. _ = Task.Run(async() => { while (isRunning) { RpcCommand?next = await serverCache.DequeueCommand(timeoutMs: -1); // No timeout if (next != null) { next.SetState(RpcCommandState.Sent); await ExecuteOnServerNow(next); // In case of a networking problem, wait a second before trying the next command if (next.GetResult().Failure?.IsNetworkProblem ?? false) { await Task.Delay(1000); } } } }); }