使用命名管道NamePipe实现服务看门狗功能并附C#源码

使用命名管道NamePipe实现服务看门狗功能

程序或服务中经常会碰到很多异常情况,需要实现杀掉自身进程然后重新启动的情况, 即类似于硬件上看门狗的功能。
关于命名管道(NamePipe)可以参考如何:使用命名管道进行网络进程间通信
这里是通过在服务子进程中通过NamePipeServer每隔固定时间(此处设置为15秒)发送一次心跳包myheartbeat, 然后在主进程中实现NamePipeClient中接收这个心跳包,更新最后一次心跳包的时间,如果超过固定的最大间隔时间(此处为2*60秒)则通过Application.Restart()重启服务。

管道名称PipeName必须设置相同, 此处为“FaceOfflineRestNamePipe”

1.NamePipeServer

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Pipes;
using NLog;

namespace HTTPServerLib
{
    /// <summary>
    /// NamePipeServer
    /// </summary>
    public class NamePipeServer
    {
        /// <summary>
        /// Logger for xxxx
        /// </summary>
        public static Logger logger = LogManager.GetLogger("FaceOfflineRest");

        private NamedPipeServerStream _pipe;
        private const string PipeName = "FaceOfflineRestNamePipe";
        private const int PipeInBufferSize = 4096;
        private const int PipeOutBufferSize = 65535;
        private Encoding encoding = Encoding.UTF8;

        /// <summary>
        /// StartNamePipe
        /// </summary>
        public void StartNamePipe()
        {
            _pipe = new NamedPipeServerStream
            (
                PipeName,
                PipeDirection.InOut,
                1,
                PipeTransmissionMode.Message,
                PipeOptions.Asynchronous | PipeOptions.WriteThrough,
                PipeInBufferSize,
                PipeOutBufferSize
             );

            //logger.Info("NamePipeServer.Waiting for client connection...");
            _pipe.WaitForConnection();

            //logger.Info("NamePipeServer.Client connected.");
        }

        /// <summary>
        /// SendMessage
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public void SendMessage(string message)
        {
            if (!_pipe.IsConnected)
            {
                logger.Error("NamePipeServer.SendMessage: pipe not connected,try to restart...");
                _pipe.Close();
                StartNamePipe();
            }
            //
            try
            {
                using (StreamWriter sw = new StreamWriter(_pipe))
                {
                    sw.AutoFlush = true;
                    sw.Write(message);
                }
            }
            catch (Exception ex)
            {
                logger.Error("NamePipeServer.SendMessage: ex: " + ex.Message);
            }                
        }

    }
}

2.进程引用

private void ProcessRequestInQue()
 {
  DateTime start = DateTime.Now;
  TimeSpan ts = DateTime.Now - start;

  while (IsRunning)
  {                
      if(reqClientQue.Count >0)
      {
          logger.Info("\nCurrent task number:{0}", reqClientQue.Count.ToString());
          TcpClient client = (TcpClient)reqClientQue.Dequeue();
          ProcessRequest(client);
      }
      ts = DateTime.Now - start;
      if(ts.TotalSeconds >= 15)
      {
          start = DateTime.Now;
          LocalMachineAction("myheartbeat");
      }
      Thread.Sleep(5);
  }
}

private static void LocalMachineAction(string command)
{
  NamePipeServer nps = new NamePipeServer();
  nps.StartNamePipe();

  nps.SendMessage(command);
}

3.NameClient,此处代码没有实际应用供参考,具体实现代码参见下方命名管道进程

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NLog;
using System.IO;
using System.IO.Pipes;


namespace FaceOfflineRest
{
    public class NamePipeClient
    {
        /// <summary>
        /// Logger for xxxx
        /// </summary>
        public static Logger logger = LogManager.GetLogger("FaceOfflineRest");

        private const string PipeName = "FaceOfflineRestNamePipe";
        private Encoding encoding = Encoding.UTF8;
        private NamedPipeClientStream _pipe;

        public bool _starting = false;

        public void connect()
        {
            if (_starting)
            {
                return;
            }
            try
            {
                _pipe = new NamedPipeClientStream
                (
                    ".",
                    PipeName,
                    PipeDirection.InOut,
                    PipeOptions.Asynchronous | PipeOptions.WriteThrough
                );

                _pipe.Connect();

                _pipe.ReadMode = PipeTransmissionMode.Message;

                string message = "NamePipe Connected!";

                byte[] data = encoding.GetBytes(message);

                _pipe.BeginWrite(data, 0, data.Length, PipeWriteCallback, _pipe);

                _starting = true;
            }
            catch (Exception ex)
            {
                logger.Error("NamePipeClient.connect, Exception:" + ex.Message + ex.StackTrace);
                _starting = false;
            }
        }

