Generally, the services invoked by remote interfaces are based on the client's active invocation of the server, which provides the related interface services. In the new version of XRPC, a new function is introduced, that is, two-way interface communication. Component provides services to create an interface proxy for a client session and invoke the interface services provided by the client.Next, it describes how to use XRPC to use the interface for two-way communication, and implements the local method of calling WFP/WINFROM clients on the server side.
Reference Component
The components provide two packages: BeetleX.XRPC and BeetleX.XRPC.Clients; the former is for use on.net core, and the latter is for use on WFP/WINFROM. Two-way interface invocation has been supported since version 0.8.2.3.
Use
Components are based on interfaces, so service invocation logic must be described as interfaces.Next, implement a simple interface where the client calls the registration method to the service, which creates a session proxy after accepting the client registration and invokes the time to get the client.The interface is defined as follows:
public interface IUser { Task Login(string name); Task<DateTime> GetTime(); }
The interface is relatively simple, Login and GetTime. Next, the interface is implemented on both the server and the client, and a two-way call is made.
Server-side implementation
[Service(typeof(IUser))] public class Program : IUser { static void Main(string[] args) { var builder = new HostBuilder() .ConfigureServices((hostContext, services) => { services.UseXRPC(s => { s.ServerOptions.LogLevel = BeetleX.EventArgs.LogType.Warring; s.ServerOptions.DefaultListen.Port = 9090; s.RPCOptions.ParameterFormater = new JsonPacket();//default messagepack }, typeof(Program).Assembly); }); builder.Build().Run(); } public Task<DateTime> GetTime() { return DateTime.Now.ToTask(); } public Task Login(string name) { Console.WriteLine($"{name} login"); var token = XRPCServer.EventToken; Task.Run(async () => { IUser user = token.Server.GetClient<IUser>(token.Session); while (true) { var time = await user.GetTime(); Console.WriteLine($"{name}[{token.Session.RemoteEndPoint}] time is:{time}"); //await Task.Delay(1000); } }); return Task.CompletedTask; } }
The code is simple, create an asynchronous method in the login method, create an IUser proxy in the method for the current session, and then loop through the client method to get the appropriate time.
Client implementation
class Program : IUser { static XRPCClient client; static void Main(string[] args) { client = new XRPCClient("192.168.2.18", 9090); client.PingTimeout = 5; client.Options.ParameterFormater = new JsonPacket(); client.Register<IUser>(new Program()); var user = client.Create<IUser>(); user.Login("henry"); System.Threading.Thread.Sleep(-1); } public Task<DateTime> GetTime() { return Task.FromResult(DateTime.Now); } public Task Login(string name) { return Task.CompletedTask; } }
The code required by the client is simpler than that of the server; register the local implementation class of the related interface through XRPCClient.Create.As long as the client calls user.Login("henry"); the back-end keeps getting time from the client.By running the program, you can see the following results:
The complete code above can be obtained from the following connections: https://github.com/IKende/BeetleX-Samples/tree/master/XRPC.InterfaceTwoWay
Chat Service
The convenience of interface two-way invocation has been described above, and then a chat service is quickly implemented through interface two-way invocation.
public interface IUser { Task Login(string name); Task Talk(string name, string message); Task Exit(string name); }
The above is a user chat behavior interface, which logs on, exits and sends messages.Next, a simple chat service can be completed by implementing this interface between the server and the client.
Server-side implementation
[EventNext.Service(typeof(IUser))] public class UserImpl : IUser { public Task Exit(string name) { return Task.CompletedTask; } public Task Login(string name) { var token = XRPCServer.EventToken; token.Session.Name = name; foreach (var session in token.Server.Server.GetOnlines()) { if (!string.IsNullOrEmpty(session.Name)) { IUser user = token.Server.GetClient<IUser>(session); user.Login(name); } } return Task.CompletedTask; } public Task Talk(string name, string message) { var token = XRPCServer.EventToken; if (string.IsNullOrEmpty(token.Session.Name)) { throw new Exception("Invalid login!"); } foreach (var session in token.Server.Server.GetOnlines()) { if (!string.IsNullOrEmpty(session.Name)) { IUser user = token.Server.GetClient<IUser>(session); user.Talk(session.Name, message); } } return Task.CompletedTask; } }
The server implements two main methods, landing and sending messages. Both methods are basically the same, in which the IUser proxy for all sessions is obtained and the related methods are executed.Why isn't Exit implemented? The service handles disconnection events mainly by listening for them. The code is as follows:
static void Main(string[] args) { var builder = new HostBuilder() .ConfigureServices((hostContext, services) => { services.UseXRPC(s => { s.ServerOptions.LogLevel = BeetleX.EventArgs.LogType.Debug; s.ServerOptions.DefaultListen.Port = 9090; s.RPCOptions.ParameterFormater = new JsonPacket();//default messagepack s.RPCDisconnect += (o, e) => { foreach (var session in e.Server.GetOnlines()) { if (session != e.Session && !string.IsNullOrEmpty(session.Name)) { IUser user = s.GetClient<IUser>(session); user.Exit(e.Session.Name); } } }; }, typeof(Program).Assembly); }); builder.Build().Run(); }
This is a simple chat service. Next, let's take a look at how clients implement this interface to complete their functions.
Client implementation
/// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window, IUser { public MainWindow() { InitializeComponent(); } public Task Login(string name) { AddMessage(name, "login"); return Task.CompletedTask; } public Task Exit(string name) { AddMessage(name, "exit"); return Task.CompletedTask; } public Task Talk(string name, string message) { AddMessage(name, message); return Task.CompletedTask; } private BeetleX.XRPC.Clients.XRPCClient mClient; private IUser mUser; private void Window_Loaded(object sender, RoutedEventArgs e) { mClient = new BeetleX.XRPC.Clients.XRPCClient("192.168.2.18", 9090); mClient.Options.ParameterFormater = new JsonPacket(); mClient.Register<IUser>(this); mUser = mClient.Create<IUser>(); txtMessages.Document.Blocks.Clear(); } private async void CmdLogin_Click(object sender, RoutedEventArgs e) { try { if (string.IsNullOrEmpty(txtName.Text)) { MessageBox.Show("Please enter a login name!"); return; } await mUser.Login(txtName.Text); MessageBox.Show("Landing Success!"); } catch (Exception e_) { MessageBox.Show(e_.Message); } } private async void CmdTalk_Click(object sender, RoutedEventArgs e) { try { await mUser.Talk(null, txtTalk.Text); } catch (Exception e_) { MessageBox.Show(e_.Message); } } }
The above is a WPF form implementation, is the code function simple, through remote method calls, the server can directly call the client form's method code.Next, take a look at the actual results:
This simple chat service is done, does it seem very simple or not; if you need to download the full code of the sample, you can access it: https://github.com/IKende/BeetleX-Samples/tree/master/XRPC.WFPChat
By using two-way interface invocation, you can achieve simpler communication application development because you no longer need to define message tags to distinguish processing behavior, which greatly improves development efficiency.