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

发布时间 2023-10-19 15:16:59作者: devdog

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

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

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

NamePipeServer

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

namespace HTTPServerLib
{
///


/// NamePipeServer
///

public class NamePipeServer
{
///
/// Logger for xxxx
///

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);
        }                
    }

}

}`

进程引用

`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);
}`

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
{
///


/// Logger for xxxx
///

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;
        }
    }
}

}`

命名管道及重启服务线程

`//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发来的消息进行处理

///


/// 命名管道t_namepipe
///

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();

///


/// 命名管道线程
///

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); ;
}

}
///


/// 命名管道线程
///

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`

以上