*Windows環境下,實現進程間的通信方式消息、命名管道(Name Pipes)、剪貼板(ClipBoard)等,郵槽(MailSlot)也是其中一種。
1.郵槽
- 郵槽(Mailslot)也稱為郵件槽,它是 Windows 提供的一種用來實現進程間通信的手段,是單向數據傳輸的服務。
- 郵槽的弊端:郵槽是單向通信。服務器端只能讀取,客戶端只能寫入。如果要雙向通信就需要服務端寫一個郵槽,客戶端在寫一個郵槽。創建兩個郵槽。
- 服務端和客戶端的區分:創建郵槽的為服務端,已存在的郵槽的哪一端。
- 客戶端在使用郵槽發送數據的時候只有當數據的長度<425字節的時候,才能被廣播給多個服務器,如果消息長度>425字節,郵槽不支持廣播通信。
-
郵槽最大的缺點是只允許從客戶機到服務器,建立一種不可靠的單向數據通信。而郵槽最大的一個優點在於,它們使客戶機應用能夠非常容易地將廣播消息發送給一個或多個服務器應用。
- 將郵槽理解為文件系統,那么其實就是創建文件、寫入文件和讀取文件的關系。兩台計算機使用郵槽通信,其實就是在服務端上創建一個文件供寫入,當其他計算機(或進程)需要給它發送消息時,就是打開這個文件,將內容寫入。當目的計算機進程發現文件內容非空,就讀取文件進行分析顯示。
2.實現流程
- 調用CreateMailslot函數,將創建郵槽的請求傳遞給內核的系統服務。
- 請求傳遞給NtcreatMailslot函數,這個函數會到達底層的郵槽驅動程序。
- 底層驅動程序msfs.sys,然后一些創建郵槽的工作就交給郵槽驅動程序來完成了.
3.油槽的文件名
- 郵槽的文件名。對於郵槽文件,格式為\\ComputerName\mailslot\[path]name
- 對於本地郵槽文件,ComputerName使用“.”來代替,就是“\\.\MailSlot\路徑\文件名”
- 對於寫入的目標計算機的郵槽文件,就是\\目標計算機\MailSlot\路徑\文件名。
- 郵槽支持群發,如果向默認工作組/域發送(就是當前發送計算機所在工作組/域),可以使用\\*\MailSlot\路徑\文件名。如果向指
- 定工作組/域發送,可以使用\\工作組/域名\MailSlot\路徑\文件名
4.用到的函數
Windows API函數 Visual Basic 語法格式聲明
- CreateMailslot()
| Declare Function CreateMailslot Lib "kernel32" Alias "CreateMailslotA" (ByVal lpName As String, ByVal nMaxMessageSize As Long, ByVal lReadTimeout As Long, lpSecurityAttributes As SECURITY_ATTRIBUTES) As Long | |
| Windows API函數功能說明 | |
| 創建一個郵路。返回的句柄由郵路服務器使用(收件人) | |
| Windows API函數出口返回值 | |
| Long,如執行成功,返回郵路的句柄;INVALID_HANDLE_VALUE表示失敗。會設置GetLastError | |
| Windows API函數入口參數表 | |
| Windows API函數入口參數 | 類型及說明 |
| lpName | String,指定郵路的名字,采用的形式如下:\\.\郵路\[路徑\]郵路名 |
| nMaxMessageSize | Long,指定一個郵路消息的最大長度。零表示無限長。請注意,對於穿越一個網絡域到多個郵路的廣播消息,最大長度是400 |
| lReadTimeout | Long,等待指定的數據時,用這個參數指定郵路使用的默認超時設置,以毫秒為單位。零表示不等待。常數MAILSLOT_WAIT_FOREVER表示一直等到數據到達 |
| lpSecurityAttributes | SECURITY_ATTRIBUTES,指定一個結構,或傳遞零值(將參數聲明為ByVal As Long,並傳遞零值),表示使用不允許繼承的默認描述符 |
- GetMailslotInfo()
| Declare Function GetMailslotInfo Lib "kernel32" Alias "GetMailslotInfo" (ByVal hMailslot As Long, lpMaxMessageSize As Long, lpNextSize As Long, lpMessageCount As Long, lpReadTimeout As Long) As Long | |
| Windows API函數功能說明 | |
| 獲取與一個郵路有關的信息 | |
| Windows API函數出口返回值 | |
| Long,非零表示成功,零表示失敗。會設置GetLastError | |
| Windows API函數入口參數表 | |
| Windows API函數入口參數 | 類型及說明 |
| hMailslot | Long,指定一個郵路的句柄 |
| lpMaxMessageSize | Long,指定一個長整數變量,用於裝載這個郵路的最大消息長度 |
| lpNextSize | Long,指定一個長整數變量,用於裝載下一條消息的長度。如沒有消息准備好,則可設為常數MAILSLOT_NO_MESSAGE |
| lpMessageCount | Long,指定一個長整數變量,用於裝載郵路中准備好的消息數量 |
| lpReadTimeout | Long,指定一個長整數變量,用於裝載郵路的默認閱讀超時 |
- readfile()
| Declare Function ReadFile Lib "kernel32" Alias "ReadFile" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, lpOverlapped As OVERLAPPED) As Long | |
| Windows API函數功能說明 | |
| 從文件中讀出數據。與lread函數相比,這個函數要明顯靈活的多。該函數能夠操作通信設備、管道、套接字以及郵槽 | |
| Windows API函數出口返回值 | |
| Long,非零表示成功,零表示失敗。會設置GetLastError。如啟動的是一次異步讀操作,則函數會返回零值,並將ERROR_IO_PENDING設置成GetLastError的結果。如結果不是零值,但讀入的字節數小於nNumberOfBytesToRead參數指定的值,表明早已抵達了文件的結尾 | |
| Windows API函數入口參數表 | |
| Windows API函數入口參數 | 類型及說明 |
| hFile | Long,文件的句柄 |
| lpBuffer | Any,用於保存讀入數據的一個緩沖區 |
| nNumberOfBytesToRead | Long,要讀入的字符數 |
| lpNumberOfBytesRead | Long,從文件中實際讀入的字符數 |
| lpOverlapped | OVERLAPPED,如文件打開時指定了FILE_FLAG_OVERLAPPED,那么必須用這個參數引用一個特殊的結構。那個結構定義了一次異步讀取操作。否則,應將這個參數設為NULL(將函數聲明成ByVal As Long,並傳遞零值) |
| 注解 | |
| 並非每種操作系統都支持對每種設備進行異步操作。Windows 95不支持對一個磁盤文件進行異步讀操作(重復讀) | |
- WriteFile()
| Declare Function WriteFile Lib "kernel32" Alias "WriteFile" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, lpOverlapped As OVERLAPPED) As Long | |
| Windows API函數功能說明 | |
| 將數據寫入一個文件。該函數比lwrite函數要靈活的多。也可將這個函數應用於對通信設備、管道、套接字以及郵槽的處理 | |
| Windows API函數出口返回值 | |
| Long,TRUE(非零)表示成功,否則返回零。會設置GetLastError | |
| Windows API函數入口參數表 | |
| Windows API函數入口參數 | 類型及說明 |
| hFile | Long,一個文件的句柄 |
| lpBuffer | Any,要寫入的一個數據緩沖區 |
| nNumberOfBytesToWrite | Long,要寫入數據的字節數量。如寫入零字節,表示什么都不寫入,但會更新文件的“上一次修改時間”。針對位於遠程系統的命名管道,限制在65535個字節以內 |
| lpNumberOfBytesWritten | Long,實際寫入文件的字節數量 |
| lpOverlapped | OVERLAPPED,倘若在指定FILE_FLAG_OVERLAPPED的前提下打開文件,這個參數就必須引用一個特殊的結構。那個結構定義了一次異步寫操作。否則,該參數應置為空(將聲明變為ByVal As Long,並傳遞零值) |
| 注解 | |
| 並不是每種操作系統都支持在任何類型的設備上進行異步操作。windows 95不支持對磁盤文件的重疊讀取操作 | |
例子:
(1)服務端程序
#include<stdio.h> #include<stdlib.h> #include<Windows.h> #define MAILSLOT "\\\\.\\mailslot\\sjx" //給郵槽命名為sjx void main() { //名稱,數量 等待時間 安全屬性 HANDLE hmailslot = CreateMailslotA(MAILSLOT, 0, MAILSLOT_WAIT_FOREVER, NULL); if (hmailslot == NULL) { printf("創建失敗"); } else { while (1) { system("pause"); DWORD dxNextsize = 0;//標識下一個的大小 DWORD dxmsgcount = 0;//消息數量 DWORD readcount = 0;//讀取成功的數量 //文件名 標記下一個字符串長度多少, 多少條消息 if (GetMailslotInfo(hmailslot, NULL, &dxNextsize, &dxmsgcount, NULL)) { for (int i = 0; i < dxmsgcount;i++) { // LPBYYE unsigned char * LPBYTE lpbuf = malloc(dxNextsize + 1);//分配內存 //讀取 //首地址, 大小 //讀的個數 ReadFile(hmailslot, lpbuf, dxNextsize, &readcount, NULL); printf("\n%s", lpbuf); } } else { printf("error"); } } } system("pause"); }
(2)客戶端程序
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<Windows.h> #define MAILSLOT "\\\\.\\mailslot\\sjx" //#define GENERIC_READ (0x80000000L) //#define GENERIC_WRITE (0x40000000L) //#define GENERIC_EXECUTE (0x20000000L) //#define GENERIC_ALL (0x10000000L) void main() { //第一個名稱,第二個寫入,第三個共享讀,打開已經存在 HANDLE hmailslot = CreateFileA(MAILSLOT, \ GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL); if (hmailslot ==INVALID_HANDLE_VALUE) { printf("打開失敗"); } while (1) { //system("pause"); char str[10] = "123456789"; scanf("%s", str); LPBYTE lpmsg = (LPBYTE)str;//轉化了指針類型 int wok = 0; //寫入信息,第一個句柄,第二個寫入內存首地址,第三個長度,第四個寫入成功保存wok WriteFile(hmailslot, lpmsg,10,&wok,NULL); } CloseHandle(hmailslot);//關閉 system("pause"); }
