您当前的位置: 首页 >  服务器

寒冰屋

暂无认证

  • 0浏览

    0关注

    2286博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

带有服务器回调的通用客户端——服务器 DotNet远程处理

寒冰屋 发布时间:2021-06-16 23:14:42 ,浏览量:0

目录

介绍

DotNet 远程处理框架

约束、限制

客户端——服务器定义

接口

服务器通知参数

服务器异常

服务器实现

客户端实现

如何测试示例

本文中的项目

结论

  • 下载演示 - 194 KB
  • 下载源码 - 114.5 KB

介绍

在我担任计算机数字控制 (CNC) 高级软件工程师期间,我有机会开发一个涵盖广泛应用的框架。本文专门介绍了具有服务器回调通知的通用客户端——服务器通信框架。

后续文章将重点介绍使用log4net和DbgView进行诊断、常见的面向WinForm的框架以及用于查看日志文件和实现框架的LogViewer应用程序作为示例。

简单就是美,我已经从框架中删除了本文不需要的所有内容。

本文仅描述在客户端——服务器通信中实现通用远程处理框架必须了解的内容。类的详细情况汇报给每个人欣赏。

DotNet 远程处理框架

在Client-Server通信中,对于客户端来说重要的是它能够随时连接到服务器,无论是在客户端之前或之后启动服务器还是由于某种原因中断了通信并稍后重新建立。对于服务器来说,重要的是它能够识别客户端是否连接,是否断开连接,或者通信由于某种原因被切断并稍后重新建立。

客户端——服务器通信中最重要的事情是服务器应该能够向客户端返回通知。

约束、限制

通用远程处理框架假定多个客户端可以连接到服务器,但服务器只有一个Sink用于所有客户端。它还假定客户端和服务器之间的连接是永久的,没有租用。

客户端——服务器定义

在实现客户端——服务器通信之前,有必要定义两件事。

  1. 服务器呈现给客户端的接口。
  2. 服务器用于通知客户端的通知参数的类型。

由于此信息由服务器和客户端使用,因此需要在客户端和服务器的公共项目中定义它们。对于本文中的测试示例,常用项目是TestLibrary。客户端项目是TestClient,服务器是TestServer.

接口

对于此测试示例,接口如下所示:

namespace TestLibrary
{
    /// Interface provided by the server for clients.
    public interface ITestInterface : IConnect
    {
        /// The server will send this message back to the clients asynchronously
        void Execute(string message);

        /// The server will send this message back to the clients asynchronously
        void Execute(Guid clientID, string message);

        /// The server will send this message back to the clients synchronously.
        void ExecuteSync(string message);

        /// The server will generate an exception.
        void ExecuteException();

        /// The server will notify an exception.
        void ExecuteNotifyException();
    }
}

 

接口必须始终派生自IConnect! IConnect接口在RemoteServer类服务器端和RemoteConnector类客户端中实现。只有您的接口是使用这两个基类来实现的。

可以在此接口中定义任何方法,但必须注意所使用的方法参数必须是可序列化的。本示例定义了 fife 方法,但数量不限。这些方法只是向服务器发送消息或要求服务器生成异常。服务器实现将通过异步或同步通知将消息发送回客户端或生成异常作为示例。

服务器通知参数

服务器通知参数如下所示:

namespace TestLibrary
{
    [Serializable]
    public class TestMessageArgs : EventArgs
    {
        /// The message.
        public string Message { get; }

        /// Constructs a TestMessageArgs object.
        public TestMessageArgs(string message)
        {
            Message = message;
        }
    }
}

 

它应该来自EventArgs但更重要的是它应该是可序列化的。

该服务器上的Executes(..)信息将发回消息通知到使用此参数的客户端。

服务器异常

有两种异常类型。通知异常或服务器抛出的异常。您可以在服务器上捕获服务器异常并通过NotifyExceptionArgs通知通知客户端发生了异常,或者您可以在客户端上捕获通用服务器异常。可以在公共库中定义一个异常,但该异常应该是可序列化的。

服务器实现

服务器实现如下所示:

namespace TestServer
{
	/// Class to manage a TestServer.
	public class TestServer : RemoteServer
	{
		/// Constructs a TestServer object, the base class has no default constructor.
		public TestServer(IChannel channel)
			: base(channel) { }

		/// Notify all clients
		public void SendNotificationToAllClients(string message)
		{
			Sink?.PerformNotifyClients(new TestMessageArgs(message));
		}
	}
}

 

该TestServer类从泛型的RemoteServer派生,并只用于定义Sink为T参数。

