由于自己在写进程间通信的相关程序,查阅了关于资料。觉得命名管道方法实现通信是不错的选择,可是对于其使用方式却有诸多的不解。
同步方式、异步方式、重叠方式………..
有很多的不解。
首先是关于
WaitNamedPipe函数的使用
这个函数的使用感觉有些奇怪,有的地方在用,有的人在写通信程序的使用又没有用。真是其功能甚是不解。
简单的说这个函数就是用来看看有没有可用的管道,若没有管道存在不管等待时间到没有都直接返回错误。若有管道存在且可用,也是直接返回。
不过返回的是正确。若没有空闲的管道(注意是没有空闲,而不是说不存在)则等待规定的时间。一般我们会给指定两种值:
NMPWAIT_USE_DEFAULT_WAIT(等待默认的等待时间,这个值时的等待时间 是在使用创建管道的函数CreateNamedPipe时的倒数第二个参数中指定)
NMPWAIT_WAIT_FOREVER(永远等待,要么是成功等到了有可用的管道。要么是没有管道存在返回错误)
1: HANDLE CreateNamedPipe( LPCTSTR lpName, // pointer to pipe name
2: DWORD dwOpenMode, // pipe open mode
3: DWORD dwPipeMode, // pipe-specific modes
4: DWORD nMaxInstances, // maximum number of instances
5: DWORD nOutBufferSize, // output buffer size, in bytes
6: DWORD nInBufferSize, // input buffer size, in bytes
7: DWORD nDefaultTimeOut, // time-out time, in milliseconds
8: LPSECURITY_ATTRIBUTES lpSecurityAttributes // pointer to security attributes
9: );
另外还要说明的这一个函数不是真正的用于连接。不是说调用它后若是成功返回则表明连接建立。其实不然,从字面意思理解与一般的wait函数相比较。
则知道它的作用只是等待某个事情的发生(不用事件,用‘事情’更形象)。这里等待的是有可用管道实例这一个事实发生。
所以说它返回不代表连接已经建立。相反,还要在它正确返回后用CreateFile和CallNamePipe两个函数之一来建立一个与命名管道服务器端的连接。
MSDN中有说在createfile后正式与管道建立连接。但是在WaitForNamedPipe返回正确时,CreateFile也可能出现错误。不能建立连接。因为在此期间
有可能管道关闭不能再使用。也有可能其它的客户端把可用管道实例给占用完了不能再建立连接。
上面说的是这个函数的作用。也说了一般用法:先用WaitForNamedPipe来等待一个可用的服务器端管道。然后再用CreateFile或者是CallNamedPipe来
建立与管道的连接。同时也要处理可能出现的问题与错误。但是msdn中还用了另外一种这些函数的调用时序。
正如上我所说的一样,在用CreateFile来连接一个可用管道时,调用这个WaitForNamedPipe不是必须的。可以直接用CreateFile来连接管道。而不必管
WaitForNamedPipe这个函数到底是做什么的。这个时候我们可以看下面的一段msdn的说明
A named pipe client uses the CreateFile function to open a handle to a named pipe. If the pipe exists but all of its instances are busy,
CreateFile returns INVALID_HANDLE_VALUE and the GetLastError function returns ERROR_PIPE_BUSY. When this happens, the named pipe
client uses the WaitNamedPipe function to wait for an instance of the named pipe to become available.
The CreateFile function fails if the access specified is incompatible with the access specified (duplex, outbound, or inbound) when the server
created the pipe. For a duplex pipe, the client can specify read, write, or read/write access; for an outbound pipe (write-only server), the
client must specify read-only access; and for an inbound pipe (read-only server), the client must specify write-only access.
说明:CreateFile可以用以建立连接,若有管道且空闲,返回成功。若有管道且所有可用实例都被打开则会返回失败。错误代码为ERROR_PIPE_BUSY。
此时可以用WaitForNamedPipe来等待管道空闲下来。若CreateFile失败,且返回的错误代码为:ERROR_FILE_NOT_FOUND(0x02)说明没有管道存在
因此使用时的顺序应该为先用CreateFile来尝试连接 一下管道 ,看是否成功,若不成功错误错码是什么。若是busy则此时可以调用WaitForNamedPipe来等待其
可用 。若真的不存在则可以直接错误返回了。
ConnectNamedPipe函数的使用
这个函数的使用大家可能也有很多的疑问。如孙鑫的书中有提到说这一个函数应该以Waitxx开始更符合其功能。它就是等待一个客户端的连接,若是阻塞模式则
会等待到真正的在另外一端(客户端)用CreateFile或者是CallNamePipe来创建连接才返回。不过通过实验发现,这个函数管道并不是要用这一个函数来等待,客户端
才能与之进行连接。可以试验一下创建一个字节模式的管道后,不调用它,先把主线程暂停一下。在客户端直接用CreateFile来连接它,发现可以直接连接它,且可以往里面
写数据。此时再用ConnectNamedPipe函数时会发现错误,若仔细查阅其错误代码会发现为ERROR_PIPE_CONNECTED。说明为管道的另一端有进程,也就是说连接已经
建立起来了。所以这个函数的使用也比较的诡异。这个也是要注意的地方
一般用法为CreateNamedPipe来创建管道后调用ConnectNamedPipe来等待别人的连接。(而别人是用CreateFile来建立连接的)
管道的字节模式与消息模式
这个是我最想说的,也是我写这篇文章的目的所在。因为不明白在PIPE_TYPE_BYTE与PIPE_TYPE_MESSAGE间到底有什么区别。
关于怎么样使用管道的中文资料还真是蛮多的,可是你会发现那些东西都是千篇一律的。感觉中国的东西就是天下东西一大抄了。MSDN对消息模式的解释很 少,以至于大家直接翻译msdn的英文意思,也不说个所以然出来,当然可能大家都不知道所以然啦,另外也可能是他们真的用不到,或者是他们的不求甚解。
后来自己探索发现:
两都间没有太大的区别,只是在字节模式的时候是以字节流接收与发送的。每次可以发送不同的字节数,在没有把发送的数据收走(用ReadFile)前也可以再次发送,只要没有超过管道的默认的缓冲区大小 。其实也可以说是我们用的是PIPE_WAIT,所以会是在阻塞模式,也就是说会让数据直接发送到了服务器端的缓冲区中,虽然sever端没有调用ReadFile来读取数据,其实已经发到那一边去了,它不读出来没有关系,我们客户端还是可以继续发送数据的。而且对每次发送的数据大小没有限制,不会说每次都得发送固定大小的数据 。读的那一端也会把接收到的数据作为一个无机的整体,也就是说是没有结构的二进制流。读的时候可以想读出任意的字节数都可以的。如一次发送了5个字节,然后再发送了40个字节 ,服务器端可以一次性全部读出45个字节 也可以每次读出一个字节 。以及小于45的任意字节。
而对于消息模式的话,对于写的大小与次数也没有限制。只是说若是以消息模式写的,则会发给服务器的时候说,这是一个消息,若你用消息模式读的话不能只读其中的一部分。你得全部都读走,若有一个消息发到了服务器端,10个字节。现在你读了8字节(用ReadFile函数)会返回错误,错误代码为ERRORM_More_data.也就是说即是没有超过缓冲区中已有的数据,你没有读够也会不让你读成功。
注意:以消息模式发送的数据,在服务器端 是可以用字节模式接收的,如上所说。若是这样的话也会把有格式的消息化为没有格式的字节流。
但是,不能以消息模式来读取以字节模式发送的消息。
ps:以后大家找不到中文资料的时候不要忘记了英文的网站,记得用Google。
下面一个参考的网页地址: