C# 进程通信-命名管道

之前看wcf服务的时候看到wcf有支持管道通信协议,之前不知道,最近刚好有用到这个,这里写个简单实例

.net有已经封装好的pip通信的对象NamedPipeServerStream 和NamedPipeClientStream对象,底层应该还是调用C++实现的api实现的

对服务端和客户端做个简单的封装方便调用:

server:

public class PipServer:Log
    {
        public Action<string> ReceiveEvent;
        NamedPipeServerStream m_pipServer;
        AutoResetEvent monitor = new AutoResetEvent(false);
        Thread m_thread;
        bool run = true;
        string servname;

        public PipServer(string name)
        {
            m_pipServer = new NamedPipeServerStream(name,PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
            servname = name;
        }
        public void Listen()
        {
            try
            {
                m_thread = new Thread(() =>
                {
                     WaitConnect();
                });
                m_thread.Start();
            }
            catch (Exception ex)
            {
                P(ex, "[PipServer.WaitForConnect]");
            }
        }
        void WaitConnect()
        {

            AsyncCallback callback = null;
            callback = new AsyncCallback(ar =>
            {
                var pipeServer = (NamedPipeServerStream)ar.AsyncState;
                pipeServer.EndWaitForConnection(ar);
                Accept();
                pipeServer.Disconnect();
                m_pipServer.BeginWaitForConnection(callback, m_pipServer);
            });
            m_pipServer.BeginWaitForConnection(callback, m_pipServer);
        }

        void Accept()
        {
            try
            {

                var res = Read();
                if(!string.IsNullOrEmpty(res))
                    ReceiveEvent?.Invoke(res);
            }
            catch(Exception ex)
            {
                P(ex, "[PipServer.Accept]");
            }
        }
        public bool Send(string msg)
        {
            try
            {
                var buf = Encoding.UTF8.GetBytes(msg);
                if (m_pipServer.CanWrite)
                {
                    m_pipServer.Write(buf, 0, buf.Length);
                    m_pipServer.Flush();
                    return true;
                }
                return false;
            }
            catch (Exception ex)
            {
                P(ex, "[PipServer.Send]");
                return false;
            }

        }

        public string Read()
        {
            try
            {
                if (m_pipServer.CanRead)
                {
                    int count = 0;
                    List<byte> data = new List<byte>();
                    byte[] buf = new byte[1024];
                    do
                    {
                        count=m_pipServer.Read(buf, 0, buf.Length);
                        if (count == buf.Length)
                        {
                            data.AddRange(buf);
                        }
                        else
                        {
                            var dst = new byte[count];
                            Buffer.BlockCopy(buf, 0, dst, 0, count);
                            data.AddRange(dst);
                        }
                    } while (count > 0&&m_pipServer.CanRead);
                    var res = Encoding.UTF8.GetString(data.ToArray());
                    return res;
                }
                return null;

            }
            catch (Exception ex)
            {
                P(ex, "[PipServer.Read]");
                return null;
            }
        }

        public void Close()
        {
            run = false;
            m_thread.Join();
            if (m_pipServer.IsConnected)
            {
                m_pipServer.Close();
            }

        }
    }

client:

  public class PipClient:Log
    {

        string serv;
        public PipClient(string server)
        {
            serv = server;
        }
        public bool Send(string msg)
        {
            try
            {
                var buf = Encoding.UTF8.GetBytes(msg);
                NamedPipeClientStream pipclient = new NamedPipeClientStream(serv);
                pipclient.Connect(3000);
                if (pipclient.CanWrite)
                {
                    pipclient.Write(buf, 0, buf.Length);
                    pipclient.Flush();
                    pipclient.Close();
                    return true;
                }
                return false;
            }
            catch (Exception ex)
            {
                P(ex, "[PipClient.Send]");
                return false;
            }
        }
    }

log类写了一个简单日志打印类,集成下方便打印日志,可以直接去掉继承,吧日志打印去掉:

    public class Log
    {
        public void L(string msg)
        {
            Console.WriteLine(msg);
        }
        public void L(string format, params string[] data)
        {
            Console.WriteLine(string.Format(format,data));
        }
        public void P(Exception ex, string format, params string[] data)
        {
            var msg = string.Format(format, data);
            Console.WriteLine(string.Format("{0}:{1},{1}", msg, ex.Message, ex.StackTrace));
        }
    }

调用实例:

 static void  PipTest()
        {
            Thread thread = new Thread(() =>
            {
                PipServer pip = new PipServer("TEST_PIP");
                pip.ReceiveEvent += s =>
                {
                    w(string.Format("receive:{0}",s));
                };
                pip.Listen();
            });
            thread.Start();

            bool send = true;
            int count = 0;
            AutoResetEvent monitor = new AutoResetEvent(false);
            Thread client = new Thread(() =>
            {
                PipClient ct = new PipClient("TEST_PIP");
                while (send)
                {
                    string msg = string.Format("这是第{0}条数据", count);
                    w(msg);
                    ct.Send(msg);
                    count++;
                    if (monitor.WaitOne(1000))
                    {
                        break;
                    }
                }
            });
            client.Start();
            while (true)
            {
                var input = Console.ReadLine();
                if (input == "q" || input == "Q")
                {
                    send = false;
                    monitor.Set();
                    break;
                }
            }
        }

运行时,是客户端向服务端每隔一秒发送一次数据

有几个要注意的点:

1 要注意编码方式,怎么编码就怎么解码,最好是要有固定编码,不要直接写string,因为如果是不同的语言和不同平台实现的类,可能default对应的编码方式是不一样的,这样会造成读取乱码

2 这里可以用streamreader来读取,但是不要用readend这种写法,如果发送方不及时调用close方法,这样写会一直卡住,调用flush也没用

3 这里初始化只传入了servername,实际底层的地址是\\\\.\\pipe\\TEST_PIP,调试的时候下个断点可以看到的,如果用C++写的话,直接调用API传入的地址就是全名,到C#这边会自动被解析

4 可以再传入的信息上做一些文章,加上ID,发送方和接收方,这样可以实现类似回调的功能,这个是支持双向通信的,这里只有单向

5 类库是支持同步和异步的,这里是异步的等待连接,同步的读取,但是貌似没有直接支持4.5await写法的方法,只有AsyncCallback的写法

时间: 2024-10-29 01:08:17

C# 进程通信-命名管道的相关文章

Linux学习笔记(13)-进程通信|命名管道

匿名管道只能在具有亲属关系的进程间通信,那么如果想要在不具有亲戚关系,想在陌生人之间通信,那又该怎么办呢? 别慌,Linux身为世界上*强大的操作系统,当然提供了这种机制,那便是命名管道-- 所谓命名管道,那便是拥有名字的管道,同时也被称之为FIFO,谈到FIFO,那么做过单片机开发的同学想必是不陌生的. 在很多单片机的项目中,都使用过FIFO,FIFO其实是一种队列,先进先出,这样可以保证读出数据和写入数据的一致性. 使用FIFO文件,便可以在不同的,且不具有亲属关系的进程中进程通信. 创建命

进程通信——命名管道通信

private static void WaitData() { using (NamedPipeServerStream pipeServer = new NamedPipeServerStream("testpipe", PipeDirection.InOut, 1)) { try { pipeServer.WaitForConnection(); pipeServer.ReadMode = PipeTransmissionMode.Byte; using (StreamReade

进程通信之管道

本节学习进程通信的另一种方式:管道.管道是一个进程连接数据流到另一个进程的通道,它通常把一个进程的输出通过管道连接到另一个进程的输入.在shell命令中经常会看到管道的应用,比如我们要列出当前文件下所有命名中有"test"的文件:ls -l | grep test.其中"|"就代表我们在使用管道,它会把"ls -l"的查询结果通过管道,发送给grep,然后执行"grep test"命令后把结构输出到终端.总之一句话:管道会把一

Linux下进程通信之管道

每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication).如下图所示. 目前进程通信的方式有: 管道 FIFO 消息队列 信号量 共享内存 套接字 管道 管道概念 管道是Linux支持的最初Unix IPC形式之一,具有以下特点: 管道是半双工的,数

c# c++通信--命名管道通信

进程间通信有很多种,windows上面比较简单的有管道通信(匿名管道及命名管道) 最近做个本机c#界面与c++服务进行通信的一个需求.简单用命名管道通信.msdn都直接有demo,详见下方参考. c++:LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe"); c#:new NamedPipeClientStream("localhost", "mynamedpipe", PipeDirect

进程通信——匿名管道

有的时候在程序的开发过程中,两个进程之间会有数据的交互.信号的机制能够实现进程通信.它通过 “中断--响应” 的异步模式来工作.但作为通信来讲,信号还是远远不够的.因为它不能够携带任何其他的信息.只利用信号机制来实现进程通信显得捉襟见肘,并且信号的优势并不此.所以必须开发新的进程间通信的方法.本文所学习的 "匿名管道" 便是其中的一种最简单的方法. 基本概念 在讲管道的基本概念之前,首先提一个问题.如果让你来设计进程通信的方式,你会怎么做?最简单的方式莫过于两个进程打开同一个文件,这样

进程通信-无名管道

无名管道:主要是针对进程通信的(自己感觉它有很大的局限性) 特点:它不是一个文件系统,不能按名访问,这也是它和有名管道之间最大的区别.无名管道只是一个系统内存里面的东西. 半双工模式,数据只能流向一个方向(老师给我们举得例子就是水厂的水到居民用水,不可能倒着流对吧). 进程之间通信,但是只能是有亲缘关系的进程才能进行通信?比如父子进程:因为在父子进程中,子进程拷贝父进程的数据段,这让这两个进程在通信的时候就有了相连的关系. 下面我们来看一下:无名管道的创建,写入,读取,关闭(我用的的是Liunx

linux进程通信之管道

1.介绍: 1)同一主机: unix进程通信方式:无名管道,有名管道,信号 system v方式:信号量,消息队列,共享内存 2)网络通信:Socket,RPC 2.管道: 无名管道(PIPE):使用一个进程的标准输出作为另一个进程的标准输入建立的一个单向管道,执行完成后消失.主要用于父进程与子进程之间,或者两个兄弟进程之间.采用的是单向 1)创建无名管道:(#include(unistd.h)) extern int pipe(int pipes[2]),pipes[0]完成读操作,pipes

Linux进程通信----匿名管道

Linux进程通信中最为简单的方式是匿名管道 匿名管道的创建需要用到pipe函数,pipe函数参数为一个数组 表示的文件描述字.这个数组有两个文件描述字,第一个是用 于读数据的文件描述符第二个是用于写数据的文件描述符. 不能将用于写的文件描述符进行读操作或者进行读的文件描述 符进写操作,这样都会导致错误. 关于匿名管道的几点说明: 1.匿名管道是半双工的,即一个进程只能读,一个进程只能写    要实现全双工,需要两个匿名管道. 2.只能在父子进程或者兄弟进程进行通信. 3.在读的时候关闭写文件描