• 《C#高级编程》读书笔记(十九):Windows服务


    1,Windows 服务

      Windows 服务是可以在系统启动时自动打开的程序。如果需要在没有用户交互操作情况下运行程序,或者在权限比交互式用户更大的用户下运行程序,就可以创建 Windows 服务。

    2,Windows 服务的体系架构

      操作 Windows 服务需要3种程序:

      • 服务程序

      • 服务控制程序

      • 服务配置程序

      服务程序本身用于提供需要的实际功能。

      服务控制程序可以把控制请求发送给服务,如开始、停止、暂停和继续。

      使用服务配置程序可以安装服务,也可以在以后改变服务的配置。

    3,服务程序

      服务程序实现服务的功能。服务程序需要 3 个部分:  

      • 主函数

      • service-main 函数

      • 处理程序

      服务的主函数是程序的一般入口点,即Main()方法,它可以注册多个 service-main 函数,service-main 函数包含服务的实际功能。服务必须为所提供的每项服务注册一个 service-main 函数。

    4,Windows 服务的类

      可以在System.ServiceProcess名称空间中找到实现服务的 3 部分的服务类:

       • 必须从 ServiceBase 类继承才能实现服务。ServiceBase 类用于注册服务、响应开始和停止请求。

       • ServiceController类用于实现服务控制程序。使用这个类,可以把请求发送给服务。

       • ServiceProcessInstaller类和ServiceInstaller类用于安装和配置服务程序。

    5,创建Windows服务程序

      实例程序对于客户发出的每一个请求,引用服务器都返回引用文件的一个随机引用。解决方案由 3 个程序集完成,一个用户客户端,两个用于服务器。

      QuoteServer 类库包含实际的功能,QuoteClient 是 WPF 胖客户端应用程序,这个应用程序创建客户端套接字,以便与 Quote Server 进行通信。第三个程序集是实际的服务。Quote Service 开始和停止 QuoteServer,服务将控制服务器。

      QuoteServer.cs

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Diagnostics;
      4 using System.Diagnostics.Contracts;
      5 using System.IO;
      6 using System.Linq;
      7 using System.Net;
      8 using System.Net.Sockets;
      9 using System.Text;
     10 using System.Threading.Tasks;
     11 
     12 namespace QuoteService
     13 {
     14     public class QuoteServer
     15     {
     16         private TcpListener listener;
     17         private int port;
     18         private string filename;
     19         private List<string> quotes;
     20         private Random random;
     21         private Task listenerTask;
     22 
     23         public QuoteServer()
     24             :this("quotes.txt")
     25         {
     26         }
     27 
     28         public QuoteServer(string filename) 
     29             : this(filename, 7890)
     30         {
     31         }
     32 
     33         public QuoteServer(string filename, int port)
     34         {
     35             Contract.Requires<ArgumentNullException>(filename != null);
     36             Contract.Requires<ArgumentException>(port >= IPEndPoint.MinPort &&
     37                 port <= IPEndPoint.MaxPort);
     38             this.filename = filename;
     39             this.port = port;
     40         }
     41 
     42         protected void ReadQuotes()
     43         {
     44             try
     45             {
     46                 quotes = File.ReadAllLines(filename).ToList();
     47                 if (quotes.Count == 0)
     48                 {
     49                     throw new QuoteException("quote file is empty");
     50                 }
     51                 random = new Random();
     52             }
     53             catch (IOException ex)
     54             {
     55                 throw new QuoteException("I/O Error",ex);
     56             }
     57         }
     58 
     59         protected string GetRandomQuoteOfTheDay()
     60         {
     61             var index = random.Next(0, quotes.Count);
     62             return quotes[index];
     63         }
     64 
     65         protected void Listener()
     66         {
     67             try
     68             {
     69                 IPAddress ipAddress = IPAddress.Any;
     70                 listener = new TcpListener(ipAddress, port);
     71                 listener.Start();
     72                 while (true)
     73                 {
     74                     Socket clientSocket = listener.AcceptSocket();
     75                     string message = GetRandomQuoteOfTheDay();
     76                     var encoder = new UnicodeEncoding();
     77                     byte[] buffer = encoder.GetBytes(message);
     78                     clientSocket.Send(buffer, buffer.Length, 0);
     79                     clientSocket.Close();
     80                 }
     81             }
     82             catch (SocketException ex)
     83             {
     84                 Trace.TraceError($"QuoteServer {ex.Message}");
     85                 throw new QuoteException("socket error",ex);
     86             }
     87         }
     88 
     89         #region 控制
     90 
     91         public void Start()
     92         {
     93             ReadQuotes();
     94             listenerTask = Task.Factory.StartNew(Listener,
     95                 TaskCreationOptions.LongRunning);
     96         }
     97 
     98         public void Stop()
     99         {
    100             listener.Stop();
    101         }
    102 
    103         public void Suspend()
    104         {
    105             listener.Stop();
    106         }
    107 
    108         public void Resume()
    109         {
    110             Start();
    111         }
    112 
    113         #endregion
    114 
    115         public void RefreshQuotes()
    116         {
    117             ReadQuotes();
    118         }
    119 
    120     }
    121 
    122     [Serializable]
    123     public class QuoteException : Exception
    124     {
    125         public QuoteException() { }
    126         public QuoteException(string message) : base(message) { }
    127         public QuoteException(string message, Exception inner) : base(message, inner) { }
    128         protected QuoteException(
    129         System.Runtime.Serialization.SerializationInfo info,
    130         System.Runtime.Serialization.StreamingContext context)
    131           : base(info, context) { }
    132     }
    133 }

     创建测试程序并调用 QuoteServer 类的 Start() 方法,测试程序是一个 C#控制台应用程序TestQuoteServer,并引用QuoteServer类的程序集。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace QuoteService
    {
        class Program
        {
            static void Main(string[] args)
            {
                var qs = new QuoteServer("quotes.txt", 7890);
                qs.Start();
                Console.WriteLine("Hit return to exit");
                Console.ReadLine();
                qs.Stop();
            }
        }
    }

    胖客户端代码:

    QuoteInformation.cs

    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    
    namespace QuoteClient
    {
        public class QuoteInformation:INotifyPropertyChanged
        {
            public QuoteInformation()
            {
                EnableRequest = true;
            }
    
            private string quote;
    
            public string Quote
            {
                get { return quote; }
                internal set { SetProperty(ref quote, value); }
            }
    
           
            private bool enableRequest;
    
            public bool EnableRequest
            {
                get { return enableRequest; }
                internal set { SetProperty(ref enableRequest, value); }
            }
    
            private void SetProperty<T>(ref T field, T value,[CallerMemberName] string propertyName="")
            {
                if (!EqualityComparer<T>.Default.Equals(field, value))
                {
                    field = value;
                    var handler = PropertyChanged;
                    if (handler != null)
                    {
                        handler(this,new PropertyChangedEventArgs(propertyName));
                    }
                }
            }
    
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    MainWindow.xaml

            <TextBlock x:Name="textBlock" HorizontalAlignment="Left" Margin="111,103,0,0" TextWrapping="Wrap" Text="{Binding Quote}" VerticalAlignment="Top" Height="144" Width="277"/>
            <Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="216,47,0,0" VerticalAlignment="Top" Width="75" IsEnabled="{Binding EnableRequest}" Click="OnGetQuote"/>

    MainWindow.xaml.cs

    using System.Net.Sockets;
    using System.Text;
    using System.Windows;
    using System.Windows.Input;
    
    namespace QuoteClient
    {
        /// <summary>
        /// MainWindow.xaml 的交互逻辑
        /// </summary>
        public partial class MainWindow : Window
        {
            private QuoteInformation quoteInfo = new QuoteInformation();
    
            public MainWindow()
            {
                InitializeComponent();
                this.DataContext = quoteInfo;
            }
    
            protected async void OnGetQuote(object sender, RoutedEventArgs e)
            {
                const int bufferSize = 1024;
                Cursor currentCursor = this.Cursor;
                this.Cursor = Cursors.Wait;
                quoteInfo.EnableRequest = false;
    
                string serverName = Properties.Settings.Default.ServerName;
                int port = Properties.Settings.Default.PortNumber;
    
                var client = new TcpClient();
                NetworkStream stream = null;
                try
                {
                    await client.ConnectAsync(serverName, port);
                    stream = client.GetStream();
                    byte[] buffer = new byte[bufferSize];
                    int received = await stream.ReadAsync(buffer, 0, bufferSize);
                    if (received <= 0)
                    {
                        return;
                    }
                    quoteInfo.Quote = Encoding.Unicode.GetString(buffer).Trim('');
                }
                catch (SocketException ex)
                {
                    MessageBox.Show(ex.Message, "Error Quote of the day",
                        MessageBoxButton.OK, MessageBoxImage.Error);
                    throw;
                }
                finally
                {
                    if (stream != null)
                    {
                        stream.Close();
                    }
    
                    if (client.Connected)
                    {
                        client.Close();
                    }
                }
    
                this.Cursor = currentCursor;
                quoteInfo.EnableRequest = true;
            }
        }
    }

    服务程序代码:

    QuoteService.cs

    using System;
    using System.IO;
    using System.ServiceProcess;
    
    namespace QuoteService
    {
        public partial class QuoteService : ServiceBase
        {
            private QuoteServer quoteServer;
            public QuoteService()
            {
                InitializeComponent();
            }
    
            protected override void OnStart(string[] args)
            {
                quoteServer = new QuoteServer(Path.Combine(
                    AppDomain.CurrentDomain.BaseDirectory,"quotes.txt"));
                quoteServer.Start();
            }
    
            protected override void OnStop()
            {
                quoteServer.Stop();
            }
    
            protected override void OnPause()
            {
                quoteServer.Suspend();
            }
    
            protected override void OnContinue()
            {
                quoteServer.Resume();
            }
    
            public const int commandRefresh = 128;
    
            protected override void OnCustomCommand(int command)
            {
                switch (command)
                {
                    case commandRefresh:
                        quoteServer.RefreshQuotes();
                        break;
                    default:
                        break;
                }
            }
        }
    }

    Program.cs

    using System.ServiceProcess;
    
    namespace QuoteService
    {
        static class Program
        {
            /// <summary>
            /// 应用程序的主入口点。
            /// </summary>
            static void Main()
            {
                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[]
                {
                    new QuoteService()
                };
                ServiceBase.Run(ServicesToRun);
            }
        }
    }

    6,服务的安装

      服务类切到设计视图,右键菜单“添加安装程序”,就可以给服务添加安装程序。

  • 相关阅读:
    AWS的EC2实例搭建服务器使用stackoverflow教程
    亚马逊云开服之旅
    Linux主机之间ssh免密登录配置方法
    阿里云运维培训讲解
    区块链技术在物流领域应用分析
    公众号页面分享朋友圈后点击跳转到小程序的方法介绍
    数据库主库从库宕机重启后binlog数据同步
    Docker+ElasticSearch+Logstash+Kibana+Filebeat搭建方法
    linux实现磁盘自动挂载脚本
    阿里云ecs基于镜像进行旧服务器迁移到新服务器
  • 原文地址:https://www.cnblogs.com/khjian/p/5903693.html
Copyright © 2020-2023  润新知