今天記錄下自己學的郵槽和命名管道,學習過程中遇到點問題也拿出來分享下。哈 開整
先說一下大體的概念奧。
郵槽定義
郵槽(Mailslot)也稱為郵件槽,是Windows 提供的進程間通信的手段,
其提供的是基于不可靠的,郵件槽只支持單向數據傳輸,也就是服務器只能接收數據,而客戶端只能發(fā)送數據,
何為服務端?創(chuàng)建郵槽的那一端就是服務端。
還有需要提及的一點是,客戶端在使用郵槽發(fā)送數據的時候只有當數據的長度 < 425 字節(jié)時,
才可以被廣播給多個服務器,如果消息的長度 > 425 字節(jié)的話,那么在這種情形下,郵槽是不支持廣播通信的。
這是我看到的郵槽的簡要說明吧。
先說下郵槽的使用過程吧。然后再分析函數,在貼代碼。非常簡單哦
服務端: 客戶端:
首先創(chuàng)建郵槽CreateMailslot 打開油槽CreateFile
讀取數據 ReadFile 寫入數據WriteFile
完事了,只有這四個函數。也很容易理解。 客戶端寫入數據 服務端讀取數據。
CreateMailslot(_T("\\\\.\\mailslot\\chenxiao"),0, MAILSLOT_WAIT_FOREVER,NULL);
第一個參數是個固定格式\\.\\mailslot\\name 點代表本機。mailslot是硬編碼 不能變,name可以自己起個郵槽的名字。‘\’放入字符串中要用轉義字符\
所以就寫成了"\\\\.\\mailslot\\chenxiao"
第二個參數To specify that the message can be of any size, set this value to zero. 設置成0
第三個參數為了下面的讀取操作應該等待的時間 MAILSLOT_WAIT_FOREVER 傳這個代表參數代表永久等待。
最后一個參數安全屬性 嘎嘎 null
ReadFile(hMailSlot,pData,sizeof(TCHAR)*80,&dByteRead,NULL);
這幾個參數很簡單了。第一個參數就是創(chuàng)建郵槽返回來的句柄 第二個參數一個[out]buffer用來接收從郵槽中讀出來的東東。第三個參數就是讀取多少個字節(jié)。
第四個參數基本沒用,是一個[out]的LPDWord 很蛋疼只能DWORD dByteRead; 然后傳個他的地址。
因為msdn上說了If lpOverlapped is NULL, lpNumberOfBytesRead cannot be NULL;
lpoverlapped就是我們的最后一個參數,這個參數可以設置同步和異步,如果文件打開模式是FILE_FLAG_OVERLAPPED這個的話,我們這個就不可以是NULL
這個同步異步問題我在下面的命名管道中在說。這里就先過去。這個參數設成NULL。
客戶端函數
CreateFile(_T("\\\\.\\mailslot\\chenxiao"),GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL);
這幾個參數也很容易理解了。第一個參數要和創(chuàng)建郵槽的時候的參數一樣。如果要遠程通信的話可以把‘.’設置成服務器 主機名 或者在一個區(qū)域內廣播‘*’
但是我用兩個機器實驗了,沒有成功,目前我只能用郵槽在本地一個機器上通信。。。尷尬。。有知道怎么在兩個機器上通信的,要給我留言教教我哦。
后幾個參數根據參數名大家就可以猜個差不多了,我就不說了。吼吼。
WriteFile(hMailSlot,str,sizeof(TCHAR)*80,&dByteWrite,NULL);這個函數 跟 readfile差不多 就是向郵槽中寫入數據用的。
第二個參數是要寫入的內容,第三個是大小(以字節(jié)為單位).
好了這幾個函數都說完了。貼上小代碼,就清晰了。
//服務器端 我用的c++寫的。
#include <iostream>
#include <Windows.h>
#include <tchar.h>
using namespace std;


int main()

{
HANDLE hMailSlot=CreateMailslot(_T("\\\\.\\mailslot\\chenxiao"),0,
MAILSLOT_WAIT_FOREVER,NULL);
TCHAR pData[80];
ZeroMemory(pData,sizeof(TCHAR)*80);
DWORD dByteRead;
while(1)

{
BOOL b=ReadFile(hMailSlot,pData,sizeof(TCHAR)*80,&dByteRead,NULL);
wprintf_s(_T("%s\n"),pData);
}
system("pause");
return 0;
}
//客戶端我在mfc中寫的。
void CclientDlg::OnBnClickedButtonSend()

