郵槽

郵槽 （MailSlot）是Windows作業系統提供的一種單向進程間通信機制，可用於單機或者網路上的多機分布式環境。

對於相對簡短的低頻率資訊發送，使用郵槽通常比命名管道或Unix域套接字更簡單。如低頻率的狀態改變消息、作為對等點發現協議（peer-discovery protocol）的一部分。郵槽機制允許短報文廣播給域上的電腦中所有監聽的進程。

## 特性

郵槽是一種伺服器-客戶接口。伺服器創建郵槽，客戶可以向命名的郵槽寫入內容。只有伺服器可以讀取郵槽，因此郵槽是一種單向進程間通信機制。郵槽不提供報文已收到的確認，因此是不可靠通信。

郵槽基於RPC協議，可以在同一個網路域上跨電腦使用。

## 應用

Windows信使服務是郵槽的最知名的應用。信使服務是一個郵槽伺服器，等待報文到達後，就彈窗顯示在螢幕上。

郵槽的應用舉例:

- MAILSLOT\Messngr - 微軟Windows信使服務協議
- MAILSLOT\Browse - 微軟網路鄰居共享資源瀏覽服務
- MAILSLOT\Alerter
- MAILSLOT\53cb31a0\UnimodemNotifyTSP
- MAILSLOT\HydraLsServer - Microsoft Terminal Services Licensing
- MAILSLOT\CheyenneDS - CA BrightStor Discovery Service

## 實現

### 郵槽命名

郵槽的名字，從形式上看類似於文件名。格式為`\\ComputerName\mailslot\[path]name` 本地郵槽名字中的ComputerName使用`.`來代替，即`\\.\MailSlot\路径\文件名`。如果向當前發送電腦所在工作組或域群發，可以使用`*`作為ComputerName。如果向指定工作組或域群發，應使用工作組或域名作為ComputerName

本機的Windows的信使服務使用的郵槽名字是`\\.\MailSlot\ messngr`

### 郵槽報文內容

郵槽報文內容包含：接收日期、發送人、接收人、具體內容。發送人、接收人、具體內容三項內容之間使用字節值`0`間隔。

### 創建郵槽

使用`CreateMailslot`

### 寫入報文到郵槽

類似於寫入普通文件，使用`CreateFile`打開郵槽，使用`WriteFile`將內容寫入。

也可以使用`NetMessageBufferSend`直接發送。

### 讀取郵槽報文

使用`GetMailSlotInfo`來判斷郵槽內是否有內容。發現有內容的時候，可以使用`ReadFile`讀取。如果`ReadFile`

在使用MAILSLOT_WAIT_FOREVER標誌的郵槽上等待消息到來，這時郵槽突然中止運行，那麼這個應用會被永遠「掛起」直至重啟Windows系統。為此，讀郵槽的進程可以使用一個單獨線程執行讀掛起操作；主線程要結束進程時給一個全局標誌位置位，並給郵槽寫入一條消息以喚醒讀郵槽線程。

`SetMailslotInfo`設置讀取郵槽的超時值

## 例子

```cpp
//邮槽服务器，负责创建邮槽，读取邮槽
#include <windows.h>
#include <stdio.h>
int main()
{
 HANDLE Mailslot;
 char buffer[256];
 DWORD NumberOfBytesRead;
 //Create the mailslot
 Mailslot = CreateMailslot("////.//Mailslot//Myslot",  //指定邮槽的名字
  0,                  //可写入邮槽的消息的最大字节长度；为0表示接收任意长度消息
  MAILSLOT_WAIT_FOREVER, //等待或不等待，单位毫秒，MAILSLOT_WAIT_FOREVER无限期等待，0立即返回 
  NULL);               //访问控制权限，一般为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;
}
```

```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,      //必须为GENERIC_WRITE,因为客户端只能向邮槽写入数据
        FILE_SHARE_READ,   /必须为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;
}
```

## 外部連結

- Mailslots (MSDN Documentation)
- Using Mailslots for Interprocess Communication
- Using a Mailslot to read/write data over a network