Windows服务实现Tcp服务器

前言

    没有前言。

Windows服务介绍

    Windows服务可以在系统启动时自动打开,如果需要在没有用户操作的情况下运行的程序,就可以创建Windows服务。
比如:

  • 一些需要长期运行的TCP/IP服务器。
  • IIS的Windows服务被称为World Wide Publishing Service
  • 用于日志记录的服务
  • 守护线程的宿主进程

一个Windows服务包括三个部分:

  • 服务程序
  • 服务控制程序
  • 服务配置程序

    服务程序用于提供实际功能;服务控制程序可以把控制请求发送给服务,比如:开始,暂停,结束等,在编程时,我们只需要重写ServiceBase类与之对应的方法即可;服务配置程序可以安装服务,服务的信息需要写进注册表,供服务控制管理器(SCM)用于启动、停止服务。

服务程序需要三个部分:

  • 主函数
  • service-main函数
  • 处理程序

服务控制管理器(SCM)是操作系统的组成部分,它的作用是与各种各样的服务进行通信。这种通信的流程为:

SCM ----启动服务进程----> 服务
SCM <----注册service-main函数---- 服务
SCM ----service-main----> 服务
SCM <----注册处理程序---- 服务

    在系统启动时,将启动每个服务进程,进而调用该进程的主函数,它是该服务的入口。一个服务进程可能包含了多个服务,每项都会被该服务注册一个service-main函数入口点,service-main函数包含服务的实际功能。

    service-main函数的一个重要任务是用SCM注册一个处理程序。处理程序用来响应来自服务控制程序的事件,如SCM:停止,暂停或重新开始。

创建Windows服务程序

    在VS中选择Windows Service模板,新建项目。双击自动生成的Service1,可以看到一个与winForm类似的设计器,我们不需要往上面拖动任何控件。我们可以在属性界面来调整该服务的属性值。

  • AutoLog 指定把启动和停止服务的事件写入日志。该日志会在服务安装后自动生成,在该服务的exe文件所在的目录中。
  • ServiceName 是写到注册列表中的服务名称,它在系统中应是唯一的,使用它可以控制服务。
  • CanHandlePoweEvent 是否响应低电源事件,对于运行在笔记本和移动端的服务有效。

    右键查看Service1的代码,可以发现它继承自ServiceBase类,并重写了OnStart和OnStop方法,ServiceBase类是.Net Framework框架下用来开发Windows服务的基类。服务类实际上是使用一个称为NativeMethods的辅助类与SCM通信,当然,我们并不需要了解过多的细节。
Windows服务实现Tcp服务器_第1张图片

    我们来查看Program类里的主函数:

    static void Main()
    {
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[]
        {
            new Service1()
        };
        ServiceBase.Run(ServicesToRun);
    }

    可以看出,一个服务进程可以运行多个服务,ServicesToRun数组中的对象需要继承自ServiceBase。
ServiceBase类注册处理程序,并在调用了OnStart()方法后把服务启动成功的消息通知给SCM。在调用Run方法后,主线程会处于阻塞状态,直到服务结束。所以初始化操作应在Run方法之前完成,且不应超过30s。如果有长时间的初始化任务,应该将服务线程化。因为调用Run方法超过30s,SCM会任务该服务启动失败。每个服务启动时,会调用其OnStart方法。我们可以在OnStart方法里写具体的功能实现。

一个简单的TCP服务器

    这个列子的作用是在系统上运行监听程序,向客户端的任何请求都回复指定文件里的随机一行内容。Windows服务并不能直接调试,我们可以通过记录日志,附加到进程等方式进行间接调试。我们最好先建一个方便调试的功能程序,调试无误后再移植到Windows服务程序中。