{
TCHAR str[80];
ZeroMemory(str,sizeof(TCHAR)*80);
GetDlgItem(IDC_EDIT_INPUT)->GetWindowText(str,70);
DWORD dByteWrite;
HANDLE hMailSlot=CreateFile(_T("\\\\.\\mailslot\\chenxiao"),GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,NULL);
if (hMailSlot==INVALID_HANDLE_VALUE)

{
MessageBox(_T("createfile失敗,請打開服務器"));
return ;
}
BOOL b=WriteFile(hMailSlot,str,sizeof(TCHAR)*80,&dByteWrite,NULL);
GetDlgItem(IDC_EDIT_INPUT)->SetWindowText(_T(""));
CloseHandle(hMailSlot);
}

這就是運行結果啦。這個東西沒啥大用。就是學習一下而已。以后萬一用到也能弄弄。
下面我說下命名管道。這個東西坑了我一下午。。。
郵槽建立的是無連接的通信。。那么命名管道 就是有鏈接的可靠的通信了。他跟郵槽挺相似的。但是比郵槽好很多。
同上面。我粘一些概念性的東西。
命名管道是通過網絡來完成進程之間的通信的,命名管道依賴于底層網絡接口,
其中包括有 DNS 服務,TCP/IP 協(xié)議等等機制,但是其屏蔽了底層的網絡協(xié)議細節(jié),
對于匿名管道而言,其只能實現在父進程和子進程之間進行通信,而對于命名管道而言,
其不僅可以在本地機器上實現兩個進程之間的通信,還可以跨越網絡實現兩個進程之間的通信。
命名管道使用了 Windows 安全機制,因而命名管道的服務端可以控制哪些客戶有權與其建立連接,
而哪些客戶端是不能夠與這個命名管道建立連接的。
利用命名管道機制實現不同機器上的進程之間相互進行通信時,
可以將命名管道作為一種網絡編程方案時,也就是看做是 Socket 就可以了,
它實際上是建立了一個客戶機/服務器通信體系,并在其中可靠的傳輸數據。
命名管道的通信是以連接的方式來進行的,
服務器創(chuàng)建一個命名管道對象,然后在此對象上等待連接請求,
一旦客戶連接過來,則兩者都可以通過命名管道讀或者寫數據。
命名管道提供了兩種通信模式:字節(jié)模式和消息模式。
在字節(jié)模式下,數據以一個連續(xù)的字節(jié)流的形式在客戶機和服務器之間流動,
而在消息模式下,客戶機和服務器則通過一系列的不連續(xù)的數據單位,進行數據的收發(fā),
每次在管道上發(fā)出一個消息后,它必須作為一個完整的消息讀入。
我相信很多人看了幾句就跳到這里來了。。概念性的東西 確實太不好玩了。我也不愛看。。哈哈
介紹命名管道需要的函數。
服務器端
CreateNamedPipe 創(chuàng)建命名管道
ConnectNamedPip 連接
ReadFile 讀
WriteFile 寫
客戶端
WaitNamedPipe 查看命名管道
CreateFile 打開命名管道
WriteFile ReadFile 寫 讀
就這些東西,今天由于不仔細看msdn 寫程序寫蒙了。。。等會我在說啊。大家要注意哦。
CreateNamedPipe(_T("\\\\.\\pipe\\chenxiao"),PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,PIPE_TYPE_BYTE,1,1024,1024,2000,NULL);
很多參數啊!不怕不怕 慢慢來
第一個我略過了哦。第二個大家在msdn上可以看到有
PIPE_ACCESS_DUPLEX 讀寫雙向
PIPE_ACCESS_INBOUND 數據只能從客戶端到服務端
PIPE_ACCESS_OUTBOUND 和上面那個相反
這個參數我們設置成 第一個。然而通過msdn大家可以看到這個參數可以附加 flag 我們就附加FILE_FLAG_OVERLAPPED 這個了
MSDN那一大堆英文我也瞅不太明白,大至意思就是這個呢 用了這個參數 程序操作讀,寫,連接等操作,可以立馬返回。比如說讀一個大文件吧
你把這個文件從頭讀到偉 需要很長時間。這樣的話你的readfile函數就不會反回 就會阻塞在那里一直讀。這樣很不好,所以有了這個參數。這個參數就是使你的讀 寫 等待函數立馬返回,這個就屬于程序的異步,這個讀函數和主程序一起執(zhí)行。
下一個參數就是以字節(jié)流還是消息方式發(fā)送文件 讀取文件。我們采用字節(jié)流方式PIPE_TYPE_BYTE。
下一個參數是最多可以創(chuàng)建幾個命名管道 比如我們設置成3,就是可以創(chuàng)建3個這樣的管道。我們這里設置成1,我們只用一個管道做演示就行。然后是分配的輸入 輸出 緩沖區(qū)大小 ,就類似創(chuàng)建線程時分配棧空間大小一樣。然后是一個超時時間設置 這個設置成0就可以。最后一個NULL安全屬性
ConnectNamedPipe服務端的連接管道函數這個函數兩個參數第一個參數句柄,第二個參數一個結構體對象
這個結構體呢 里面有一個事件句柄。剛才上邊由于設置了異步,所以你要有一個標志著讀結束的標志,這個標志就用的這個事件。創(chuàng)建這個事件要設置成手動的,初始為無信號。
這樣服務端的就寫完了。
然后再說一下客戶端的函數
WaitNamedPipe(_T("\\\\.\\pipe\\chenxiao"),0);
這個函數呢就屬于一個查看函數,看看有沒有叫chenxiao的命名管道
大家不要認為這個函數可以打開命名管道 或者連接管道
大家從msdn上可以看到這句話If the function succeeds,the process should use the CreateFile function to open a handle to the named pipe
今天我由于沒看到這句話苦苦弄了一個下午也沒連上管道5555555555
在客戶端可以用waitnamedpipe檢查下有沒有這個管道 然后再createfile打開它。
哦了 搞定了。搞上我的代碼瞅瞅效果。
//服務器端的代碼 MFC寫的

