管道,无名管道,管道间通信

管道通信

管道通信(Communication Pipeline)即发送进程以字符流形式将大量数据送入管道,接收进程可从管道接收数据,二者利用管道进行通信。无论是SQL Server用户,还是PB用户,作为C/S结构开发环境,他们在网络通信的实现上,都有一种共同的方法——命名管道。由于当前操作系统的不惟一性,各个系统都有其独自的通信协议,导致了不同系统间通信的困难。尽管TCP/IP协议目前已发展成为Internet的标准,但仍不能保证C/S应用程序的顺利进行。命名管道作为一种通信方法,有其独特的优越性,这主要表现在它不完全依赖于某一种协议,而是适用于任何协议——只要能够实现通信。


管道的创建和读写

#include <stdio.h>

#define SIZE 1024*100

int main()
{
	FILE *fp = popen("ps -ef", "r");
	if (fp == NULL)
	{
		perror ("popen");
		return -1;
	}
	
	char buf[SIZE] = {0};
	
	int ret = fread(buf, sizeof(char), SIZE-1, fp);
	
	// printf ("读到的数据:\n %s\n", buf);
	
	
	FILE *fp2 = popen("grep a.out", "w");
	if (fp2 == NULL)
	{
		perror ("popen");
		return -1;
	}
	
	fwrite (buf, sizeof(char), ret, fp2);
	printf ("写入完成\n");
		
	pclose (fp);
	pclose (fp2);
		
	return 0;
}


特点

使用灵活性
命名管道具有很好的使用灵活性,表现在:
1) 既可用于本地,又可用于网络。
2) 可以通过它的名称而被引用。
3) 支持多客户机连接。
4) 支持双向通信。
5) 支持异步重叠I/O操作。
不过,当前只有Windows NT支持 服务端命名管道技术。

单个进程中的管道

#include <stdio.h>
#include <unistd.h>

#define SIZE 1024*100

int main()
{
	int fd[2];
	
	int ret = pipe(fd);
	if (ret == -1)
	{
		perror ("pipe");
		return -1;
	}
	
	ret = write (fd[1], "hello", 5);
	
	
	printf ("写入 %d 个字节\n", ret);
	char ch;
	while (1)
	{
		// 如果管道里面没有数据可读,read会阻塞
		ret = read (fd[0], &ch, 1);
		if (ret == -1)
		{
			perror ("read");
			break;
		}
		
		printf ("读到 %d 字节: %c\n", ret, ch);
	}
	close (fd[0]);
	close (fd[1]);
		
	return 0;
}


父子进程中管道通信

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>

#define SIZE 1024

// 子进程通过管道从父进程接收数据
void child_do(int *fd)
{
	// 将管道的写端关闭
	close (fd[1]);
	
	char buf [SIZE];
	
	while (1)
	{
		// 从父进程读取数据
		int ret = read (fd[0], buf, SIZE-1);
		if (ret == -1)
		{
			perror ("read");
			break;
		}
		buf[ret] = '\0';
		printf ("子进程读到 %d 字节数据: %s\n", ret, buf);
	}
	
	// 关闭读端
	close (fd[0]);
}

// 父进程通过管道向子进程发送数据
void father_do(int *fd)
{
	// 将管道读端关闭
	close (fd[0]);
	
	char buf[SIZE];
	
	while (1)
	{
		fgets (buf, SIZE, stdin);
		
		// 向子进程发送数据
		int ret = write (fd[1], buf, strlen(buf));
		printf ("父进程发送了 %d 字节数据\n", ret);
	}
	
	// 关闭写端
	close (fd[1]);
}

int main()
{
	int fd[2];
	
	// 创建管道
	int ret = pipe(fd);
	if (ret == -1)
	{
		perror ("pipe");
		return -1;
	}
	
	// 创建子进程
	pid_t pid = fork();
	
	switch (pid)
	{
		case -1:
			perror ("fork");
			break;
		case 0:   // 子进程
			child_do(fd);
			break;
		default:
			father_do(fd);
			break;
	}
		
	return 0;
}



