程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> c++通道應用(服務器篇)

c++通道應用(服務器篇)

編輯:關於C++

【C++程序中命名管道通訊的實現】

最近學習c++中,試手過程選擇實 現一個程序與被注入程序互通的工具,感覺如果是計算機本地的通訊使用socket 似乎有些不太理性,俗話說“殺雞焉牛宰牛刀”,所以使用偉大的 google找到了更適合輕量級程序通訊的內容,即下面所要說的:“管道 ”。

首先按照國際管理,介紹下c++實現“管道” 的幾 個核心函數;

CreateNamedPipe(        //服務器端創建 並命名一個管道,服務器端通過提供管道名與其進行通訊
  LPCTSTR lpName, // 管道名稱
  DWORD dwOpenMode, // 管道打開模式
  DWORD dwPipeMode, // 管道的其他模式定義
  DWORD nMaxInstances, // 這個管道能夠創建的最大實例數量 。必須是1到常數PIPE_UNLIMITED_INSTANCES間的一個值。它對於管道的所有實 例來說都應是相同的
  DWORD nOutBufferSize, // 建議的輸出緩沖區長度;零表示 用默認設置
  DWORD nInBufferSize, //  建議的輸入緩沖區長度;零表示 用默認設置
  DWORD nDefaultTimeOut, // 管道的默認等待超時。對一個管 道的所有實例來說都應相同
  LPSECURITY_ATTRIBUTES lpSecurityAttributes // pointer to security attributes
  )

詳細參數請見:http:  //vbworld.sxnw.gov.cn/vbapi/detail/CreateNamedPipe.htm

C onnectNamedPipe( //指示一台服務器等待下去,直至客戶機同一個命名管道連 接
  HANDLE handle, //管道的句柄
      lpOverlapped OVERLAPPED  //如設為NULL(傳遞 ByVal As Long),表示將線程掛起,直到一個客戶同管道連接為止。否則就立 即返回;此時,如管道尚未連接,客戶同管道連接時就會觸發lpOverlapped結構 中的事件對象。隨後,可用一個等待函數來監視連接
  )

詳細參數請見:http:  //vbworld.sxnw.gov.cn/vbapi/detail/ConnectNamedPipe.htm

WaitNamedPipe(        //由一個客戶進程調用,等候一個管道變成可 用狀態
  LPCTSTR lpNamedPipeName, //指定要連接的管道名稱
  DWORD nTimeOut //超時設定
  )

詳細參數請見:http:  //vbworld.sxnw.gov.cn/vbapi/detail/WaitNamedPipe.htm CreateFile(
  LPCTSTR lpFileName, //指向文件名的指針,如果是寫入到管 道當中,則寫入管道名
    DWORD dwDesiredAccess, //訪問模式(寫/讀)
    DWORD dwShareMode, //共享模式
    LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向 安全屬性的指針
    DWORD dwCreationDisposition, //如何創建
    DWORD dwFlagsAndAttributes, //文件屬性
    HANDLE hTemplateFile //用於復制文件句柄
  )

詳細參數請見:http://baike.baidu.com/view/1288759.htm

WriteFile( //用於向 管道中寫入內容
  HANDLE hFile, // 文件句柄,通過CreateFile創建的句柄
    LPCVOID lpBuffer, // 數據緩存區指針
    DWORD nNumberOfBytesToWrite, // 你要寫的字節數
    LPDWORD lpNumberOfBytesWritten, // 用於保存實際寫 入字節數的存儲區域的指針
    LPOVERLAPPED lpOverlapped // OVERLAPPED結構體指針
  )

詳細參數請見:http://baike.baidu.com/view/1295782.htm ReadFile(     //用於讀取管道中以寫入的信息
  HANDLE hFile, //文件的句柄
    LPVOID lpBuffer, //用於保存讀入數據的一個緩沖區
    DWORD nNumberOfBytesToRead, //要讀入的字符數
    LPDWORD lpNumberOfBytesRead, //指向實際讀取字節數 的指針
    LPOVERLAPPED lpOverlapped //如文件打開時指定了 FILE_FLAG_OVERLAPPED,那麼必須,用這個參數引用一個特殊的結構。該結構定 義了一次異步讀取操作。否則,應將這個參數設為NULL
 )

