zoukankan      html  css  js  c++  java
  • C++和C#进程之间通过命名管道通信(附源码)—下

    接上篇:

    我采用的是C#开发的一个windows应用程序(pipe_server_csharp)作为服务器端,而MFC开发的应用程序(NamedPipeClient_vc)作为客户端。客户端和服务器端要进行频繁的大量的通信,常见的是文本信息和曲线数据,例如,一共有10条曲线,每条曲线有1000000double数据。

     

    服务器端:

    服务器端是用在VS2005中用C#开发的一个名为pipe_server_csharp的应用程序,只有一个名为frmServer的主界面。

    由于管道的相关API函数都是属于kernel32.dll函数,C#中不能直接调用,所以必须将所要用到的API函数全部封装在一个类NamedPipeNative中。 至于如何调用这些API函数,有兴趣的朋友可以上网搜索,或者看我的另一篇文章《C#中怎么调用命名管道的WinAPI》。

    NamedPipeNative类中几个主要函数如下(大家可以对比这些重写的API函数和原来的函数有什么变化):

    [DllImport("kernel32.dll", SetLastError=true)]

    public static extern IntPtr CreateNamedPipe

    (

    String lpName,                                          // pipe name

    uint dwOpenMode,                                   // pipe open mode

    uint dwPipeMode,                                   // pipe-specific modes

    uint nMaxInstances,                                // maximum number of instances

    uint nOutBufferSize,                           // output buffer size

    uint nInBufferSize,                                // input buffer size

    uint nDefaultTimeOut,                          // time-out interval

    IntPtr pipeSecurityDescriptor        // SD

    );

     

    [DllImport("kernel32.dll", SetLastError=true)]

    public static extern bool ConnectNamedPipe

    (

    IntPtr hHandle,                                         // handle to named pipe

    Overlapped lpOverlapped                   // overlapped structure

    );

     

    [DllImport("kernel32.dll", SetLastError=true)]

    public static extern IntPtr CreateFile

    (

    String lpFileName,                          // file name

    uint dwDesiredAccess,                       // access mode

    uint dwShareMode,                                  // share mode

    SecurityAttributes attr,                  // SD

    uint dwCreationDisposition,          // how to create

    uint dwFlagsAndAttributes,           // file attributes

    uint hTemplateFile);                      // handle to template file

     

    [DllImport("kernel32.dll", SetLastError=true)]

    public static extern bool ReadFile

    (

    IntPtr hHandle,    // handle to file

    byte[] lpBuffer,// data buffer字节流

    uint nNumberOfBytesToRead,// number of bytes to read

    byte[] lpNumberOfBytesRead,// number of bytes read

    uint lpOverlapped// overlapped buffer

    );

     

    [DllImport("kernel32.dll", SetLastError=true)]

    public static extern bool WriteFile

    (

    IntPtr hHandle,    // handle to file

    byte[] lpBuffer,// data buffer字节流

    uint nNumberOfBytesToWrite, // number of bytes to write

    byte[] lpNumberOfBytesWritten,   // number of bytes written

    uint lpOverlapped // overlapped buffer

    );

     

    还有其他一些常量:

    public const uint PIPE_ACCESS_DUPLEX = 0x00000003;

    public const uint PIPE_ACCESS_OUTBOUND = 0x00000002;

    public const uint PIPE_TYPE_BYTE = 0x00000000;

    public const uint PIPE_TYPE_MESSAGE = 0x00000004;

    在此不一一列举了。

    封装之后,在C#中就可以直接调用这些函数和常量了。

     

    另外,创建了一个类CMyPipe,封装了服务器管道的属性和方法,如创建管道,读写数据等:

     

    namespace pipe_server_csharp

    {

        public class CMyPipe

        {

            private string m_PipeName;//管道名全称

            public string PipeName

            {

                get { return m_PipeName; }

                set { m_PipeName = value; }

            }

     

            private IntPtr m_HPipe;//管道句柄

            public IntPtr HPipe

            {

                get { return m_HPipe; }

                set { m_HPipe = value; }

            }

     

            public CMyPipe()//无参构造函数

            {

                m_HPipe = (IntPtr)NamedPipeNative.INVALID_HANDLE_VALUE;

                m_PipeName = "\\\\.\\pipe\\robinTest";

            }

     

            public CMyPipe(string pipeName)//有参构造函数

            {

                m_HPipe = (IntPtr)NamedPipeNative.INVALID_HANDLE_VALUE;

                m_PipeName = pipeName;

            }

     

            ~CMyPipe()//析构函数

            {

                Dispose();

            }

     

            public void Dispose()

            {

                lock (this)

                {

                    if (m_HPipe != (IntPtr)NamedPipeNative.INVALID_HANDLE_VALUE)

                    {

                        NamedPipeNative.CloseHandle(m_HPipe);

                        m_HPipe = (IntPtr)NamedPipeNative.INVALID_HANDLE_VALUE;

                    }

                }

            }

     

            public void CreatePipe()//创建管道

            {

                m_HPipe = NamedPipeNative.CreateNamedPipe(m_PipeName,

                                NamedPipeNative.PIPE_ACCESS_DUPLEX,         // 数据双工通信

                                NamedPipeNative. PIPE_TYPE_MESSAGE | NamedPipeNative.PIPE_WAIT,                            100,                         // 最大实例个数

                                128,                           // 流出数据缓冲大小

                                128,                           // 流入数据缓冲大小

                                150,                         // 超时,毫秒

                                IntPtr.Zero                  // 安全信息

                                );

                if (m_HPipe.ToInt32() == NamedPipeNative.INVALID_HANDLE_VALUE)

                {

                    frmServer.ActivityRef.AppendText("创建命名管道失败" );

                    frmServer.ActivityRef.AppendText(Environment.NewLine);

                    return;

                }

                frmServer.ActivityRef.AppendText("创建命名管道完毕" );

                frmServer.ActivityRef.AppendText(Environment.NewLine);          

            }

     

            public void ReadCurveData()//读取曲线数据

            {

                int nCurvesToRead = 0;

                nCurvesToRead = ReadInt(HPipe);//先读取需要读取的曲线条数,

                frmServer.CurveDataList.Clear();

                for (int j = 1; j <= nCurvesToRead; j++)

                {

                    string curveName = ReadString(HPipe);//读取该曲线名称

                    int nCount = 0;

                    nCount = ReadInt(HPipe);//读取该曲线的数据总数(数组大小)

                    double[] readData = new double[nCount];

                    for (int i = 0; i < nCount; i++)

                    {

                        readData[i] = ReadDouble(HPipe);//顺序读取曲线的数据

                    }

                    CCurve newCurve = new CCurve();

                    newCurve.CurveName = curveName;

                    newCurve.CurveData = readData;

                    frmServer.CurveDataList.Add(newCurve);

                }

            }

     

            public void ReadTextInfo()//读取文本信息

            {

                string textInfo = ReadString(HPipe);//读取该曲线名称

                frmServer.ActivityRef.AppendText(textInfo + Environment.NewLine);

                frmServer.ActivityRef.AppendText(Environment.NewLine);

            }

     

            public void ReadData()

            {

                //read data

                int flag = -1;

                flag = ReadInt(HPipe);//获取当前要读取的数据的信息

                if (flag == 0)//flag==0表示读取曲线数据

                {

                    ReadCurveData();

                }

                else if (flag == 1)//flag==1表示读取文本信息

                {

                    ReadTextInfo();

                }

            }

     

    //写字符串,由于字符串的大小不同,所以先将字符串的大小写入,然后写字符串内容,对应的,

    //c++端,读字符串时先读取字符数组大小,从而给字符数组开辟相应的空间,然后读取字符串内容。

            public bool WriteString(IntPtr HPipe, string str)

            {

                byte[] Val = System.Text.Encoding.UTF8.GetBytes(str);

                byte[] dwBytesWrite = new byte[4];

                int len = Val.Length;

                byte[] lenBytes = System.BitConverter.GetBytes(len);

                if (NamedPipeNative.WriteFile(HPipe, lenBytes, 4, dwBytesWrite, 0))

                    return (NamedPipeNative.WriteFile(HPipe, Val, (uint)len, dwBytesWrite, 0));

                else

                    return false;

            }

     

            public string ReadString(IntPtr HPipe)

            {

                string Val = "";

                byte[] bytes = ReadBytes(HPipe);

                if (bytes != null)

                {

                    Val = System.Text.Encoding.UTF8.GetString(bytes);

                }

                return Val;

            }

     

            public byte[] ReadBytes(IntPtr HPipe)

            {

                //传字节流

                byte[] szMsg = null;

                byte[] dwBytesRead = new byte[4];

                byte[] lenBytes = new byte[4];

                int len;

                if (NamedPipeNative.ReadFile(HPipe, lenBytes, 4, dwBytesRead, 0))//先读大小

                {

                    len = System.BitConverter.ToInt32(lenBytes, 0);

                    szMsg = new byte[len];

                    if (!NamedPipeNative.ReadFile(HPipe, szMsg, (uint)len, dwBytesRead, 0))

                    {

                        //frmServer.ActivityRef.AppendText("读取数据失败");

                        return null;

                    }

                }

                return szMsg;

            }

     

            public float ReadFloat(IntPtr HPipe)

            {

                float Val = 0;

                byte[] bytes = new byte[4]; //单精度需4个字节存储

                byte[] dwBytesRead = new byte[4];

                if (!NamedPipeNative.ReadFile(HPipe, bytes, 4, dwBytesRead, 0))

                {

                    //frmServer.ActivityRef.AppendText("读取数据失败");

                    return 0;

                }

                Val = System.BitConverter.ToSingle(bytes, 0);

                return Val;

            }

     

            public double ReadDouble(IntPtr HPipe)

            {

                double Val = 0;

                byte[] bytes = new byte[8]; //双精度需8个字节存储

                byte[] dwBytesRead = new byte[4];

     

                if (!NamedPipeNative.ReadFile(HPipe, bytes, 8, dwBytesRead, 0))

                {

                    //frmServer.ActivityRef.AppendText("读取数据失败");

                    return 0;

                }

                Val = System.BitConverter.ToDouble(bytes, 0);

                return Val;

            }

     

            public int ReadInt(IntPtr HPipe)

            {

                int Val = 0;

                byte[] bytes = new byte[4]; //整型需4个字节存储

                byte[] dwBytesRead = new byte[4];

     

                if (!NamedPipeNative.ReadFile(HPipe, bytes, 4, dwBytesRead, 0))

                {

                    //frmServer.ActivityRef.AppendText("读取数据失败");

                    return 0;

                }

                Val = System.BitConverter.ToInt32(bytes, 0);

                return Val;

            }

        }

    }

     

     

    主程序,读取和显示通信信息:

        public partial class frmServer : Form

        {

            public static System.Windows.Forms.TextBox ActivityRef;//显示信息      

            public static ArrayList CurveDataList = new ArrayList();//存储数据

            public static string PipeName = "robinTest";

            public static string ServeName = ".";

            public static string FullPipeName = "\\\\"+ServeName+"\\pipe\\"+PipeName;//管道全名

     

            public static CMyPipe ThePipe;//管道实例

     

            private Thread hThread;//线程

     

            public frmServer()

            {

                InitializeComponent();

                frmServer.ActivityRef = this.txtMessage;

                Control.CheckForIllegalCrossThreadCalls = false;//不捕获线程错误调用,这样线程就可以别的线程中创建的东西,这样做也会破坏了线程的安全

                ThePipe = new CMyPipe(FullPipeName);

                ThePipe.CreatePipe();//创建管道

     

                hThread = new Thread(new ThreadStart(PipeProcess));

                hThread.IsBackground = true;

                hThread.Name = "Main Pipe Thread";

                hThread.Start();// 启动线程.监听管道,进行通信

                Thread.Sleep(1000);

            }

     

            public void PipeProcess()

            {

                bool ret = false;

                try

                {

                    while (true)

                    {

                        ret = NamedPipeNative.ConnectNamedPipe(ThePipe.HPipe, null);//连接到管道

     

                        if (!ret)

                        {

                            if (NamedPipeNative.GetLastError() == NamedPipeNative.ERROR_PIPE_CONNECTED)  // 客户还在连接中

                            {

                                frmServer.ActivityRef.AppendText("连接正常,客户还在连接中……" + Environment.NewLine);//此处会出错,因为线程调用的问题

                                ret = true;

                            }

                            if (NamedPipeNative.GetLastError() == NamedPipeNative.ERROR_NO_DATA)         // 客户已经关闭

                            {

                                frmServer.ActivityRef.AppendText("客户已经关闭,等待客户…………" + Environment.NewLine);

                                NamedPipeNative.DisconnectNamedPipe(ThePipe.HPipe);

                                continue;

                            }

                        }

                        else

                        {

                            frmServer.ActivityRef.AppendText("有客户接入" + Environment.NewLine);                       

                        }

     

                        ThePipe.ReadData();//读取数据

                    }

                }

                catch (Exception ex)

                {

                    MessageBox.Show(ex.ToString());

                }

                finally

                {

                    frmServer.ActivityRef.AppendText("线程退出!");

                }

            }

        }

      客户机端:

    客户端是在VS6.0中用MFC开发的一个单界面应用程序,名为NamedPipeClient_vc。在vc6.0中可以直接调用API函数,不需要封装。

    当然,按照惯例,还是将客户端的命名管道相关的属性和方法封装成类CMyPipe,如下:

    CString PipeName = "robinTest";

    CString ServerName = ".";

    CString FullPipeName="\\\\"+ServerName+"\\pipe\\"+PipeName;

     

    CMyPipe::CMyPipe()

    {

           m_pThread=NULL;

    }

     

    CMyPipe::~CMyPipe()

    {

           if(m_pThread)

           {

                  if(TerminateThread(m_pThread->m_hThread,0))

                  {

                         if(m_pThread)

                                delete      m_pThread;

                         m_pThread = NULL;

                  }

           }

    }

     

    //客户端和服务器端建立连接

    void CMyPipe::ClientCreateFile()

    {

           BOOL ret = ::WaitNamedPipe(FullPipeName, 5000);

           if (!ret)

           {

                  //ClientMsg="管道忙或者没有启动……\n";

                  return;

           }

           m_hPipe  = ::CreateFile(FullPipeName,

                  GENERIC_READ|GENERIC_WRITE,

                  FILE_SHARE_READ|FILE_SHARE_WRITE,

                  NULL,

                  OPEN_EXISTING,

                  FILE_ATTRIBUTE_NORMAL,

                  NULL);

        if (m_hPipe == INVALID_HANDLE_VALUE)

           {

                 

            char szErr[256] = "";

            DWORD dwErr = GetLastError();

                  sprintf(szErr, "%l", dwErr);

            //ClientMsg=szErr;

            return;

     

        }

          

           //ClientMsg="打开管道了\n";

    }

     

    //写字符串

    void CMyPipe::WriteString(char *szMsg)

    {

           DWORD  dwSize = strlen(szMsg)+1, dwBytesWritten = 0;

           BOOL ret =WriteFile(m_hPipe, &dwSize, 4, &dwBytesWritten, NULL);

           if (ret)

           {

                  BOOL ret2=WriteFile(m_hPipe, szMsg, dwSize, &dwBytesWritten, NULL);

           }

    }

     

    //写单精度

    void CMyPipe::WriteFloat(float szMsg)

    {

           DWORD  dwSize = 4, dwBytesWritten = 0;

           BOOL ret=WriteFile(m_hPipe, &szMsg, dwSize, &dwBytesWritten, NULL);

    }

     

    //写双精度

    void CMyPipe::WriteDouble(double szMsg)

    {

           DWORD  dwSize = 8, dwBytesWritten = 0;

           BOOL ret=WriteFile(m_hPipe, &szMsg, dwSize, &dwBytesWritten, NULL);

    }

     

    //写整型

    void CMyPipe::WriteInt(int szMsg)

    {

           DWORD  dwSize = 4, dwBytesWritten = 0;

           BOOL ret=WriteFile(m_hPipe, &szMsg, dwSize, &dwBytesWritten, NULL);

    }

     

    void CMyPipe::ClosePipe()

    {

           CloseHandle(m_hPipe);

    }

     

    //读字符串

    char * CMyPipe::ReadString()

    {

           char *readStr;

           DWORD  drSize=0, dwBytesRead = 0;

           BOOL ret =ReadFile(m_hPipe, &drSize, 4, &dwBytesRead, NULL);

           if (ret)

           {

                  readStr=new char[drSize];

                  BOOL ret2=ReadFile(m_hPipe, readStr, drSize, &dwBytesRead, NULL);

           }

           return readStr;

    }

     

     

    int CMyPipe::ReadInt()

    {

           int intRead=0;

           DWORD  drSize=4, dwBytesRead = 0;

           BOOL ret =ReadFile(m_hPipe, &intRead, drSize, &dwBytesRead, NULL);

           return intRead;

    }

     

    float CMyPipe::ReadFloat()

    {

           float floatRead=0;

           DWORD  drSize=4, dwBytesRead = 0;

           BOOL ret =ReadFile(m_hPipe, &floatRead, drSize, &dwBytesRead, NULL);

           return floatRead;

    }

     

    double CMyPipe::ReadDouble()

    {

           double floatRead=0;

           DWORD  drSize=8, dwBytesRead = 0;

           BOOL ret =ReadFile(m_hPipe, &floatRead, drSize, &dwBytesRead, NULL);

           return floatRead;

    }

     

    而主程序NamedPipeClient_vcDlg.cpp如下:

    CMyPipe thePipe;

     

    //连接服务器管道

    void CNamedPipeClient_vcDlg::OnOpen()

    {

           // TODO: Add your control notification handler code here

           thePipe.ClientCreateFile();

    }

     

    //发送数据

    void CNamedPipeClient_vcDlg::OnWrite()

    {

           // TODO: Add your control notification handler code here

           WriteData();

    }

    //关闭管道

    void CNamedPipeClient_vcDlg::OnClose()

    {

           // TODO: Add your control notification handler code here

           thePipe.ClosePipe();

    }

    //写曲线数据(注意写入的顺序,读的时候也要按照这个顺序)

    void CNamedPipeClient_vcDlg::WriteCurveData()

    {

           int nCurvesToWrite=10;

           thePipe.WriteInt(nCurvesToWrite);//先写入要传递的曲线总条数

           for (int j = 1; j <= nCurvesToWrite; j++)

           {

                  char *curveName= "curve";//

                  thePipe.WriteString(curveName);//写该曲线名称

                  int nCount = 10000;//该曲线的数据总数(数组大小)

                  thePipe.WriteInt(nCount);

                  double *writeData=new double[nCount];

                  for (int i = 0; i < nCount; i++)

                  {

                         writeData[i]=i;//给每个曲线数据赋初值

                         thePipe.WriteDouble(writeData[i]);//顺序写曲线的数据

                  }

           }

    }

    //写文本信息

    void CNamedPipeClient_vcDlg::WriteTextInfo(char *textInfo)

    {

           thePipe.WriteString(textInfo);

    }

     

    void CNamedPipeClient_vcDlg::WriteData()

    {

           int flag=1;//传递的信息的标识,0表示曲线数据,1表示文本信息

           if (flag==0)

           {

                  thePipe.WriteInt(flag);

                  WriteCurveData();

           }

           else if (flag==1)

           {

                  thePipe.WriteInt(flag);

                  char * charWrite="hedafasfddsafdsafdsafdsafdsafsafdsafdsafdsafsaaaa";

                  WriteTextInfo(charWrite);

           }

    }

  • 相关阅读:
    类似-Xms、-Xmn这些参数的含义:
    类似-Xms、-Xmn这些参数的含义:
    类似-Xms、-Xmn这些参数的含义:
    类似-Xms、-Xmn这些参数的含义:
    Java 虚拟机是如何判定两个 Java 类是相同的?
    Java 虚拟机是如何判定两个 Java 类是相同的?
    Java 虚拟机是如何判定两个 Java 类是相同的?
    Java 虚拟机是如何判定两个 Java 类是相同的?
    互联网支付系统整体架构详解
    DTO
  • 原文地址:https://www.cnblogs.com/xiongxuanwen/p/1992389.html
Copyright © 2011-2022 走看看