父子进程中管道通信实现文件复制

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>

#define SIZE 1024

// 子进程通过管道从父进程接收数据
void child_do(int *fd)
{
	// 将管道的写端关闭
	close (fd[1]);
		
	int fd_write = open ("2.mmap", O_WRONLY|O_CREAT, 0777);
	if (fd_write == -1)
	{
		perror ("open");
		return;
	}
	int ret;
	char buf [SIZE];
	
	// read 从管道读数据,如果管道没有数据可读,read 会阻塞
	// 如果 管道的写端 被关闭, read 返回 0
	while (ret = read (fd[0], buf, SIZE))
	{
		if (ret == -1)
		{
			perror ("read");
			break;
		}
		
		// 把从父进程接收的数据写入到新文件中
		write (fd_write, buf, ret);
	}
	
	printf ("文件复制完成\n");
	// 关闭读端
	close (fd[0]);
	close (fd_write);
}

// 父进程通过管道向子进程发送数据
void father_do(int *fd)
{
	// 将管道读端关闭
	close (fd[0]);
	
	int fd_read = open ("1.mmap", O_RDONLY);
	if (fd_read == -1)
	{
		perror ("open");
		return;
	}
	int ret;
	char buf[SIZE];
	while (ret = read (fd_read, buf, SIZE))
	{
		if (ret == -1)
		{
			perror ("read");
			break;
		}
		
		// 把读到的内容发送给子进程
		write (fd[1], buf, ret);
	}
	
	// 关闭写端
	close (fd[1]);
	close (fd_read);
}

int main()
{
	int fd[2];
	
	// 创建管道
	int ret = pipe(fd);
	if (ret == -1)
	{
		perror ("pipe");
		return -1;
	}
	
	// 创建子进程
	pid_t pid = fork();
	
	switch (pid)
	{
		case -1:
			perror ("fork");
			break;
		case 0:   // 子进程
			child_do(fd);
			break;
		default:
			father_do(fd);
			break;
	}
		
	return 0;
}


管道读端关闭,写端继续写


#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>

#define SIZE 1024

// 子进程通过管道从父进程接收数据
void child_do(int *fd)
{
	close (fd[1]);
	close (fd[0]);
}

void father_do(int *fd)
{
	// 将管道读端关闭
	close (fd[0]);
	
	printf ("等待子进程关闭读端\n");
	sleep(2);
	
	// 所有读端都关闭了,写端继续往管道写入数据
	// 如果管道所有的读端都被关闭,继续写数据系统默认的操作是使程序退出
	write (fd[1], "hello", 5);
	
	
	printf ("11111111111111111111111111111111\n");
	// 关闭写端
	close (fd[1]);
}

int main()
{
	int fd[2];
	
	// 创建管道
	int ret = pipe(fd);
	if (ret == -1)
	{
		perror ("pipe");
		return -1;
	}
	
	// 创建子进程
	pid_t pid = fork();
	
	switch (pid)
	{
		case -1:
			perror ("fork");
			break;
		case 0:   // 子进程
			child_do(fd);
			break;
		default:
			father_do(fd);
			break;
	}
		
	return 0;
}

程序设计的注意事项
1.如果 命名管道 客户端已打开,函数将会强迫关闭管道,用DisconnectNamedPipe关闭的管道,其客户端还必须用CloseHandle来关闭最后的管道。
2. ReadFile和WriteFile的hFile句柄是由CreateFile及ConnectNamedPipe返回得到。
3.一个已被某客户端连接的管道句柄在被另一客户通过ConnectNamedPipe建立连接之前,服务端必须用DisconnectNamedPipe函数对已存在的连接进行强行拆离。服务端拆离管道会造成管道中数据的丢失,用 FlushFileBuffers函数可以保证数据不被丢失。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值