        private void PipeWriteCallback(IAsyncResult ar)
        {
            try
            {            
                var pipe = (NamedPipeClientStream)ar.AsyncState;

                pipe.EndWrite(ar);
                pipe.Flush();
                pipe.WaitForPipeDrain();

                var data = new byte[65535];

                var count = pipe.Read(data, 0, data.Length);

                if (count > 0)
                {
                    string message = encoding.GetString(data, 0, count);

                    logger.Info("NamePipe Received: " + message);
                }
            }
            catch (Exception ex)
            {
                logger.Error("NamePipeClient.PipeWriteCallback, Exception:" + ex.Message + ex.StackTrace);
                _starting = false;
            }
        }
    }
}

4.命名管道及重启服务线程

//NamePipe
private static bool _namepiperunflag = true;
private static int _namepipe_in_milisecond = 100;   //100ms
private const string PipeName = "FaceOfflineRestNamePipe";
private Encoding encoding = Encoding.UTF8;
private static NamedPipeClientStream _pipe;

#region 新开线程用于创建NamePipeServer,接收NamePipeClient发来的消息进行处理
/// <summary>
/// 命名管道t_namepipe
/// </summary>
private static Thread t_namepipe = null;
private static Thread t_restart = null;

private static DateTime last_heartbeat_time = DateTime.Now;
private static object syncstate = new object();

/// <summary>
/// 命名管道线程
/// </summary>
private static void ThreadNamePipeWork()
{
    t_namepipe = new Thread(new ThreadStart(ThreadNamePipe));
    t_namepipe.Start();
    logger.Info("FaceOfflineRest.ThreadNamePipeWork.ThreadNamePipe启动!");

    t_restart = new Thread(new ThreadStart(ThreadRestart));
    t_restart.Start();
    logger.Info("FaceOfflineRest.ThreadNamePipeWork.ThreadRestart启动!");
}
private static void ThreadRestart()
{
    try
    {
        TimeSpan ts = DateTime.Now - DateTime.Now.AddSeconds(-360);

        while (_namepiperunflag)
        {
            //logger.Info("ThreadNamePipe!");
            lock (syncstate)
            {
                ts = DateTime.Now - last_heartbeat_time;
            }
            if (ts.TotalSeconds > 2 * 60)
            {
                Face.release();
                logger.Info("Ready to Restart Myself!");
                Thread.Sleep(200);
                System.Windows.Forms.Application.Restart();
                System.Environment.Exit(0);
            }
            Thread.Sleep(_namepipe_in_milisecond);
        }
    }
    catch (Exception ex)
    {
        logger.Error("FaceOfflineRest.ThreadRestart:异常:" + ex.Message); ;
    }
}
/// <summary>
/// 命名管道线程
/// </summary>
private static void ThreadNamePipe()
{
    try
    {
        _pipe = new NamedPipeClientStream
            (
                ".",
                PipeName,
                PipeDirection.InOut,
                PipeOptions.Asynchronous | PipeOptions.WriteThrough
            );
        //logger.Info("NamePipeClient:Ready to Connect to pipe.");
        _pipe.Connect();
        //logger.Info("NamePipeClient:Connected to pipe.");
        
        while (_namepiperunflag)
        {
            if (!_pipe.IsConnected)
            {
                _pipe = new NamedPipeClientStream
                (
                    ".",
                    PipeName,
                    PipeDirection.InOut,
                    PipeOptions.Asynchronous | PipeOptions.WriteThrough
                );
                //logger.Info("NamePipeClient:Again Ready to Connect to pipe.");
                _pipe.Connect();
                //logger.Info("NamePipeClient:Again Connected to pipe.");
            }
            
            using (StreamReader sr = new StreamReader(_pipe))
            {
                // Display the read text to the console
                string temp;
                while ((temp = sr.ReadLine()) != null)
                {
                    //logger.Info("NamePipeClient:Received from server: {0}", temp);
                    if (temp == "myheartbeat")
                    {
                        lock (syncstate)
                        {
                            last_heartbeat_time = DateTime.Now;
                        }
                    }                            
                }
            }                    
        }
    }
    catch (Exception ex)
    {
        logger.Error("FaceOfflineRest.ThreadNamePipe:异常:" + ex.Message);
    }
}
#endregion

5.以上

posted @ 2023-10-19 15:10  devdog  阅读(71)  评论(0编辑  收藏  举报