管道是一种用于进程通信的特殊文件。管道的类型有两种:匿名管道和命名管道。匿名管道最初用于本地系统中父进程与它启动的子进程之间的通信。命名管道则通过一个名字进行标识,使客户端和服务端应用程序可以通过该管道进行通信。Win32命名管道甚至可以在不同系统的进程间使用,这使它成为许多客户/服务器应用程序的理想之选。
现在用命名管道来举例如何利用管道实现进程间的通信。首先,需要创建命名管道,它的具体创建代码如下:
hPipe = CreateNamedPipe("\\\\.\\pipe\\MyPipe",
//管道名称
PIPE_ACCESS_DUPLEX |
FILE_FLAG_OVERLAPPED,
0, 1, 1024, 1024, 0, NULL);
if(INVALID_HANDLE_VALUE == hPipe)
{
printf("CreateNamedPipe() failed\n");
hPipe = NULL;
return;
}
//利用Event来同步数据的读写
HANDLE hEvent;
hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if(!hEvent)
{
printf("CreateEvent() failed\n");
CloseHandle(hPipe);
hPipe = NULL;
return;
}
OVERLAPPED ovlap;
ZeroMemory(&ovlap, sizeof(OVERLAPPED));
ovlap.hEvent = hEvent;
if(!ConnectNamedPipe(hPipe, &ovlap))
{
if(ERROR_IO_PENDING != GetLastError())
{
printf("ConnectNamedPipe() failed\n");
CloseHandle(hPipe);
CloseHandle(hEvent);
hPipe = NULL;
return;
}
}
if(WAIT_FAILED == WaitForSingleObject(hEvent,
INFINITE))
{
printf("WaitForSingleObject() failed\n");
CloseHandle(hPipe);
CloseHandle(hEvent);
hPipe = NULL;
return;
}
CloseHandle(hEvent);
管道创建后,读取数据,对于命名管道的数据读取操作,与匿名管道的读取操作是一样的,代码如下:
//利用Event等待数据的到来
HANDLE hEvent =
CreateEvent(NULL, FALSE, FALSE, "PIPE_EVENT");
WaitForSingleObject(hEvent, INFINITE);
char buf[100];
DWORD dwRead;
if(!ReadFile(hPipe, buf, 100, &dwRead, NULL))
{
printf("ReadFile() failed\n");
return;
}
m_recieve = buf;
管道创建后,写入数据,对于命名管道的数据写入操作,与匿名管道的写入操作也是相同的,代码如下:
HANDLE hEvent =
CreateEvent(NULL, FALSE, FALSE, "PIPE_EVENT");
char * buf = (char*)(LPCTSTR)m_send;
DWORD dwWrite;
if(!WriteFile(hPipe, buf, strlen(buf)+1, &dwWrite,
NULL))
{
printf("WriteFile() failed\n");
return;
}
//设置Event,标志写数据完成
SetEvent(hEvent);
客户端在读写管道之前需要连接命名管道。客户端在连接服务器端程序创建的命名管道之前,首先应判断一下,是否有可以利用的命名管道,这可以通过调用 WaitNamedPipe()函数,该函数会一直等待,直到指定的超时间隔已过,或者指定了命名管道的实例可以用来连接了,也就是说该管道的服务器进程有了一个未决的ConnectNamedPipe操作。如果当前命名管道的实例可以使用,那么客户端就可以调用CreateFile函数打开这个命名管道,与服务端进程进行通信了。客户端的连接命名管道的代码如下:
if(!WaitNamedPipe(_T("\\\\.\\pipe\\MyPipe"),
NMPWAIT_WAIT_FOREVER))
{
printf("no pipe instance exist\n");
return;
}
hPipe = CreateFile(_T("\\\\.\\pipe\\MyPipe"),
GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if(INVALID_HANDLE_VALUE == hPipe)
{
printf("CreateFile() failed\n");
hPipe = NULL;
return;
}
完成管道连接之后,客户端可以读取管道中的数据了。如果客户端成功打开了指定的命名管道,那么就可以进行读取和写入操作了,具体代码如下:
char buf[100];
DWORD dwRead;
if(!ReadFile(hPipe, buf, 100, &dwRead, NULL))
{
printf("ReadFile() failed\n");
return;
}
m_recieve = buf;
同样也可以通过客户端对管道写入数据。客户端向命名管道写入数据与上面服务器端向命名管道写入数据一样,具体代码如下:
char * buf = (char*)(LPCTSTR)m_send;
DWORD dwWrite;
if(!WriteFile(hPipe, buf, strlen(buf)+1, &dwWrite,
NULL))
{
printf("WriteFile() failed\n");
return;
}
有名管道
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
int main(int argc,char *argv[])
{
int fd;
char buf[20] = "hello
world!!!/n";
if((mkfifo("my_fifo",
O_CREAT|O_RDWR|0666)) < 0)
{
perror("mkfifo");
exit(1);
}
if((fd = open("my_fifo" ,
O_WRONLY)) < 0)
{
perror("open");
exit(1);
}
if((write(fd,buf,strlen(buf)-1)) < 0)
{
perror("write");
exit(1);
}
return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
int main(int argc,char *argv[])
{
int fd;
char buf[20] = "";
if((fd = open("my_fifo" ,
O_RDONLY)) < 0)
{
perror("open");
exit(1);
}
if((read(fd,buf,20)) < 0)
{
perror("read");
exit(1);
}
printf("%s" , buf);
return 0;
}
无名管道
#include<stdio.h>
#include<unistd.h>
int main()
{
int n,fd[2]; // 这里的fd是文件描述符的数组,用于创建管道做准备的
pid_t pid;
char line[100];
if(pipe(fd)<0) // 创建管道
{
printf("pipe create
error\n");
return -1;
}
if((pid=fork())<0) //利用fork()创建新进程
{
printf("fork error\n");
return -1;
}
else if(pid>0)
{ //这里是父进程,先关闭管道的读出端,然后在管道的写端写入“hello world"
close(fd[0]);
write(fd[1],"hello
word\n",11);
}
else
{
close(fd[1]); //这里是子进程,先关闭管道的写入端,然后在管道的读出端读出数据
n= read(fd[0],line,100);
write(STDOUT_FILENO,line,n);
}
exit(0);
}
Copyright 2011-2020 © MallocFree. All rights reserved.