一般遠程接口調用的服務都是基於客戶端主動調用服務端,由服務端來提供相關的接口服務;在新版本的XRPC
中引入了一個新的功能,即接口雙向通訊,組件提供服務創建客戶會話的接口代理並調用客戶提供的接口服務。接下來介紹如何通過XRPC
來使用接口雙向通訊的應用,並實現服務端調用WFP/WINFROM
這些客戶端的本地方法。
引用組件
組件提供兩個包分別是:BeetleX.XRPC
和BeetleX.XRPC.Clients
;前者是在.net core
上使用,而后者則提供給WFP/WINFROM
使用.在0.8.2.3
版本開始支持接口雙向調用功能。
使用
組件是以接口作為基礎通訊單元,所以必須以接口的方式來描述服務調用邏輯。接下來實現一個簡單的接口,客戶端向服務調用注冊方法,服務端在接受客戶注冊后創建一個會話代理並調用獲取客戶端的時間。接口定義如下:
public interface IUser { Task Login(string name); Task<DateTime> GetTime(); }
接口比較簡單分別是Login
和GetTime
,接下來會分別在服務端和客戶端實現這一接口,並進行一個雙向調用。
服務端實現
[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; } }
代碼比較簡單,在登陸方法中創建一個異步方法,並在方法中創建一個IUser
針對當前會話的一個代理,然后循環調用客戶端方法獲取相應的時間。
客戶端實現
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; } }
比起服務端,客戶端所需要的代碼就更簡單了;通過XRPCClient.Create
注冊相關接口的本地實現類。只要客戶端調用user.Login("henry");
后服務端就是不停地向客戶獲取時間。通過運行程序可以看到以下運行結果:
以上完整代碼可以從以下連接獲取: https://github.com/IKende/BeetleX-Samples/tree/master/XRPC.InterfaceTwoWay
聊天服務
上面已經描述了接口雙向調用的便利性,接下來通過接口雙向調用快速地實現一個聊天服務。
public interface IUser { Task Login(string name); Task Talk(string name, string message); Task Exit(string name); }
以上是一個用戶聊天的行為接口,分別是登陸、退出和發送消息。接下來通過服務端和客戶端實現這一接口即可完成一個簡單的聊天服務。
服務端實現
[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("登陸無效!"); } 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; } }
服務端主要實現了兩個方法,分別是登陸和發送消息;兩個方法的都基本一樣,在方法調用里面獲取所有會話的IUser
代理,並執行相關方法即可。為什么Exit
這個方法沒有實現呢,主要是服務通過監聽連接斷開事件進行處理,代碼如下:
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(); }
這樣一個簡單的聊天服務就完成,接下來看一下客戶端同樣實現這一接口來完成功能。
客戶端實現
/// <summary> /// 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("請輸入登錄名稱!"); return; } await mUser.Login(txtName.Text); MessageBox.Show("登陸成功!"); } 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); } } }
以上是一個WPF
窗體的實現,代碼功能是不是很簡單,通過遠程方法調用,服務端可以直接調用客戶端窗體的方法代碼。接下來看一下實際運行效果:
這樣一個簡單了聊天服務就完成了,看上去是不是非常簡單;如果需要下載示例的完整代碼可以訪問: https://github.com/IKende/BeetleX-Samples/tree/master/XRPC.WFPChat
通過接口雙向調用的功能,你可以實現更簡單的通訊應用開發,因為你再也不需要定義消息標記來區分處理行為,可以大大地提高開發效率。