进程间通信之邮槽

简介

有时候我们需要一个程序与另一个程序之间进行通信。你可能在多台机器上有多个服务器运行,而在一个中央位置对一个或多个服务器进行远程监控。 Windows 平台为我们提供了一系列通信方式,从 Socket 到命名管道, DDE,DCOM ,邮槽等。本文中作者将探讨邮槽这一通信机制,假定读者熟悉 CreateFile(),ReadFile(),WriteFile() API 函数,此外还假定读者熟悉重叠 I/O 的基础知识。

邮槽

实现一个 多写者 / 单读者 协议。一个进程通过指定一个名称创建一个邮槽,然后等待有消息写入到邮槽中。若其他进程知道邮槽的名称,则可以打开邮槽,往邮槽中写入消息。只能有一个邮槽读者,但可以有多个写者。微软使用服务器 / 客户来描述。服务器创建邮槽并从中读取消息。客户连接到一个已经存在的邮槽,并往里面写入消息。

邮槽有一个有趣而有用的属性。一个进程往邮槽中写入一个消息,读者就可以接收到消息。消息是一整块任意长度的数据,若写者写入 60 字节,读者读到 60 字节,不多不少。若写者写 327 字节,读者读到 327 字节。这是一个面向消息的协议,而不是面向字节的协议。这类似于命名管道上的消息模式。这并不是说你不能只读取消息的一部分,只不过使用邮槽的 自然 方式是面向消息的,这在对读者可用的 API 里反映出来。

邮槽可以跨网络使用。若在同一台机器上,则你可以给邮槽命名为 ""."mailslot"slotname 。若跨网络使用,则你可以将 ”.” 替换为创建邮槽的机器的名称。

创建邮槽

这通过 CreateMailslot() 函数完成,第一个参数指明了邮槽名称。其他参数依次是能写入邮槽中的消息的最大大小,邮槽读者等待消息的时间,一个指明句柄是否被子进程继承的安全描述符。

连接邮槽

你可以使用 CreateFile() 函数,在其中指定邮槽名称来实现。若你希望实现多写者 / 单读者模式,你必须在打开邮槽时小心共享模式。若邮槽写者打开邮槽时没有指定 FILE_SHARE_WRITE 为共享模式,那么它将阻止其他任何写者往邮槽里写入消息。

邮槽的句柄,何以处之?

若你通过 CreateMailslot() 函数创建邮槽,你可以使用 ReadFile() 从中读取消息。邮槽句柄在重叠 I/O 模式中创建,因此你可以在它上面使用重叠 I/O ,当然若合适的话,你也可以使用非重叠 I/O 模式。你可以调用 GetMailslotInfo() 函数来查询有等待被读取的消息的个数,下一个消息的长度,读取消息的超时时限。你可以调用 SetMailslotInfo() 函数来改变超时时限。注意你传递给这两个函数的句柄必须是通过 CreateMailslot() 创建的。

若你没有创建邮槽,然后你使用 CreateFile() 函数连接到邮槽上。这种情况下你可以使用 WriteFile() 函数往邮槽里写入消息。你能否使用重叠 I/O 模式取决于你如何调用 CreateFile() 函数的方式。它可以是同步的,也可以是异步的。你无法使用 CreateFile() 函数连接到一个邮槽上并且从邮槽上读取消息。

MSDN 关于的邮槽的文档说只要邮槽上有任何一个打开句柄,邮槽就会存在。但作者说这不一定正确(在 win xp sp2 上)。 你可以有任何数量的打开的邮槽写句柄,但只要读者句柄一关闭,邮槽就消失了(一旦读句柄关闭,往邮槽中写入消息就会失败)。这是有意义的。因为你只有一个 读者,它一旦走了,那么任何写入的消息都只会被系统无意义地缓存。若没有读者了,那么缓存的消息就会永远悬停了(记住你无法使用 CreateFile() 来打开邮槽的读句柄)。

可以连接到特定域的特定名字的所有邮槽。这通过指定邮槽名称为 “""domainname"mailslot"name“ 。也可以使用 ”*” 作为首要域。这看起来不错,你可以在一个域内运行的多台机器上的任何数量的读者,并且指定域名同时往邮槽中写入消息。但有个问题,若你使用域名作为邮槽的写者,你没法写入大于 424 字节的消息。

BOOL Makeslot() 
// 创建邮槽
    CString lpszSlotName  =  _T( " .//mailslot//sample_mailslot " ); 
    