void CPipeServerDlg::OnBnClickedButtonCreate()

{
m_hNP=CreateNamedPipe(_T("\\\\.\\pipe\\chenxiao"),
PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE,1,1024,1024,0,NULL);
if (m_hNP==INVALID_HANDLE_VALUE)

{
MessageBox(_T("創(chuàng)建管道失敗"));
}
else

{
MessageBox(_T("創(chuàng)建管道成功"));
}
//連接-----------------------------
OVERLAPPED op;
ZeroMemory(&op,sizeof(OVERLAPPED));
op.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
BOOL b=ConnectNamedPipe(m_hNP,&op);
if (WaitForSingleObject(op.hEvent,INFINITE)==0)

{
MessageBox(_T("connect成功 haha"));
}
else

{
MessageBox(_T("create fail"));
}
}

void CPipeServerDlg::OnBnClickedButtonWrite()

{
TCHAR buff[100]=_T("來自服務器的信息");
DWORD d;
WriteFile(m_hNP,buff,200,&d,NULL);
}

void CPipeServerDlg::OnBnClickedButtonRead()

{
TCHAR buff[100];
ZeroMemory(buff,200);
DWORD d;
ReadFile(m_hNP,buff,200,&d,NULL);
MessageBox(buff);
}

//客戶端的代碼 MFC寫的


void CPipeClientDlg::OnBnClickedButtonOpenpipe()

{
BOOL b=WaitNamedPipe(_T("\\\\.\\pipe\\chenxiao"),0);
//BOOL b=1;
m_hFile = CreateFile(_T("\\\\.\\pipe\\chenxiao"),
GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (!b||m_hFile==INVALID_HANDLE_VALUE)

{
MessageBox(_T("連接失敗"));
}
else

{
MessageBox(_T("連接成功"));
}
}

void CPipeClientDlg::OnBnClickedButtonRecieve()

{
TCHAR buff[100];
ZeroMemory(buff,200);
DWORD d;
ReadFile(m_hFile,buff,200,&d,NULL);
MessageBox(buff);
}

void CPipeClientDlg::OnBnClickedButtonSend()

{
TCHAR buff[100]=_T("client's message");
DWORD d;
WriteFile(m_hFile,buff,200,&d,NULL);
}

下圖程序運行效果圖
哇卡卡阿卡