阻塞和非阻塞打开命名读管道readfile 阻塞有什么区别吗

使用命名管道的OVERLAPPED方式实现非阻塞模式编程
命令管道是进程间通讯的一种常用方式,对于命令管道的介绍可以参考别的资料和书籍,这里推荐一个《VC++下命名管道编程的原理及实现》这篇博文,写得比较清楚。但是都是介绍了阻塞模式的编程,我这里主要是介绍利用命令管道OVERLAPPED方式使用非阻塞模式编程。注:文中使用的方法就是函数的意思。
参考MSDN,服务器端创建命令管道(使用CreateNamedPipe方法),不使用FILE_FLAG_OVERLAPPED模式时,当使用ConnectNamedPipe方法时,服务器端会进入阻塞。我们一般处理会创建一个工作线程,在工作线程中使用命令管道,但是会引入一个问题,当我们的程序退出时,这个工作线程没有办法结束,会阻塞在ConnectNamedPipe方法中。使用OVERLAPPED方式可以很好的解决这个问题。在codeproject上有一篇文章《One use for Overlapped I/O》写的比较好,提出了解决方法,大致的思路用下面的代码表示:
OVERLAPPED
handleArray[2];
bStop = FALSE;
memset(&op, 0, sizeof(op));
handleArray[0] = op.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
handleArray[1] = gbl_hStopE
while (bStop == FALSE)
h = CreateNamedPipe(FILE_FLAG_OVERLAPPED);
ConnectNamedPipe(h, &op);
switch (WaitForMultipleObjects(2, handleArray, FALSE, INFINITE))
case WAIT_OBJECT_0:
_beginthread(threadProc, 0, h);
ResetEvent(handleArray[0]);
case WAIT_OBJECT_0 + 1:
CloseHandle(h);
bStop = TRUE;
CloseHandle(handleArray[0]);
大致的思路是创建2个事件,一个用于退出线程,一个用于OVERLAPPED接受的event事件绑定,在CreateNamedPipe时,
使用FILE_FLAG_OVERLAPPED模式,这样在ConnetNamePipe时,就不会阻塞,会立即返回。然后使用
WaitForMultipleObjects方法等待这两个事件。这样就可以实现退出。
这篇文章提出的思路是对的,但是在解决实现上还需要细化。
首先,对于创建的2个事件,参考MSDN,与OVERLAPPED结构关联的event,要是manual-reset event,同时初始态要为signaled,
也就是要使用参数TRUE(为什么初始态要为signaled,这个后面解释)。另外一个是退出线程的事件。在使用
CreateNamedPipe(FILE_FLAG_OVERLAPPED)(伪码);
使用ConnectNamedPipe(h, &op)后,会立即返回,这个时候一般是返回FALSE,使用GetLastError()会得到
ERROR_IO_PENDING,表示这个请求是悬而未决的。我使用一个BOOL fPendingIO标识来记录所有悬而未决的的请求,
fPendingIO=TRUE。然后使用WaitForMultipleObjects方法等待这2个事件。线程现在就会阻塞在这里,
直到有相关的事件处于signaled态。现在来解释一下为什么开始创建事件时初始态为signaled。按照常理,
WaitForMultipleObjects不会被阻塞,因为其中一个事件的状态为signaled。其实不然,它的状态在
connectNamedPipe(h, &op)后已经改变了。对以OVERLAPPED关联的事件,
当使用OVERLAPPED相关的方法操作后,其状态会可能会改变的,主要基于下面3个原则:
1)当实际操作在函数返回前已经完成,事件的状态不会改变。
2)当函数返回是,实际的操作没有完成,也即是说这个操作是Pending的,这个时候事件会被设置为nonsignaled.
3) 当操作的Pending完成后,事件会被设置为signaled。
有了上面的3条原则,OVERLAPPED关联的事件的状态变化就好理解了。当使用connectNamedPipe(h, &op)方法时,
函数会立即返回,而实际这个操作并没有进行,而是Pending了,所以,event会由signaled变为nonsignaled,
当真正有Client连接时,这个操作才会完成,这个时候,event会由nonsignaled变为signaled。这个时候,
WaitForMultipleObjects会继续执行下去。对于Pending后的操作,一定要使用
GetOverlappedResult方法,判断结果。
上面的原则适用ReadFile, WriteFile, ConnectNamedPipe, 和 TransactNamedPipe等函数。
下面是我的代码,设计思路是利用namedpipe实现2个进程间的通讯,客户端发送3个整数给服务器端。
Server端:
m_hEvents[0] = CreateEvent(NULL,TRUE,TRUE,NULL);
// OVERLPPED‘s event
m_hEvents[1] = CreateEvent(NULL,TRUE,FALSE,NULL);
// exit event
namepipe线程:
NamedPipeWorkThread(LPVOID lParam)
TRACE(&NamedPipeWorkThread/n&);
CServerDlg * pDlg = (CServerDlg*)lP
OVERLAPPED
memset(&op,0,sizeof(op));
op.hEvent = pDlg-&m_hEvents[0];
LPTSTR lpszPipename = TEXT(&////.//pipe//mynamedpipe&);
HANDLE hPipeInst = CreateNamedPipe(
lpszPipename,
// pipe name
PIPE_ACCESS_DUPLEX |
// read/write access
FILE_FLAG_OVERLAPPED,
// overlapped mode
PIPE_TYPE_MESSAGE |
// message-type pipe
PIPE_READMODE_MESSAGE |
// message-read mode
PIPE_WAIT,
// blocking mode
// number of instances
BUFSIZE*sizeof(TCHAR),
// output buffer size
BUFSIZE*sizeof(TCHAR),
// input buffer size
PIPE_TIMEOUT,
// client time-out
// default security attributes
if(hPipeInst == INVALID_HANDLE_VALUE) {
AfxMessageBox(&CreateNamedPipe failed with %d./n&, GetLastError());
DWORD dwBytesR
BOOL fConnected,fPendingIO = FALSE;
fConnected = ConnectNamedPipe(hPipeInst, &op);
if (fConnected) {
AfxMessageBox(&ConnectNamedPipe failed with %d./n&, GetLastError());
switch (GetLastError()) {
// The overlapped connection in progress.
case ERROR_IO_PENDING:
fPendingIO = TRUE;
// Client is already connected, so signal an event.
case ERROR_PIPE_CONNECTED:
if (SetEvent(op.hEvent))
// If an error occurs during the connect operation...
AfxMessageBox(&ConnectNamedPipe failed with %d./n&, GetLastError());
BYTE btState = 0;
DWORD dwResult = WaitForMultipleObjects(2,pDlg-&m_hEvents,FALSE,INFINITE);
if(0 == dwResult - WAIT_OBJECT_0){
if(fPendingIO){
fSuccess = GetOverlappedResult(
hPipeInst, // handle to pipe
&op, // OVERLAPPED structure
// bytes transferred
// do not wait
switch(btState){
case CONNECTING_STATE:
if (! fSuccess) {
AfxMessageBox(&Error %d./n&, GetLastError());
btState = READING_STATE;
case READING_STATE:
if(!fSuccess || dwRet == 0){
DisconnectNamedPipe(hPipeInst);
TRACE(&Read bytes = %d/n&,dwRet);
TRACE(&nX=%d,nY=%d,nColor=%d/n&,PtColor.nX,PtColor.nY,PtColor.nColor);
fSuccess = ReadFile( hPipeInst,&PtColor, sizeof(PT_COLOR),&dwBytesReaded,&op);
if(fSuccess && dwBytesReaded != 0){
fPendingIO = FALSE;
TRACE(&Read bytes = %d/n&,dwBytesReaded);
TRACE(&nX=%d,nY=%d,nColor=%d/n&,PtColor.nX,PtColor.nY,PtColor.nColor);
DWORD dwErr = GetLastError();
if (! fSuccess && (dwErr == ERROR_IO_PENDING)) {
fPendingIO = TRUE;
DisconnectNamedPipe(hPipeInst);
TRACE(&exit NamedPipeWorkThread/n&);
typedef struct Tag_Pt_Color{
}PT_COLOR;
LPTSTR lpszPipename = TEXT(&////.//pipe//mynamedpipe&);
m_hPipe = CreateFile(
lpszPipename,
// pipe name
GENERIC_READ |
// read and write access
GENERIC_WRITE,
// no sharing
// default security attributes
OPEN_EXISTING,
// opens existing pipe
// default attributes
// no template file
if(INVALID_HANDLE_VALUE == m_hPipe){
AfxMessageBox(&Create NamedPipe failed with %d./n&);
DWORD dwMode = PIPE_READMODE_MESSAGE;
BOOL fSuccess = SetNamedPipeHandleState(
// pipe handle
// new pipe mode
// don't set maximum bytes
// don't set maximum time
if ( ! fSuccess) {
CString strM
strMsg.Format(&SetNamedPipeHandleState failed. GLE=%d/n&, GetLastError());
AfxMessageBox(strMsg);
ptColor.nX = m_nX;
ptColor.nY = m_nY;
ptColor.nColor = m_nC
DWORD dwBytesW
BOOL fSuccess = WriteFile(m_hPipe,&ptColor,sizeof(PT_COLOR),&dwBytesWritten,NULL);
if (!fSuccess)
CString strM
strMsg.Format(&WriteFile to pipe failed. GLE=%d/n&, GetLastError());
AfxMessageBox(strMsg);
有点复杂哦
[e01]真爽!领走了!O(∩_∩)O哈哈~
哥,试了试。。。不能用呀。。
还有问题额,不支持多客户端链接
本分类共有文章30篇,更多信息详见
& 2012 - 2016 &
&All Rights Reserved. &
/*爱悠闲图+*/
var cpro_id = "u1888441";How do I perform a non-blocking fopen on a named pipe (mkfifo)? [我如何执行一个非阻塞命名管道fopen(mkfifo)?] - 问题-字节技术
How do I perform a non-blocking fopen on a named pipe (mkfifo)?
我如何执行一个非阻塞命名管道fopen(mkfifo)?
问题 (Question)
If I have a program which creates and attempts to open a named pipe using mkfifo, how can I open a pipe for reading or writing without blocking?
Specifically, I'm writing a C program which can be run with or without a gui (written in Java).
In the C program, I successfully create the named pipes using mkfifo, however when I do
FILE* in = fopen(PIPE_IN, "r"); /* Where PIPE_IN is the filename*/
fopen doesn't return until the GUI opens that pipe for writing. What I wish to do is have that pipe ready to be read once (if) the GUI decides to write to it - I'll be putting the file descriptor in a select() call. It's reasonable to expect that the java GUI may never actually be started, so I cannot rely on it to open the other end of the pipe at any specific point or even at all.
I will also have a second pipe open for writing, and I assume I will have the same problem. Further, I can't set O_NONBLOCK on an output pipe that has no reader.
Any suggestions?
(This is running on a linux system)
如果我有一个程序创建并尝试打开命名管道使用mkfifo,我怎么能读或写不阻塞开管?具体地说,我写一个C程序可以运行或没有GUI(写在Java)。在C程序中,我成功地创建命名管道使用mkfifo,然而当我做FILE* in = fopen(PIPE_IN, "r"); /* Where PIPE_IN is the filename*/
函数不返回到GUI打开管写。我想做的就是有管准备读一次(如果)GUI决定写它——我会把文件描述符在select()呼叫。这是合理的预期,Java GUI可能从来没有真正开始,所以我不能依靠它来打开管道的另一端在任何特定的点或干脆。我也有写打开第二管,我以为我会有同样的问题。另外,我不能设置o_nonblock输出管有没有读者。有什么建议吗?(这是在Linux系统上运行)
最佳答案 (Best Answer)
You could open() your pipe O_RDONLY | O_NONBLOCK, and if you want the C stream, you can get it with fdopen(). However, there might be a problem with the select() - AFAIK, a pipe fd open for reading that has no writer is always prepared for reading, and read() returns 0, so the select() would fire indefinitely.
A kludgy way of overcoming this would be to open the pipe O_RDWR; that is, have at least one writer (your C++ program). Which would solve your problem anyway.
你可以open()你管O_RDONLY | O_NONBLOCK,如果你想C流,你可以得到它fdopen()。然而,可能有一个问题select()-就我所知,一打开阅读,没有作家管FD总是准备阅读,和read()返回0,所以select()将火下去。克服这种笨拙的方式将打开管O_RDWR;就是说,至少有一个作家(你的C + +程序)。这无论如何都会解决你的问题。
本文翻译自StackoverFlow,英语好的童鞋可直接参考原文:#include&sys/types.h&
#include&sys/stat.h&
#include&errno.h&
#include&string.h&
#include&stdio.h&
#include&unistd.h&
#include&fcntl.h&
main (void)
char buf[1024];
char fn[] = &myfifo&;
int ret = mkfifo (fn, S_IRUSR | S_IWUSR);
if (ret == -1)
printf (&mkfifo error:%s\n&, strerror (errno));
int rd = open (fn, O_RDONLY | O_NONBLOCK);
int fd = open (fn, O_WRONLY | O_NONBLOCK, S_IRWXU);//此处注意,必须先打开读,否则会打不开fifo,根据论坛提到的如果设置非阻塞,必须先open一个fifo读。
if (fd == -1)
printf (&open error:%s\n&, strerror (errno));
write (fd, &hello&, 5);
read (rd, buf, 5);
write (1, buf, 5);//向设备1写入,即显示屏
close (fd);
unlink (fn);
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:40421次
积分:1005
积分:1005
排名:千里之外
原创:51篇
转载:62篇
(1)(1)(6)(2)(1)(3)(1)(1)(1)(1)(3)(6)(4)(6)(16)(20)(31)(6)(2)(1)

我要回帖

更多关于 c 命名管道 的文章

 

随机推荐