//  The mailslot handle "hSlot1" is declared globally. 
    hSlot1  =  CreateMailslot(lpszSlotName.GetBuffer( 10 ), 
        
0 ,                              //  no maximum message size 
        MAILSLOT_WAIT_FOREVER,          //  no time-out for operations 
        (LPSECURITY_ATTRIBUTES) NULL);  //  no security attributes 
     if  (hSlot1  ==  INVALID_HANDLE_VALUE) 
    

        
return  FALSE; 
    }
 
     
return  TRUE; 
}
 

 

BOOL Readslot() 
{ // 读邮槽
    DWORD cbMessage, cMessage, cbRead; 
    BOOL fResult; 
    LPWSTR lpszBuffer; 
    TCHAR achID[
80 ]; 
    DWORD cAllMessages; 
    HANDLE hEvent;
    OVERLAPPED ov;
    cbMessage 
=  cMessage  =  cbRead  =   0 ;
    hEvent 
=  CreateEvent(NULL, FALSE, FALSE, _T( " ExampleSlot " ));
    ov.Offset 
=   0 ;
    ov.OffsetHigh 
=   0 ;
    ov.hEvent 
=  hEvent;
    
//  Mailslot handle "hSlot1" is declared globally. 
    fResult  =  GetMailslotInfo(hSlot1,  //  mailslot handle 
        (LPDWORD) NULL,                //  no maximum message size 
         & cbMessage,                    //  size of next message 
         & cMessage,                     //  number of messages 
        (LPDWORD) NULL);               //  no read time-out 
     if  ( ! fResult) 
    

        
// ErrorHandler(hwnd, "GetMailslotInfo"); 
         return  FALSE; 
    }
 
    
if  (cbMessage  ==  MAILSLOT_NO_MESSAGE) 
    

        
// TextOut(hdc, 10, 10, "No waiting messages.", 20); 
         return  TRUE; 
    }
 
    cAllMessages 
=  cMessage; 
    
while  (cMessage  !=   0 )   //  retrieve all messages
    
        
//  Create a message-number string. 
        wsprintf((LPWSTR) achID, 
            _T(
" /nMessage #%d of %d/n " ), cAllMessages  -  cMessage  +   1
            cAllMessages); 
        
//  Allocate memory for the message. 
        lpszBuffer  =  (LPWSTR) GlobalAlloc(GPTR, 
            lstrlen((LPWSTR) achID) 
+  cbMessage); 
        lpszBuffer[
0 =   ' /0 '
        fResult 
=  ReadFile(hSlot1, 
            lpszBuffer, 
            cbMessage, 
            
& cbRead, 
            
& ov); 
        
if  ( ! fResult) 
        

            
// ErrorHandler(hwnd, "ReadFile"); 
            GlobalFree((HGLOBAL) lpszBuffer); 
            
return  FALSE; 
        }
 
        
//  Concatenate the message and the message-number string. 
        lstrcat(lpszBuffer, (LPWSTR) achID); 
        GlobalFree((HGLOBAL) lpszBuffer); 
 
        fResult 
=  GetMailslotInfo(hSlot1,  //  mailslot handle 
            (LPDWORD) NULL,                //  no maximum message size 
             & cbMessage,                    //  size of next message 
             & cMessage,                     //  number of messages 
            (LPDWORD) NULL);               //  no read time-out 
 
        
if  ( ! fResult) 
        

            
return  FALSE; 
        }
 
    }
 
    
return  TRUE; 
}
 

 

BOOL Writeslot()
{ // 写邮槽
    CString lpszMessage  =  _T( " Message for sample_mailslot in primary domain. " ); 
    BOOL fResult; 
    HANDLE hFile; 
    DWORD cbWritten; 
     
    hFile 
=  CreateFile(_T( " .//mailslot//sample_mailslot " ), 
        GENERIC_WRITE, 
        FILE_SHARE_READ,  
//  required to write to a mailslot 
        (LPSECURITY_ATTRIBUTES) NULL, 
        OPEN_EXISTING, 
        FILE_ATTRIBUTE_NORMAL, 
        (HANDLE) NULL); 
     
    
if  (hFile  ==  INVALID_HANDLE_VALUE) 
    

        
return  FALSE; 
    }
 
     
    fResult 
=  WriteFile(hFile, 
        lpszMessage.GetBuffer(
10 ), 
        (DWORD) lstrlen(lpszMessage) 
+   1 ,   //  include terminating null 
         & cbWritten, 
        (LPOVERLAPPED) NULL); 
     
    
if  ( ! fResult) 
    

        
return  FALSE; 
    }
 
    fResult 
=  CloseHandle(hFile); 
     
    
if  ( ! fResult) 
    

        
return  FALSE; 
    }
 
    
return  TRUE; 

}

作者:洞庭散人

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值