static async Task Main(string[] args) { // First argument: client number (otherwise 0) int clientNumber = 0; if (args.Length > 0 && int.TryParse(args[0], out int it)) { clientNumber = it; } clientID += "-" + clientNumber; // Welcome message Log.Write("Welcome to the test client: " + clientID); // RPC initialization var demoRpcConfig = new RpcClientConfig { ClientID = clientID, ServerUrl = "http://localhost:5000/rpc" }; RpcMain.InitRpcClient(demoRpcConfig, AuthenticateClient, () => new List <RpcFunctions> { new BankClientRpc() }, defaultTimeoutMs: 1000, new DemoRpcCommandBacklog()); // Run until killed while (true) { await Task.Delay(1000); } }
static async Task Main(string[] args) { // Welcome message Log.Write("Welcome to the simple demo client!"); // RPC initialization, using two server function classes var serverDemo = new DemoServerRpcStub(); var serverCalc = new CalcRpcStub(); var demoRpcConfig = new RpcClientConfig { ClientID = clientID, ServerUrl = "http://localhost:5000/rpc" }; RpcMain.InitRpcClient(demoRpcConfig, AuthenticateClient, () => new List <RpcFunctions> { new DemoClientRpc(), new CalcRpc() }); // Each few seconds, send commands to the server. Log the result. var random = new Random(); while (true) { try { // Send greeting Log.Write("Sending greeting..."); var greeting = new Greeting { Name = "Andi", MoreData = new SampleData { Text = $"Hi server, now it is {DateTime.Now}" } }; await serverDemo.SayHelloToServer(greeting); // Send calculation task. May fail on the remote side, when there is division by zero. Log.Write("Successfully greeted. Now sending a little calculation task:"); int a = random.Next(1, 100); int b = random.Next(0, 10); long startTime = CoreUtils.TimeNow(); try { int result = await serverCalc.DivideNumbers(a, b); long runTime = CoreUtils.TimeNow() - startTime; Log.Write($"{a}/{b}={result} (runtime: {runTime}ms)"); } catch (RpcException ex) { long runTime = CoreUtils.TimeNow() - startTime; Log.Write($"{a}+{b}=Fail! (runtime: {runTime}ms; {ex.Type}: {ex.Message})"); } } catch (RpcException ex) { Log.Write("Error: " + ex.Failure.Type + ": " + ex.Message); } // Wait a second before the next round await Task.Delay(1000); } }
static async Task Main(string[] args) { // First argument: client number int clientNumber = 0; if (args.Length > 0 && int.TryParse(args[0], out int it)) { clientNumber = it; } else { throw new Exception("Must be started with client number as parameter"); } clientID += "-" + clientNumber; // Welcome message Log.Write("Welcome to the test client: " + clientID); // RPC initialization var serverCalc = new CalcRpcStub(); var demoRpcConfig = new RpcClientConfig { ClientID = clientID, ServerUrl = "http://localhost:5000/rpc" }; RpcMain.InitRpcClient(demoRpcConfig, AuthenticateClient, () => new List <RpcFunctions> { new CalcRpc() }); // Each 0-100 ms, send a simple calculation task to the server: a + b = ? // a is an ascending number, starting from clientNumber * 1000 // b is a random number between 1 and 100. // Write the calculations in the file "{clientID}.calclog" (used in the RpcLibTest project) string filename = $"{clientID}.calclog"; File.Delete(filename); int a = clientNumber * 1000; var random = new Random(); while (true) { long startTime = CoreUtils.TimeNow();; a++; int b = random.Next(1, 100); try { int result = await serverCalc.AddNumbers(a, b); long runTime = CoreUtils.TimeNow() - startTime; Log.WriteToFile(filename, $"{a}+{b}={result} | {runTime}ms"); } catch (RpcException ex) { long runTime = CoreUtils.TimeNow() - startTime; Log.WriteToFile(filename, $"{a}+{b}=? | {runTime}ms | Fail: {ex.Type}: {ex.Message}"); } await Task.Delay(random.Next(0, 100)); } }
/// <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); } } } }); }
/// <summary> /// Initialize the RPC client with the given configuration, authentication method /// server-side RPC functions, optionally default timeout in ms and optionally backlog for retrying failed commands. /// </summary> public static void InitRpcClient(RpcClientConfig config, Action <HttpClient> auth, Func <List <RpcFunctions> > rpcFunctions, int?defaultTimeoutMs = null, IRpcCommandBacklog?commandBacklog = null) { // Set default timeout if (defaultTimeoutMs != null) { RpcCommand.defaultTimeoutMs = defaultTimeoutMs.Value; } // Start client RpcClientEngine.Instance.Start(rpcFunctions, config, auth, commandBacklog); }
static async Task Main(string[] args) { // First argument: client number (otherwise 0) int clientNumber = 0; if (args.Length > 0 && int.TryParse(args[0], out int it)) { clientNumber = it; } clientID += "-" + clientNumber; // Welcome message Log.Write("Welcome to the test client: " + clientID); // RPC initialization var bankServer = new BankServerRpcStub(); var demoRpcConfig = new RpcClientConfig { ClientID = clientID, ServerUrl = "http://localhost:5000/rpc" }; RpcMain.InitRpcClient(demoRpcConfig, AuthenticateClient, () => new List <RpcFunctions> { }, defaultTimeoutMs: 1000, new DemoRpcCommandBacklog()); // See the RetryOnClientTest test project to understand what we are doing now. // Repeatedly, get the current account balance and send an increasing amount (1 ct, 2ct, 3ct, ...) // to the bank, which is still offline at the beginning. This is done for 20 seconds. // Each 5 seconds, change the owner name. string filename = $"{clientID}.banklog"; // TODO bankServer.OnAddMoneyRetryFinished = (command) => // Log.WriteToFile(filename, $"{command.GetParam<int>(1)} | {command.GetResult().ResultJson} | retried"); for (int i = 1; i <= 20; i++) { // Get current balance (no retry!) long startTime = CoreUtils.TimeNow(); try { int newCents = await bankServer.GetBalance(clientNumber); long runTime = CoreUtils.TimeNow() - startTime; Log.WriteToFile(filename, $"Now | {newCents} | {runTime}ms"); } catch (RpcException ex) { long runTime = CoreUtils.TimeNow() - startTime; Log.WriteToFile(filename, $"Now | Fail: {ex.Type}: {ex.Message} | {runTime}ms"); } // Add money (retry for each command) startTime = CoreUtils.TimeNow();; try { int newCents = await bankServer.AddMoney(clientNumber, i); long runTime = CoreUtils.TimeNow() - startTime; Log.WriteToFile(filename, $"Add | {i} | {newCents} | {runTime}ms"); } catch (RpcException ex) { long runTime = CoreUtils.TimeNow() - startTime; Log.WriteToFile(filename, $"Add | {i} | Fail: {ex.Type}: {ex.Message} | {runTime}ms"); } // Change owner name (retry for newest call of command) if (i % 5 == 0) { startTime = CoreUtils.TimeNow(); string newName = "MyName-" + (i / 5); try { await bankServer.ChangeOwnerName(clientNumber, newName); long runTime = CoreUtils.TimeNow() - startTime; Log.WriteToFile(filename, $"Name | {newName} | {runTime}ms"); } catch (RpcException ex) { long runTime = CoreUtils.TimeNow() - startTime; Log.WriteToFile(filename, $"Name | {newName} | Fail: {ex.Type}: {ex.Message} | {runTime}ms"); } } await Task.Delay(1000); } // Finished. Close client. }