的资料,此时我们知道该是重新读取资料内容的时候了。
呼! 终於将Shared memory的范例程式DemoSMem讲完了,下图是它执行的画面,彼此看来是亳无关联,但是经由共同分享的记忆体与Mutex,Event两种同步协调技术,彼此正在密切交换意见。
图: DemoSMem执行情形
MailSlot 执行DemoSMem时如果让你有广播的感觉,接下来要说的MailSlot会让你更有广播的感觉,而且它是可以跨越机器边界向网路广播的。从字面上看来,这像是与寄信有关的通讯机制,实际上它的行为也的确与其名称相符合。MailSlot就像是你的信箱,只要知道地址,任何人都可以寄信给你,不过,只有你才可以打开信箱读信。
MailSlot是一种由系统维护的虚拟档案,建立并拥有Mailslot的行程扮演Server.的角色,其他的行程包含MailSlot Server本身的行程均可以开启MailSlot写入讯息,不过,只有MailSlot Server可以读取资料的内容。这是个单一Server多个Client的机制,同时,资料只允许由Client对Server单向传送。
我想你可能也习惯了,要产生一个MailSlot物件大概也需要一个识别名称吧! :p 说不定连CreateMailSlot()函数名称都猜得一字不差。不过,这次的名称可不像先前那样可以随便高兴取什麽就取什麽的,它具有以下的固定格式:
\\ServerName\mailslot\[path]name
我第一次看到时心想: 天哪! 这该怎麽填呀? 边举例边说明会比较容易懂
\\.\mailslot\MyMailSlotName MailSlot的识别名称一定从「\\」双倒斜线开始。接下来的是机器的名称或组群网域的名称,这 的「.」句号代表的是行程所在的那部机器。再来是「\mailslot」,对於MailSlot,一定是这个单字照抄就是了。最後则是你自订的MailSlot名字。先前提到MailSlot实际上是特殊的虚拟档案,所以,要当它是档名应该也是说得通的。
的确,援引我们对於档案系统的概念,MailSlot的识别名称就像路径档名一样,可以经过适当的阶层加分类管理,例如: \\.\mailslot\Account\Note。最後再看一个例子: \\*\mailslot\MyMailSlotName,其中「*」指的是群组内的所有机器。
说得够多了,让我们动手做做看吧! 首先是建立MailSlot Server的例子,取自本文所附的ChienIPC这个程式单元
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;
再强调一次,只有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,刚才我也写了一支阳春的, 次图是MyWinpop.exe 执行的情况。由於MailSlot广播的特性,十分适合网管时用来知会使用者重要的讯息,此外,MIS系统也可以用它适时的报告异常状况,各使用者如果在「开始┃启动」中都放置这支小程式,彼此便可以之交换讯息,当讯号进来时,也会立即显示讯息的内容。
图: MyWinPop.exe执行情形
当然,你还可以想得到其他的应用。像我就觉得它很适合用来作为程式除错工具,不仅可以将程式执行的过程与情况记录下来,而且程式在网路上各节点的执行状况也将源源而来,这是一般的测试方法所不容易达成的效果。
Pipe 看过广播式的 MailSlot後,Pipe则是点对点的通讯机制,资料允许单向或双向於管子连接的两端移动。pipe可分为Anonymous pipe与Named pipe 两种,Anonymous pipe的资料只能单向流动,而且仅限於单机内使用,但却是行程重导其标准输出(Standard Output)成为另一行程之标准输入的方法;Named pipe 就如同先前讨论的各项IPC机制,由於有一个识别名称,其他的行程很容易可以依照名称找过来,通讯范围不限於单机,同时,资料允许双向流通。
DDE 如同本文第一个TwinApp这个例子,DDE也是建立在讯息通讯这个基础上的,不过它的协定内容显然严谨很多。
DDE是由Client端以WM_DDE_INITIATE广播讯息起拉开通话的序幕,Server端受理後以WM_DDE_ACK回应,连通後则是一连串Server与Client间彼此互送WM_DDE_DATA、WM_DDE_REQUEST、WM_DDE_ACK等讯息。实际的资料并不是真的经由讯息传递,而是提供线索彼此利用Atoms(由Windows系统提供的字串对照表)寻求Application(应用程式), Topic(主题)与Data(资料)等三个项目。最後,以WM_DDE_TERMINATE讯息结束对话。
行程间建立DDE连接时,当Server端的资料改变时,依资料交换的频繁与Client的主动程度,其通道的形态可分为:
Cold Link:来要才有;Client端得主动要求传送资料,如果没有来要,即使Server的资料已经改变很多了,Server对Client也置之不理。 Hot Link:有变就给;当资料改变时,Server端将主动通知Client改变的内容。 Warm Link:更新通知;当资料改变时,Server端只对Client端告知资料改变的消息,真正的资料要等Client提出要求才会送出。由於DDE讯息通讯牵涉的实作细节颇多,为了使用方便起见,微软也提供DDE管理函式库(The DDE Management Library, 简称 DDEML), 使用上的最大差别在於使用DDEML的程式是用Callback函数处理DDE交易(Transaction) 。另外,三大项目的Application改口叫做(Service name)服务。
时至今日,讨论DDE的文献已不在少数,的确,DDE的使用应该是容易许多了,几乎没有一个Windows程式开发工具不提供一些元件或类别让程式员更方便制作DDE Server或Client程式。当然,如果你的需求只是在行程间通知某些消息,自行设计一套讯息通讯协定倒也简单得以完成任务,我想本文的第一个例子TwinApp是一个不错的提示。
其他的IPC技术 EXE通常呼叫DLL的输出函数(exports function),某些情况下DLL也会使用EXE 事先预备的回呼(Callback)函数。函数呼叫这个观念与想法如果移植到行程通讯中会发生什麽事呢? 我的意思是说,让一个行程呼叫另一个行程的函数。Ya! 这就是所谓的 RPC,行程之间属於函数呼叫层级的合作。可以想见的,由於行程各有其定址空间,如同OLE,要达到 RPC确实需要额外标准的介面加以定义。
有关IPC的技术与观念我们已经介绍得不少了,不论是讯息交换,剪贴簿,Shared memory,DDE,MailSlot,Pipe等等,几乎都是资料的交换或者Client与Server「要求-回应」,叁与通讯的行讯必须对於交换的资料有一定程度的了解与处理能力。换句话说,在我们以DDE向试算表软体要求传回资料後,这份资料到底代表什麽得自己解释;同样的,如果要传入资料到试算表软体,即使透过现成元件的帮忙,仍然必须对试算表软体有基本的认识。
话说回来了,只有试算表自己最清楚资料代表什麽,不是吗?那麽,由它来处理资料应该才是适当的人选,强以外部程式去操作总有外行人指导内行人的遗憾。利用OLE技术将应用程式整合在一起工作确实是比较合理的作法,如果COM物件可以像电子IC一样安插进我们的程式与我们的程式一同工作,那这种我们称之为OLE Control(ActiveX),距离拉大到网路上,DCOM这个名词你一定听说过.
想想看,终於我们可以用甲公司的统计图表元件,然後用乙公司的元件将图表传真出去,这样窗景真是美好。窗子确实只提供局部的风景,但是加装了望远镜的窗子可是一个天文台,加了风铃的窗子所提供的就不只是风景了,还有悦耳的声音。
不论是RPC或OLE,我想这都是属於本文应该讨论但肯定是来不及讨论的,这两个主题甚至以单篇文章来谈都不怎麽够用。事实上,有些地方(例如DDE这一节)我也没有提到技术方面的实作细节,碍於篇辐(这篇文章已经太长了) 日後我们会在本专栏继续以专文介绍RPC等主题。关於以Winsock作为IPC通讯机制这部分,本专栏的前一篇文章「走! 让我们上BBS聊天去」才刚说明过,在此就不再重覆了。
应用IPC到你的程式中 各项IPC的技术往往以各种方式组合在一起。例如本文提供的DemoSMem范例程式就同时用了ShareMem交换资料,同步机制则采用Mutex与Event。情况并不如想像中的复杂:既是行程通讯,那必然是两个以上行程之间的事,既是分开的,中间一定有介面存在,定义这个介面的具体内容就是所谓的协定,留意资料交换的位置与方式,需要协调避让的采用合适的同步控制加以处理。这些重点把握住了,应该心 就已经有数了。
面对各式各样的技术时,如果你正考虑应用IPC到你的程式中,首先得正视自己的需求,不妨提出类似以下的问题问问自己,最好将之写下来
是否真的需要跨行程处理,成效何在?技术实作的难易程度与所需付出的成本 资料的流向是单向或双向,需不需反馈(feedback)的控制查核这些工作只在单机完成,或者需要连上网路,范围只在公司内部区域网路或者是广域网路叁与通讯的行程最多与平均的数量是多少?只在一种作业环境,或者可能同时要满足不同的作业平台执行效能( performance)是不是关键需求.应用程式使用 GUI 介面或者 console mode 接下来开始比较各项IPC的特性,哪些是与你列出的需求相符合的,有没有哪些限制是你必须要排除而避免使用的,各项IPC经过与先前写下的需求交叉评比的结果,积分高的自然是脱颖而出。最後,事情如果能简单解决是最好,开发时程缩短成本自然降低,而且日後维护容易。
上一页 [1] [2] [操作系统]Fat32格式如何转换为NTFS格式 [办公软件]详细介绍打印纸张(A3,A4,16开,32开等)的名称由来及… [操作系统]网络转载---64位操作系统与32位的区别 [电脑技术]充分利用系统命令Regsvr32修复系统应用程序故障 [Delphi程序]給delphi 2005 for Win32 提速 [Delphi程序]Delphi Win32 API 使用的特殊情况 [VB.NET程序]使用 Visual Basic 通过 32 位 地址访问内存(中英… [VB.NET程序]巧用Win32 API函数增强VB位操作功能 [ORACLE]搭建oracle9iR2 for Windows 32 的OLAP开发环境(一…
|