詳細參數請見:http://baike.baidu.com/view/1336847.htm。行了,假設看這篇文章的同學都已經 了VS2005下MFC的創建和使用,下面的內容將開始實刀實槍的開始“管道之 旅”,使用MFC創建一個標准的管道通訊程序。

【server部分:】

1、為了更好的控制我們的程序,第一步定義一個管道基類是個好的做法 ,在這裡我們借鑒一個例子,創建一個"CMyPipe.h"的頭文件,代碼 如下:

#pragma once
class CMyPipe //創建我們的通道基類,主要為了方便定位通道
{
public:
 CMyPipe(); //創建一個構造函數;
 CMyPipe(LPCTSTR lpctstr); //構造函數的重載;
 ~CMyPipe(); //一個析構函數;

protected:
 HANDLE MyPipe; //創建一個HANDLE 變量,用於定義我們的通道 頭
 CString MyPipeName; //創建一個通道名;
};

2、定義一個基類CMyPipe的擴展類CPipeServer ,將其寫入到一個新的頭文件中,在這裡我命名為pp.h,代碼如下:

#pragma once
#include "CMyPipe.h"
class CPipeServer:public CMyPipe //定義基類CMyPipe的擴展 類
{
public: //構造函數
 CPipeServer();
 CPipeServer(LPCTSTR lpPipeName); //構造函數重載
 ~CPipeServer(); //析構函數
 CWnd* GethWndServer(); //定義一個獲取窗口函數
 CWnd* SethWndServer(CWnd *pWndServer); //定義設置窗口的 函數
 bool ServerReplyInfo(CString StrReply); //定義創建通道反 饋信息的函數
 bool ServerExit(); //服務器退出時使用
 static UINT ServerReadProc(LPVOID lParam); //管道服務器 創建後的操作,創建服務器可不是讓他閒著。。。
 void InitPipeServer(UINT InstanceCount);
 void UnInitPipeServer(void);
 CPipeServer *m_pPipeServer; //CPipeServer類動態數組(可實 現多個管道實例,提高管道服務器端負荷處理能力)
 UINT m_InstanceCount; //管道實例個數
private:
 CWnd* m_pWndServer; //定義將要寫入的窗口類變量
 CWinThread *m_pWinThread; //每次使用新線程調用服務器時的 存儲線程變量
 CString m_StrReply; //接收的信息存儲變量
};

3、接下來我們將對之前定義的函數在項目中的 cpp文件中進行實例化,並做應用:

//3個CMyPipe基類的實例化


CMyPipe::CMyPipe()//直接定義管道名
{
 MyPipeName=_T("\\\\.\\pipe\\snmp");
}
CMyPipe::CMyPipe(LPCTSTR lpPipeName)  //管道名的重載
{
 MyPipeName=lpPipeName;
}
::CMyPipe::~CMyPipe()
{
 MyPipeName.Empty();
}
CPipeServer::CPipeServer():CMyPipe()
{
 m_StrReply.Empty();
 m_pWndServer=NULL;
}
CPipeServer::CPipeServer(LPCTSTR lpPipeName):CMyPipe (lpPipeName)
{
 m_StrReply.Empty();
 m_pWndServer=NULL;
}
CPipeServer::~CPipeServer()
{
 m_StrReply.Empty();
 m_pWndServer=NULL;
}
//因為涉及到了新建線程接收管道信息,所以需要定位控件窗口
CWnd * CPipeServer::SethWndServer(CWnd *pWndServer)
{
 return m_pWndServer=pWndServer;
}
CWnd* CPipeServer::GethWndServer()
{
 return m_pWndServer;
}
//定位控件窗口

void CPipeServer::InitPipeServer(UINT InstanceCount)
{
    m_pPipeServer=NULL;
    m_InstanceCount=0;
    m_pPipeServer=new CPipeServer[InstanceCount];
    if(m_pPipeServer!=NULL)
        m_InstanceCount=InstanceCount;
}
void CPipeServer::UnInitPipeServer(void)
{
    if(m_pPipeServer!=NULL){
        delete [] m_pPipeServer;
        m_pPipeServer=NULL;
        m_InstanceCount=0;
    }
}

