有关MailSlot. msdn windows base services->interprocess communications

来源:互联网 发布:ios锁屏软件 编辑:程序博客网 时间:2024/04/29 12:04

接下来要说的MailSlot会让你更有广播的感觉,而且它是可以跨越机器边界向网路广播的。从字面上看来,这像是与寄信有关的通讯机制,实际上它的行为也的确与其名称相符合。MailSlot就像是你的信箱,只要知道地址,任何人都可以寄信给你,不过,只有你才可以打开信箱读信。

MailSlot是一种由系统维护的虚拟档案,建立并拥有Mailslot的行程扮演Server.的角色,其他的行程包含MailSlot Server本身的行程均可以开启MailSlot写入讯息,不过,只有MailSlot Server可以读取资料的内容。这是个单一Server多个Client的机制,同时,资料只允许由Client对Server单向传送。

我想你可能也习惯了,要产生一个MailSlot物件大概也需要一个识别名称吧! :p 说不定连CreateMailSlot()函数名称都猜得一字不差。不过,这次的名称可不像先前那样可以随便高兴取什麽就取什麽的,它具有以下的固定格式:

file://ServerName/mailslot/[path]name

我第一次看到时心想: 天哪! 这该怎麽填呀? 边举例边说明会比较容易懂

//./mailslot/MyMailSlotName MailSlot的识别名称一定从「//」双倒斜线开始。接下来的是机器的名称或组群网域的名称,这 的「.」句号代表的是行程所在的那部机器。再来是「/mailslot」,对於MailSlot,一定是这个单字照抄就是了。最後则是你自订的MailSlot名字。先前提到MailSlot实际上是特殊的虚拟档案,所以,要当它是档名应该也是说得通的。

的确,援引我们对於档案系统的概念,MailSlot的识别名称就像路径档名一样,可以经过适当的阶层加分类管理,例如: //./mailslot/Account/Note。最後再看一个例子: //*/mailslot/MyMailSlotName,其中「*」指的是群组内的所有机器。
 procedure TMailSlotServer.Open;
var
    ASlotName: AnsiString;
begin
    if FActive then Exit;
    // 构成 Mailslot 识别名称
    ASlotName := '//' + FServerName + '/mailslot/' + FSlotName;
    FHandle := CreateMailslot(
        pchar(ASlotName), // MailSlot 识别名称
        0, // 讯息长度的最大值,设为零表示不限
        MAILSLOT_WAIT_FOREVER, // read time-out
        nil); // 安全属性,先暂时采用预设值
    if FHandle = INVALID_HANDLE_VALUE then
        FActive := False
    else
    begin
        FActive := True;
        FWaitThread.Resume;
    end;
end;

//delphi格式

再强调一次,只有MailSlolt Server才可以读取资料,读取的方法是先以GetMailslotInfo()侦测讯息的长度与数量,然後以回圈逐一配置记忆体并以ReadFile()读出资料(别忘了MailSlot也是档案),以下是
procedure TMailSlotServer.ReadFromMailSlot;
var
    NextSize: DWORD;
    MessageCount: DWORD;
    Result: BOOL;
    Buffer: pchar;
begin
    if FHandle = INVALID_HANDLE_VALUE then Exit;
    // 侦测 MailSlot 中是否有资料
    Result := GetMailslotInfo(Fhandle, nil,
        NextSize, @MessageCount, nil);
    if not Result or (NextSize = MAILSLOT_NO_MESSAGE) then
        Exit;
    // 如果还有资料 (MessageCount <> 0),逐一读出资料
    while Result and (MessageCount <> 0) do
    begin
        // 资料的长度
        Buffer := AllocMem(NextSize + 1);
        try
            // 读出资料
            FileRead(Fhandle, Buffer^, NextSize);
            if Assigned(FOnDataAvailable) then
            FOnDataAvailable(Self, StrPas(Buffer));
        finally
            FreeMem(Buffer, NextSize + 1);
        end;
        // 继续看看 MailSlot 中还有没有资料
        Result := GetMailslotInfo(Fhandle, nil,
        NextSize, @MessageCount, nil);
    end;
end;

至於MailSlot的Client程式则没有什麽好说的,就当是档案迳行开启与写入即可:

procedure TMailSlotClient.Open;
var
    ASlotName: string;
begin
    if FActive then Exit;
    // MailSlot 的识别名称
    ASlotName := '//' + FServerName + '/mailslot/' + FSlotName;
    // 开启 MailSlot(档案)
    FHandle := CreateFile(pchar(ASlotName),
        GENERIC_WRITE, // Client 端对於 MailSlot 只能写入
        FILE_SHARE_READ, // 设定为可供分享读取
        Nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    FActive := FHandle <> INVALID_HANDLE_VALUE;
end;

function TMailSlotClient.WriteIntoMailSlot(
    const Data: string): integer;
begin
    Result := 0;
    if FHandle = INVALID_HANDLE_VALUE then Exit;
    Result := FileWrite(Fhandle, Data[1], Length(Data));
end;

稍早提到MailSlot适合於跨越机器边界的网路广播, 可是我也说明了只有MailSlot Server才可以读取资料,那要怎麽广播啊?答案在於MailSlot的名称。别的机器如果也用相同的名称建立MailSlot Server,一旦任一个Client对某一个MailSlot(也是经由名称来叁考)送出讯息,这份讯息会游向网路节点上各个指定同名的MailSlot,这样子就达成广播的效果。至於讯息是怎麽流来流去的,就留给系统与网路底层去伤脑筋了,程式只管以档案写入资料的方式送出资料即可。

使用MailSlot时很可能你会遇到讯息重覆的问题;也就是说,虽然MailSlot Client端只写了一个讯息,但相同的讯息MailSlot Server却可能收到两份。原因是这样的:由於Win32多重通讯协定的缘故,MailSlot在广播时,并不知道到底该采用哪一条路径,於是便各种可能的通路都传了一份。情况有点像在发布台风警报,我们在电视,广播与网路都同时会晓得有台风要来的消息。解决的方法是在资料开头处加上一些控制用的编号代码,Server据以判断是否是相同的资料。

像MailSlot这样的通讯机制可以应用在哪些场合呢? 着名的例子是WinPopup

//

1,server process create mailslots, and can only read

2, client process can only write to the slot queue

原创粉丝点击