QuoteService类
TCP模块:
请注意看catch代码块里注释掉的内容,如果服务运行时,程序抛出了异常的话,服务是可能停掉的。

    protected void Listener()
    {
        try
        {
            IPAddress ipAddress = IPAddress.Any;
            tcpListener = new TcpListener(ipAddress, port);
            tcpListener.Start();
            while (true)
            {
                var client = tcpListener.AcceptTcpClient();
                string message = GetRandomQuoteOfTheDay();
                var coder = new UnicodeEncoding();
                byte[] sendBuffer = coder.GetBytes(message);
                client.Client.Send(sendBuffer);
                client.Close();
            }
        }
        catch (SocketException ex)
        {
            //Trace.TraceError($"QuoteService{ex.Message}");
            //throw new QuoteExecption("socket Exception");
            Log("SocketException: " + ex.Message);
        }
    }  

暴露的Start和Stop方法

    public void Start()
    {
        listenerTask = Task.Factory.StartNew(Listener,TaskCreationOptions.LongRunning);
    }

    public void Stop()
    {
        tcpListener.Stop();
    }

QuoteWindowsService : ServiceBase 服务类

 public partial class QuoteWindowsService : ServiceBase
{
    private QuoteService quoteService;
    public QuoteWindowsService()
    {
        InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {
        quoteService=new QuoteService();
        quoteService.Start();
    }

    protected override void OnStop()
    {
        quoteService.Stop();
    }
}  

你也可以重写OnPause,OnShutdown,OnContinue等方法,OnPowerEvent()在系统电源状态发生变化时触发。注意OnStart()方法不能被阻塞,它必须及时的返回给调用者(ServiceBase类的ServiceMainCallBack方法)。

为服务添加安装程序

    安装组件在安装它的系统上注册单个服务,并让服务控制管理器知道存在该服务。

  1. 在“解决方案资源管理器”中,访问要为其添加安装组件的服务的“设计”视图。
  2. 单击设计器的背景以选择服务本身,而不是它的任何内容。
  3. 设计器具有焦点时,右键单击,然后单击“添加安装程序”。
    你会发现,项目中多了一个新类ProjectInstaller以及它上面的两个安装组件 ServiceProcessInstaller(可以为服务配置安全性方面的属性) 和 ServiceInstaller(为服务配置启动方式等属性)。配置很简单,去看文档吧。

https://docs.microsoft.com/zh-cn/dotnet/framework/windows-services/how-to-add-installers-to-your-service-application

需要注意的是ServiceInstaller的ServiceName属性需要与ServiceBase的ServiceName属性保持一致。
而且,每个ServiceBase的派生类(即每个服务)都应有一个与之对应的ServiceInstaller实例,而ServiceProcessInstaller是用于配置服务进程,一个服务进程可以包含多个服务。

    ProjectInstaller类派生自InStall.InStaller类,后者是所有自定义安装程序的基类,它可以构建基于事务的安装程序。InStaller类有Install(),Commit(),Rollback(),Uninstall()方法,这些都从安装程序中调用。可以看到它有一个特性RunInstaller,它的值为True代表安装程序集时会检查该特性。
我们可以使用installutil.exe来安装服务。

  1. 首先在开始菜单里找到 Visio Studio 2019文件夹
  2. 以管理员模式运行其下的开发者控制台
  3. 使用cd命令进入服务程序生成的exe文件所在的目录
  4. 使用命令installutil 文件名.exe   安装服务,会自动在该目录生成几个日志文件
  5. 使用installutil /u 文件名.exe   可以卸载服务。

如果服务生成失败,可能是存在同名服务,或是没有以管理员权限运行安装命令等。

    服务安装以后,你可以在 任务管理器-服务 中查看,也可以在 服务 中查看详细信息。如果想更新服务程序,只需要先停掉服务,重新生成解决方案即可。
    另外,我们可以在cmd窗口,使用sc命令+服务名直接管理服务,比如:sc delete 服务名,删除某个服务。这是一个很强大的命令,它可以检查服务的状态,配置等,当服务卸载程序无法正常工作,我们可以使用它来卸载程序。比如你设置了ServiceBase的CanStop属性为false, 那么installutil /u 方法就不好用了,因为你无法将服务停下来再删除。

你可能感兴趣的:(Windows服务)