//定義創建通道反饋信息的函數
bool CPipeServer::ServerReplyInfo(CString StrReply)
{
 bool IsSuc=false;
 MyPipe=CreateNamedPipe(MyPipeName,
  PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,
  PIPE_TYPE_BYTE|PIPE_WAIT,
  PIPE_UNLIMITED_INSTANCES,
  256,
  256,
  NULL, //NMPWAIT_USE_DEFAULT_WAIT,
  NULL);
 if (MyPipe!=INVALID_HANDLE_VALUE)
 {
  m_StrReply=StrReply;
  IsSuc=true;
  m_pWinThread=AfxBeginThread(ServerReadProc,(LPVOID) this);
 }
 return IsSuc;
}
//定義創建通道反饋信息的函數
//定義服務器退出時的操作
bool CPipeServer::ServerExit()
{
 bool IsSuc=false;
 HANDLE hFile;
 TCHAR buffer[256];
 DWORD dwNumberOfBytesWritten;
 WaitNamedPipe(MyPipeName,NMPWAIT_WAIT_FOREVER);
 hFile=CreateFile (MyPipeName,GENERIC_WRITE|GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRIT E,
                     NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_ARCHIVE|FILE_FLAG_WRITE_THROUGH,NULL   );
 if(hFile==INVALID_HANDLE_VALUE)
 {
  return IsSuc;
 }
 _stprintf(buffer,_T("exit"));
 if (WriteFile(hFile,buffer,(_tcslen(buffer)+1)*sizeof (TCHAR),&dwNumberOfBytesWritten,NULL))
 {
  IsSuc=true;
 }
 CloseHandle(hFile);
 return IsSuc;
}
//定義服務器退出時的操作


//服務器讀取並處理信息
UINT CPipeServer::ServerReadProc(LPVOID lParam)
{
 CPipeServer * pPipeServer=(CPipeServer*)lParam;
 DWORD dwNumberOfBytesRead,dwNumberOfBytesWritten;
 TCHAR buffer[256];
 while(ConnectNamedPipe(pPipeServer->MyPipe,NULL))
 {
  while(ReadFile(pPipeServer- >MyPipe,buffer,256*sizeof(TCHAR),&dwNumberOfBytesRead,NULL))
  {
   if(dwNumberOfBytesRead==0) break;
   if(_tcscmp(buffer,_T("exit"))==0)
   {
    DisconnectNamedPipe(pPipeServer->MyPipe);
                CloseHandle(pPipeServer- >MyPipe);
                return 0;
   }
   if(pPipeServer->m_pWndServer!=NULL)
   {
    ((CListBox*)pPipeServer->m_pWndServer- >GetDlgItem(IDC_LBCONTENT))->InsertString(0,buffer);
   }
   _tcscpy(buffer,pPipeServer->m_StrReply);
   WriteFile(pPipeServer->MyPipe,buffer,(_tcslen (buffer)+1)*sizeof(TCHAR),&dwNumberOfBytesWritten,NULL);
  }
  DisconnectNamedPipe(pPipeServer->MyPipe); //管道服務 器斷開該實例的連接
 }
 CloseHandle(pPipeServer->MyPipe); //關閉實例句柄
    return 0;

}
//服務器讀取並處理信息


//使用一個ID為IDC_BTNCONNECT的Button控件控制服務器開啟/關 閉,並使用一個ID為IDC_BTNCONNECT的combo box的控件顯示服務器端顯示的信 息。
void CaccDlg::OnBnClickedBtnconnect()
{
  CPipeServer * pPipeServer=(CPipeServer*)this;
     CString MyCaption;
     GetDlgItem(IDC_BTNCONNECT)->GetWindowText (MyCaption);
     UINT i;
     if(MyCaption.Compare(_T("連接"))==0) {
         for(i=0;i<pPipeServer- >m_InstanceCount;i++){
    pPipeServer->m_pPipeServer->SethWndServer (this);
             pPipeServer->m_pPipeServer- >ServerReplyInfo(_T("服務器應答!"));
         }
         GetDlgItem(IDC_BTNCONNECT)- >SetWindowText(_T("斷開"));
     }
     else{
         for(i=0;i<pPipeServer- >m_InstanceCount;i++)
             pPipeServer->m_pPipeServer- >ServerExit();
         GetDlgItem(IDC_BTNCONNECT)- >SetWindowText(_T("連接"));
     }
}

OK,按F5執行試一下……

是 不是有句提示:“CaccDlg”: 不是類或命名空間名稱

哈哈 ,說明你照抄了上面的那段OnBnClickedBtnconnect()的代碼,請把 “CaccDlg”改成你所制作的項目的Dlg名。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved