管道(Pipe)是一種進程間的通訊機制,Windows、Linux和UNIX都使用這種機制。編程
管道是經過I/O接口存取的字節流建立管道後,經過使用操做系統的任何讀或寫I/O系統調用來讀或者寫它。建立管道的進程稱爲管道服務器,鏈接到一個管道的進程爲管道客戶機。一個進程在向管道寫入數據後,另外一進程就能夠從管道的另外一端將其讀取出來。Linux管道是經過返回兩個文件描述符來實現雙向I/O,而Windows管道是使用單一句柄(相似於Linux文件描述符)支持雙向I/O的,比Linux管道要複雜得多。緩存
匿名管道服務器
匿名管道只能用於相關進程(如父子進程、兄弟進程)之間的通訊,而且它創建在內存區域。進程終止後,匿名管道也就消失了。匿名管道使得關聯的進程能夠互相傳送信息,一般匿名管道用於重定向子進程的標準輸入輸出,以便於和父進程交換數據。要雙向交換數據必須建立兩個匿名管道。父進程使用寫句柄寫數據到一個管道,子進程使用讀句柄從管道中讀取數據,相應的子進程使用寫句柄寫數據到另外一個管道,父進程使用讀句柄從管道中讀取數據。匿名管道是同一臺計算機的關聯進程的子進程重定向標準輸入輸出的一種有效方法,但不能用於網絡環境,也不能用於非關聯的進程間。網絡
命名管道併發
命名管道是在管道服務器和一臺或多臺管道客戶機之間進行單向或雙向通訊的一種命名的管道。一個命名管道的全部實例共享同一個管道名,可是每個實例均擁有獨立的緩存與句柄,而且爲客戶—服務通訊提供一個分離的管道。實例的使用保證了多個管道客戶可以在同一時間使用同一個命名管道。命名管道用於在非關聯進程和不一樣計算機上的進程間傳送數據。一般命名管道服務器進程建立使用一個衆所周知的名字或客戶機知道名字的命名管道,知道管道名字的命名管道客戶機進程在管道另外一端打開管道,並服從服務器進程指定的訪問限制。在服務器和客戶機都鏈接到管道後,就能夠在管道上使用讀寫操做來交換數據。命名管道在進程間提供一個傳送數據的簡單的編程接口,無論進程是否在同一臺計算機上。函數
採用的UNC格式:\\Server\Pipe\[Path]Name
,其中,第一部分\\Server
指定了服務器的名字,命名管道服務即在此服務器建立,其字串部分可表示爲一個小數點(表示本機)、星號(當前網絡字段)、域名或是一個真正的服務;第二部分\Pipe
與郵槽的\Mailslot
同樣是一個不可變化的硬編碼字串,以指出該文件是從屬於NTFS;第三部分`[Path]Name`則使應用程序能夠惟必定義及標識一個命名管道的名字,並且能夠設置多級目錄。this
命名管道提供了兩種基本的通訊模式:字節模式和消息模式,可在CreateNamePipe()
建立命名管道時分別用PIPE_TYPE_BYTE
和PIPE_TYPE_MESSAGE
標誌進行設定。在字節模式中,信息以連續字節流的形式在客戶與服務器之間流動。這也就意味着對於客戶機應用和服務器應用在任何一個特定的時間段內都沒法準確知道有多少字節從管道中讀出或寫入。在這種通訊模式中,一方在向管道寫入某個數量的字節後並不能保證管道的另外一方能讀出等量的字節。對於消息模式,客戶機和服務器則是經過一系列不連續的數據包進行數據的收發。從管道發出的每一條消息都必須做爲一條完整的消息讀入。在此建議使用消息模式。編碼
要想實現一個命名管道服務器必須開發一個應用程序,經過它建立命名管道的一個或多個「實例」,再由客戶機進行訪問。對服務器來講,管道實例實際就是一個句柄,用於從本地或遠程客戶機的應用程序接受一個鏈接請求。按照下面的步驟,能夠寫出一個基本的服務器應用程序。操作系統
使用API函數CreateNamedPipe
建立一個命名管道實例句柄。線程
使用API函數ConnectNamedPipe
在命名管道實例上監聽客戶機的鏈接請求。
分別使用API函數ReadFile
和WriteFile
從客戶機接收數據或將數據發送給客戶機。
使用API函數DisconnectNamedPipe
關閉命名管道的鏈接。
使用API函數CloseHandle
關閉命名管道實例句柄
實現一個命名管道客戶機時要開發一個應用程序,令其創建與某個命名管道服務器的鏈接。注意客戶機不可建立命名管道實例,它可打開來自服務器的現成的實例。按照下面步驟,能夠編寫一個最基本的客戶機應用程序。
使用API函數WaitNamePipe
等待一個命名管道實例供自已使用。
使用API函數CreateFile
創建與命名管道的鏈接。
使用API函數WriteFile
和ReadFile
分別向服務器發送數據或從中接收數據。
使用API函數CloseHandle
關閉打開的命名管道會話。
m_hPipe=CreateNamedPipe(「\\\\.\\Pipe\\Test」,PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE/|PIPE_READMODE_BYTE, 1,0,0,1000,NULL);//建立命名管道 if(m_hPipe==INVALID_HANDLE_VALUE) m_sMessage=「Errorcreatepipe」; else { m_sMessage=「success creat pipe」; AfxBeginThread(ReadProc,this);//開啓線程 }
因爲ConnectNamedPipe()
函數在沒有客戶機鏈接到服務器時會無限的等待下去,所以爲避免由此引發的主線程的阻塞,而開闢了一個子線程ReadProc
:
UINT ReadProc(LPVOIDlpVoid) { char buffer[1024];//數據緩存 DWORD ReadNum; CServerView* pView=(CServerView*)lpVoid;//獲取 視句柄 if(ConnectNamedPipe(pView->m_hPipe,NULL)==FALSE)//等待客戶機的鏈接 { CloseHandle(pView->m_hPipe);//關閉管道句柄 pView->m_sMessage=「error connect」; pView->Invalidate(); return 0; } else { pView->m_sMessage=「success connect」; pView->Invalidate(); //從管道讀取數據 if(ReadFile(pView->m_hPipe,buffer,sizeof(buffer), &ReadNum,NULL)==FALSE) { CloseHandle(pView->m_hPipe);//關閉管道句柄 pView->m_sMessage=「read error」; pView->Invalidate(); } else { buffer[ReadNum]='\0';//顯示接收到的信息 pView->m_sMessage=CString(buffer); pView->Invalidate(); } return1; } }
在客戶同服務器創建鏈接後,ConnectNamedPipe()
纔會返回,其下語句才得以執行。隨後的ReadFile()
將負責把客戶寫入管道的數據讀取出來。在所有操做完成後,服務器能夠經過調用函數DisconnectNamedPipe()
而終止鏈接:
if(DisconnectNamedPipe(m_hPipe)==FALSE)//終止鏈接 m_sMessage=「error terminate connect」; else { CloseHandle(m_hPipe);//關閉管道句柄 m_sMessage=「success terminate connect」; }
客戶端鏈接服務器併發送數據
CString Message=「[testdata,fromclient]」;//要發送的數據 DWORD WriteNum;//發送的是數據長度 //等待與服務器的鏈接 if (WaitNamedPipe (「\\\\.\\Pipe\\Test「, NMPWAIT_WAIT_FOREVER)==FALSE) { m_sMessage=「error waiting connect」; Invalidate(); return; } //打開已建立的管道句柄 HANDLE hPipe=CreateFile(「\\\\.\\Pipe\\Test」,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); if(hPipe==INVALID_HANDLE_VALUE) { m_sMessage=「erroropenpipe」; Invalidate(); return; } else { m_sMessage=「successopenpipe」; Invalidate(); } //向管道寫入數據 if(WriteFile(hPipe,Message,Message.GetLength(), &WriteNum,NULL)==FALSE) { m_sMessage="error data write"; Invalidate(); } else { m_sMessage=「success write」; Invalidate(); } CloseHandle(hPipe);
客戶端接收數據
static UINT ReceiveFromPipe(LPVOID pArgs) { try { CNamedPipeClientCDlg* pDlg = (CNamedPipeClientCDlg*)pArgs; char szBuf[1024]={0}; memset(szBuf,0,sizeof(szBuf)); DWORD dwRead,dwWrite; while(1) { if(!ReadFile(pDlg->hlPC,szBuf,1024,&dwRead,0)) break; SetWindowText((HWND)pDlg->m_ReceivedEdit,szBuf); memset(szBuf,0,1024); } } catch (CMemoryException* e) { } catch (CFileException* e) { } catch (CException* e) { } return 0; }
因爲客戶端須要不斷接收服務器端的消息,因此須要在新線程中執行,防止阻塞主線程。
hlThread = AfxBeginThread(ReceiveFromPipe,this)
其中,hlThread
的定義爲
HANDLE hlThread;
在工程中引用AsyncPipe
,定義管道對象
private NamedPipeStreamClient pipeClient = new NamedPipeStreamClient("TestC");
發送消息
pipeClient.SendMessage(Encoding.Default.GetBytes(tbSend.Text));
接收消息
1.定義委託
pipeClient.MessageReceived += new MessageEventHandler(pipeClient_MessageReceived);
2.定義回調函數
void pipeClient_MessageReceived(object sender, MessageEventArgs args) { string content = Encoding.Default.GetString(args.Message); this.tbReceive.Text += content; }
定義管道對象
pipeNameStr:='\\.\Pipe\TestC'; if(WaitNamedPipe(pchar(pipeNameStr),NMPWAIT_WAIT_FOREVER)=FALSE) then begin ShowMessage(format('WaitNamedPipe failed with error %d',[GetLastError()])); exit; end; pipeHandle := CreateFile(pchar(pipeNameStr),GENERIC_READ or GENERIC_WRITE,FILE_SHARE_WRITE,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED,0); if pipeHandle = INVALID_HANDLE_VALUE then begin ShowMessage(format('CreateFile failed with error %d',[GetLastError()])); exit; end;
其中,pipeHandle
、pipeNameStr
的定義爲
pipeHandle:HWND; pipeNameStr:string;
發送消息
if WriteFile(pipeHandle,pchar(EditMessageToSend.Text)^,length(EditMessageToSend.Text),bytesWrite,nil)= False then begin ShowMessage(format('WriteFile failed with error %d',[GetLastError()])); exit; end; ShowMessage(format('write %d Bytes',[bytesWrite]));
接收消息
function ReadFromPipe(p:Pointer):DWORD;stdcall; begin if pipeHandle <> INVALID_HANDLE_VALUE then begin while TRUE DO begin if ReadFile(pipeHandle,buffer,sizeof(buffer),bytesRead,nil)=FALSE then begin ShowMessage(format('ReadFile failed with error %d',[GetLastError()])); Application.Destroy; end; SendMessage(integer(p),WM_MYMSG,1,1); end; end ; result := 0; end ;
其中,WM_MYMSG
的定義爲
const WM_MYMSG = WM_USER+1024;
對應的消息處理函函數聲明爲
procedure display(var msg:TMessage);message WM_MYMSG;
實現爲
procedure TForm1.display(var msg:TMessage); begin EditMessageReceived.Text := EditMessageReceived.Text + buffer; end;
因爲客戶端須要不斷接收服務器端的消息,因此須要在新線程中執行,防止阻塞主線程。
threadHandle := CreateThread(nil,0,@ReadFromPipe,Ptr(form1.Handle),0,threadId);
其中,threadHandle
、threadId
的定義爲
threadHandle:HWND; threadId:Cardinal;