標籤:
Windows 郵件槽(MailSlot)
來自《Windows網路編程第二版 中文版》
優點:通過網路,將一條訊息廣播給一台或多台電腦。
缺點:只允許從客戶機到伺服器,建立一種不可靠的單向資料通訊。不提供資料可靠性傳播的保障。 郵件槽是圍繞Windows檔案系統介面設計出來的。客戶機和伺服器應用需要使用標準的Win32檔案系統I/O函數,如ReadFile和WriteFile等,以便在郵件槽上收發資料,同時利用Win32檔案系統的命名規則。
郵件槽的名字郵件槽標識遵守下述命名規則://server/Mailslot/[path]name第一部分//server對應伺服器名,在其上建立郵件槽並在上面運行伺服器程式。取值可以是小數點(.),一個星號(*),一個網域名稱或者一個真正的伺服器名字。所謂“域”,是一系列工作站和伺服器的組合,它們共用一個相同的組名。第二部分/Mailslot是固定的字串。第三部分/[path]name,其中“path”代表路徑,可指多級目錄。下面都是合法的名字://Oreo/Mailslot/Mymailslot//Testserver/Mailslot/Cooldirectory/Funtest/Anothermailslot//./Mailslot/Easymailslot//*/Mailslot/Myslot
訊息的長度郵件槽常用“無串連”形式發送“資料報”(Datagram),不要求對方提供包的收到確認資訊。通過無串連可將訊息從一個客戶機廣播給多個伺服器。例外:在Windows NT和Windows 2000中,假如訊息長度超過424個位元組,必須使用“連線導向”的協議進行傳輸,而不再使用不需連線的“資料報”形式。這樣也就不能將一條訊息從客戶機廣播給多個伺服器。對於“連線導向”的傳輸來說,必然是“一對一”通訊:一個客戶機對一個伺服器。
應用程式的編譯用VC++ 6.0編製郵件槽應用程式時,必須包含Winbase.h標頭檔。如果已經包含Windows.h就可省去Winbase.h。應用程式需要與Kernel32.lib連結(是VC++ 6.0預設配置)。
錯誤碼郵件槽應用開發中,所有Win32 API函數(CreateFile和CreateMailslot除外)在調用失敗時都返回0。CreateFile和CreateMailslot這兩個API返回INVALID_HANDLE_VALUE(無效控制代碼值)。調用失敗可以使用GetLastError函數接受與此次失敗相關的特殊資訊。
基本客戶機/伺服器伺服器處理序:建立一個郵件槽,能從該郵件槽讀資料的唯一一個進程。客戶機:負責開啟郵件槽“執行個體”,是能向其中寫入資料的唯一一種進程。
伺服器:1、用CreateMailslot API函數建立一個郵件槽並獲得控制代碼。2、調用ReadFile API函數,使用已有的郵件槽控制代碼從任何客戶機接收資料。3、用CloseHandle函數關閉郵件槽控制代碼。建立郵件槽:HANDLE CreateMailslot(LPCTSTR lpName, //指定郵件槽的名字,如//./Mailslot/[path]name,小數點表示伺服器為本的機器(不能為遠端電腦建立郵件槽)。DWORD nMaxMessageSize,//可寫入郵件槽的最大訊息長度(位元組單位),客戶機發生訊息大於該值伺服器不接受該訊息;為0,接收任意長度訊息。DWORD lReadTiemout,//等待模式和不等待模式,MAILSLOT_WAIT_FOREVER無限期等待,0立即返回,其它值以毫秒為單位。LPSECURITY_ATTRIBUTES lpSecurityAttributes//存取控制許可權,一般都這位NULL);讀取郵件槽中資料:BOOL ReadFile(HANDLE hFile,//CreateMailslot返回的郵件槽控制代碼LPVOID lpBuffer,//和nNumberOfBytesToRead一起決定可從郵件槽讀入多少資料;應比CreateMailslot中的nMaxMessageSize。DWORD nNumberOfBytesToRead,//如果不夠大,ReadFile調用失敗返回ERROR_INSUFFICIENT_BUFFER錯誤。LPWORD lpNumberOfBytesRead,//讀取完成後報告讀入的實際位元組數。LPOVERLAPPED lpOverlapped//可採用Win32重疊I/O機制;不採用就設為NULL,調用後阻塞直到有資料讀入。);
- //Server.cpp
- //伺服器郵件槽,用於接收客戶發送的廣播資訊
- #include <windows.h>
- #include <stdio.h>
- int main()
- {
- HANDLE Mailslot;
- char buffer[256];
- DWORD NumberOfBytesRead;
- //Create the mailslot
- Mailslot = CreateMailslot("////.//Mailslot//Myslot",0,
- MAILSLOT_WAIT_FOREVER,NULL);
- if (INVALID_HANDLE_VALUE == Mailslot)
- {
- printf("Failed to create a mailslot %d/n", GetLastError());
- return -1;
- }
- //Read data from the mailslot forever!
- while (0 != ReadFile(Mailslot, buffer, 256, &NumberOfBytesRead, NULL))
- {
- printf("%.*s/n",NumberOfBytesRead,buffer);
- }
- CloseHandle(Mailslot);
- return 0;
- }
客戶機:對一個現有的郵件槽進行引用和寫入。1、使用CreateFile,針對想要向其傳送資料的郵件槽,開啟指向它的一個引用控制代碼。2、調用WriteFile,向郵件槽寫入資料。3、完成資料寫入後,用CloseHandle關閉開啟的郵件槽控制代碼。開啟指向郵件槽的一個引用控制代碼:HANDLE CreateFile(LPCTSTR lpFileName,//郵件槽名字DWORD dwDesiredAccess,//必須為GENERIC_WRITE,因為客戶機只能向伺服器寫入資料。DWORD dwSharedMode,//必須為FILE_SHARE_READ,運行伺服器在郵件槽開啟和進行讀操作。LPSECURITY_ATTRIBUTES lpSecurityAttributes,//設為NULL。DWORD dwCreationDisposition,//設為OPEN_EXISTING,伺服器是本地且沒有建立郵件槽調用會失敗;伺服器在遠程,該參數無意義。DWORD dwFlagsAndAttributes,//設為FILE_ATTRIBUTE_NORMALHANDLE hTemplateFile//設為NULL);寫入資料:BOOL WriteFile(HANDLE hFile,//CreateFile返回的引用控制代碼LPCVOID lpBuffer,//發送的字串DWORD nNumberOfBytesToWrite,//發送的字串長度(一條最大長度為64KB)。LPDWORD lpNumberOfBytesWritten,//操作完成後實際發送的資料位元組數。LPOVERLAPPED lpOverlapped//郵件槽是“無串連”資料轉送,WriteFile函數在不會在I/O調用時等候,所以參數設為NULL
- //Client.cpp
- //用戶端用於發送廣播資料到伺服器
- #include <windows.h>
- #include <stdio.h>
- int main(int argc, char *argv[])
- {
- HANDLE Mailslot;
- DWORD BytesWritten;
- CHAR ServerName[256];
- //從命令列接受要發送資料到的伺服器名
- if (argc < 2)
- {
- printf("Usage: client <server name>/n");
- return -1;
- }
- sprintf(ServerName, "////%s//Mailslot//Myslot",argv[1]);
- Mailslot = CreateFile(ServerName, GENERIC_WRITE,
- FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,NULL);
- if (INVALID_HANDLE_VALUE == Mailslot)
- {
- printf("WriteFile failed with error %d/n",GetLastError());
- return -1;
- }
- if(0 == WriteFile(Mailslot, "This is a test", 14, &BytesWritten,NULL))
- {
- printf("WriteFile failed with error %d/n", GetLastError());
- return -1;
- }
- printf("Wrote %d byteds/n", BytesWritten);
- CloseHandle(Mailslot);
- return 0;
- }
伺服器其它APIGetMailslotInfo:一旦郵件槽上有訊息可以傳遞,GetMailslotInfo函數負責擷取訊息的長度資訊;利用這個函數,程式可以針對長度不定的進入訊息,動態調節其緩衝區大小。GetMailslotInfo也可以用來對進入資料進行“輪詢”。BOOL GetMailslotInfo(HANDLE hMailslot,//CreateMailslot返回的郵件槽控制代碼。LPDWORD lpMaxMessageSize,//設定可將多大的一條訊息寫入郵件槽(位元組單位)。LPDWORD lpNextSize,//下一條訊息的長度,可能返回MAILSLOT_NO_MESSAGE,表明當前沒有等待接收的訊息。LPDWORD lpMessageCount,//函數返回時讀入多少個等待的訊息。LPDWORD lpReadTimeout//等待訊息寫入的時間長度(毫秒)。); SetMailslotInfo:設定郵件槽的逾時值。超過該值讀操作不再等待進入的訊息。BOOL SetMailslotInfo(HANDLE hMailslot,//CreateMailslot返回的郵件槽控制代碼。DWORD lReadTimeout//已毫秒為單位指定讀操作等待一條訊息寫入的最長時間,0,沒有訊息立即返回;MAILSLOT_WAIT_FOREVER永遠等待下去。);
不能取消“阻塞”的I/O請求對於Windows 95和Windows 98,郵件槽伺服器用ReadFile接受資料,假如用MAILSLOT_WAIT_FOREVER標誌建立一個郵件槽,讀請求便會一直等待直到有資料可用為止。如果ReadFile請求尚未完成,伺服器應用突然中止運行,應用程式會被永遠“掛起”或“阻塞”。只有重啟Windows才能取消。為瞭解決這個問題,可讓伺服器在單獨一個線程中,開啟一個控制代碼,令其指向自己的郵件槽。並在需要退出時在主線程中發生資料,以終止處於暫停狀體的讀操作。
- //Server2.cpp
- //非阻塞伺服器郵件槽,用於接收客戶發送的廣播資訊
- #include <windows.h>
- #include <stdio.h>
- #include <conio.h>
- BOOL StopProcessing;
- DWORD WINAPI ServeMailslot(LPVOID lpParameter);
- void SendMessageToMailslot(void);
- int main()
- {
- DWORD ThreadId;
- HANDLE MailslotThread;
- StopProcessing = FALSE;
- MailslotThread = CreateThread(NULL,0,ServeMailslot,
- NULL,0,&ThreadId);
- printf("Press a key to stop the server/n");
- _getch();
- //Mark the StopProcessing flag to TRUE so that when ReadFile
- //break, our server thread will end
- StopProcessing = TRUE;
- //Send a message to our mailslot to break the ReadFile call
- //in our server
- SendMessageToMailslot();
- //等待服務線程完成
- if (WAIT_FAILED == WaitForSingleObject(MailslotThread, INFINITE))
- {
- printf("WaitForSingleObject failed with error %d/n",GetLastError());
- return -1;
- }
-
- return 0;
- }
- //This function is the mailslot server worker function to
- //process all incoming mailslot I/O
- DWORD WINAPI ServeMailslot(LPVOID lpParameter)
- {
- char buffer[2048];
- DWORD NumberOfBytesRead;
- DWORD Ret;
- HANDLE Mailslot;
- Mailslot = CreateMailslot("////.//mailslot//myslot",2048,MAILSLOT_WAIT_FOREVER,NULL);
- if (INVALID_HANDLE_VALUE == Mailslot)
- {
- printf("Failed to create a MailSlot %d/n",GetLastError());
- return -1;
- }
- while (0 != (Ret = ReadFile(Mailslot,buffer,2048,&NumberOfBytesRead,NULL)))
- {
- if (StopProcessing)
- break;
- printf("Received %d bytes/n", NumberOfBytesRead);
- }
- CloseHandle(Mailslot);
- return 0;
- }
- //The SendMessageToMailslot function is designed to send a
- //simple message to our server so we can break the blocking
- //ReadFile API call
- void SendMessageToMailslot()
- {
- HANDLE Mailslot;
- DWORD BytesWritten;
- Mailslot = CreateFile("////.//mailslot//myslot",GENERIC_WRITE,
- FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
- if (INVALID_HANDLE_VALUE == Mailslot)
- {
- printf("CreateFile failed with error %d/n", GetLastError());
- return;
- }
- if (0 == WriteFile(Mailslot, "STOP", 4, &BytesWritten, NULL))
- {
- printf("WriteFile failed with error %d/n", GetLastError());
- return;
- }
- CloseHandle(Mailslot);
- }
【轉】Windows 郵件槽(MailSlot)