应用程序无法访问由DotNet远程处理创建RemoteServer的T接收器,但该类提供了一个接收器属性,该属性在且仅当第一个客户端连接时创建。一经创建,Sink即为永久。

RemoteServer类提供事件:

  • ClientConnect: 客户端连接
  • ClientDisconnect: 客户端断开连接
  • Starting: 服务器正在启动
  • Waiting: 服务器正在等待,可以从事件处理程序中取消服务器
  • Stopping: 服务器正在停止
  • Stopped: 服务器已停止

最重要的服务器端代码是Sink,根据定义服务器中只有一个 Sink。它实现了服务器接口。

namespace TestServer
{
    /// Class to manage the Server Sink defined by its Interface.
    public class TestSink : NotifyClientSink, ITestInterface
    {
        /// The server will send the message back to the clients asynchronously.
        public void Execute(string message)
        {
            PerformNotifyClients(new TestMessageArgs(message));
        }

        /// The server will send the message back to the client asynchronously.
        public void Execute(Guid clientID, string message)
        {
            PerformNotifyClient(clientID, new TestMessageArgs(message));
        }

        /// The server will send the message back to the clients synchronously.
        public void ExecuteSync(string message)
        {
            PerformNotifyClientsSync(new TestMessageArgs(message));
        }

        /// The server will generate an exception.
        public void ExecuteException()
        {
            throw new NotImplementedException("ExecuteException");
        }

        /// The server will notify an exception.
        public void ExecuteNotifyException()
        {
            try
            {
                // server code that generates an exception
                throw new ApplicationException("ExecuteNotifyException");
            }
            catch (Exception ex)
            {
                PerformNotifyClients(new NotifyExceptionArgs(ex));
            }
        }
    }
}

 

Sink简单地异步或同步被接收的消息通知客户端或客户端。它还会生成通知异常和服务器异常。

注意:如果通知是同步的,服务器会被阻塞,直到客户端将控制权交还。

客户端实现

客户端实现如下所示:

namespace TestClient
{
    /// 
    /// Class to manage a TestClient
    /// 
    public class TestClient : IDisposable
    {
        private Delay _delay;
        private RemoteConnector _remoteConnector;
        private Guid ClientID => _remoteConnector.ClientID;
        private ITestInterface TestProvider => _remoteConnector.RemoteObject;

        public RemoteState RemoteState => _remoteConnector.RemoteState;

        /// Dispose this object
        public void Dispose()
        {
            _remoteConnector?.Dispose();
        }

        /// 
        /// Initialize a remote connector to the server giving the channel.
        /// 
        /// The channel.
        public void Initialize(IChannel channel)
        {
            // instantiate the client
            _remoteConnector = new RemoteConnector(new CHANNEL());
            _remoteConnector.Callback += OnRemoteCallback;
            _remoteConnector.RemoteStateChanged += OnRemoteStateChanged;
        }

        /// 
        /// Notification from the server to the client.
        /// 
        /// The sender is the client ID.
        /// The event argument.
        private void OnRemoteCallback(object sender, EventArgs e)
        {
            // is notification for me ?
            if ((Guid)sender != ClientID)
                return;

            // the server sends me a message
            if (e is TestMessageArgs args1)
                Console.WriteLine("Message from Server: " + args1.Message);

            // the server sends me an exception
            else if (e is NotifyExceptionArgs args2)
                Console.WriteLine("Exception from Server: " + args2.Exception.Message);
        }

        /// 
        /// Notification when the remote connection is changed.
        /// 
        /// The sender.
        /// The new remote state.
        private void OnRemoteStateChanged(object sender, RemoteState remoteState)
        {
            switch (remoteState)
            {
                case RemoteState.Connected:
                    Console.WriteLine("RemoteState: [" + RemoteState +
                    "] Client [" + ClientID +
                    "] Delay [" + _delay.TotalMilliseconds + "] ms");
                    // execute commands on the server
                    ExecuteCommandsOnServer();
                    break;
                default:
                    Console.WriteLine("RemoteState: [" + RemoteState + "]");
                    break;
            }
        }

        /// Starts the connection with the server.
        public void Start()
        {
            if (_remoteConnector == null)
                throw new ApplicationException("Please call Initialize before Start");
            _delay = new Delay();
            _remoteConnector.Start(10); // wait 10s to connect to the server else exception
        }

        /// Stops the connection with the server.
        public void Stop()
        {
            _remoteConnector?.Stop();
        }

        /// Execute commands on the server.
        private void ExecuteCommandsOnServer()
        {
            // the server will send back notification asynchronously to all clients
            for (int i = 1; i             
关注
打赏
1665926880
查看更多评论
0.0490s