简单版通信——一端轮流说一句
实现读写同步:
1.管道是半双工的,要想实现双向通信,需要设置两条管道
2.当写端没写或是管道内缓冲区数据为空,那么读端读数据会发生阻塞;
当写端断开,读端读取管道会返回零,会一直while(1)循环,需要ctrl+c结束
写端是指给管道内写入数据
读端是指从管道内读取数据
3.管道的缓冲是4k大小,标准输入大于4k,将会发生踩踏
对于1号管道,由于b端先打开写端,所以b先说话 a端 打开1号管道的读端 打开2号管道的写端
b端 打开1号管道的写端 打开2号管道的读端 这两个假如顺序错误,则会产生死锁,互相等待 比如说,打开不同管道的任意一端或者打开同一管道的同一端,会产生死锁
a端的代码如下:
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys.stat.h>
#include <fcntl.h>
#include <unistd.h>
//a端
int main(int argc, char* argv[])
{
if (argc != 3)
{
printf("error args\n");
return -1;
}
int fd1, fd2;
//必须O_RDONLY
//打开1号管道的读端
fd1 = open(argv[1], O_RDONLY);
if (-1 == fd1)
{
perror("open1");
return -1;
}
//必须O_WRONLY
//打开2号管道的写端
fd2 = open(argv[2], O_WRONLY);
if (-1 == fd2)
{
perror("open2");
return -1;
}
printf("I am A");
char buf[128] = {0};
while(1)
{
memset(buf, 0, sizeof(buf));
read(fd1, buf, sizeof(buf));//读取一号管道的内容到buf里
printf("%s\n", buf);//输出到屏幕
memset(buf, 0, sizeof(buf));
read(0, buf, sizeof(buf));//读取标准输入到buf里
write(fd2, buf, strlen(buf) - 1);//把buf里的内容写到二号管道
}
return 0;
}
b端的代码如下:
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys.stat.h>
#include <fcntl.h>
#include <unistd.h>
//b端
int main(int argc, char* argv[])
{
if (argc != 3)
{
printf("error args\n");
return -1;
}
int fd1, fd2;
//必须O_WRONLY
//打开1号管道的写端
fd1 = open(argv[1], O_WRONLY);
if (-1 == fd1)
{
perror("open1");
return -1;
}
//必须O_RDONLY
//打开2号管道的读端
fd2 = open(argv[2], O_RDONLY);
if (-1 == fd2)
{
perror("open2");
return -1;
}
printf("I am B");
char buf[128] = {0};
while(1)
{
memset(buf, 0, sizeof(buf));
read(0, buf, sizeof(buf));//读取标准输入到buf里
write(fd1, buf, strlen(buf) - 1);//把buf里的内容写到一号管道
memset(buf, 0, sizeof(buf));
read(fd2, buf, sizeof(buf));//读取二号管道的内容到buf里
printf("%s\n", buf);//输出到屏幕上
}
return 0;
}
mkfifo 1.pipe
mkfifo 2.pipe
gcc a.c -o a
gcc b.c -o b
./a 1.pipe 2.pipe
./b 1.pipe 2.pipe
缺点是只能实现一端只能轮流说一句话
正常版通信select()函数——IO多路转接模型
文件描述符具有阻塞属性的是标准输入描述符0和读描述符fdr
让select监控文件描述符,0号描述符和fdr描述符,一旦观察到buf区里面有数据,马上通知,若0号描述符可读,则写出,返回;若fdr可读,则写出,返回;若两个都不可读,则阻塞。
select函数的相关接口
#include <sys/select.h>
#include <sys/time.h>
int select(int maxfd, fd_set* readset, fd_set* writeset, fd_set* exceptionset,
const struct timeval* timeout);
//maxfd:最大的文件描述符(其值应该为最大的文件描述符字+1)
//readset:内核读操作的描述符字集合
//writeset:内核写操作的描述符字集合
//exceptionset:内核异常操作的描述符字集合
//timeout:等待描述符就绪需要多少时间,NULL代表永远等下去,一个固定值代表固定时间,
//0代表根本不等待,检查描述字之后立即返回。
//其中readset,writeset,exceptionset都是fd_set集合
void FD_ZERO(fd_set* fdset);//将所有fd清零
void FD_SET(int fd, fd_set* fdset);//增加一个fd*
void FD_CLR(int fd, fd_set* fdset);//删除一个fd*
int FD_ISSET(int fd, fd_set* fdset);//判断一个fd,是否有设置
先定义一个集合数组,用于存储要监控的fd,再传数组的指针(对数组取地址)
fd_set rdset;
FD_ZERO(&rdset);//每次循环都需要清零
//告诉select函数要监视的文件描述符
FD_SET(0, &rdset);//每次循环都需要监视
FD_SET(fdr, &rdset);//每次循环都需要监视
//设置超时时间,若超过,还没有描述符就绪可读,则返回;若同时可读,也返回
//传入传出参数rdset
select里面对rdset做的操作
fd_set tmpset
memcpy(&tmpset, &rdset, sizeof(fd_set));
//内存拷贝可以拷贝任意格式的数据
FD_ZERO(&rdset);//清空
进行轮询:int FD_ISSET(int fd, fd_set* fdset);
//循环调用该函数测试描述符函数集
b端代码如下:
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
//管道的写端
int main(int argc,char* argv[])
{
if(argc != 3)
{
printf("error args\n");
return -1;
}
int fdw,fdr;
fdw = open(argv[1], O_WRONLY);//必须O_WRONLY
if(-1 == fdw)
{
perror("open");
return -1;
}
fdr = open(argv[2], O_RDONLY);//必须O_RDONLY
if(-1 == fdr)
{
perror("open");
return -1;
}
printf("I am B\n");
char buf[128];
int ret;
fd_set rdset;
while(1)
{
FD_ZERO(&rdset);//每次循环都要重新设置rdset的值
FD_SET(0, &rdset);
FD_SET(fdr, &rdset);
ret=select(fdr+1, &rdset, NULL, NULL, NULL);//传入传出参数
//若ret大于零,则有描述符可读;
//有一个描述符可读,ret等于1;
//有两个描述符可读,ret等于2;
//有n个描述符同时可读,ret等于n
if(ret>0)
{
if(FD_ISSET(0, &rdset))//判断标准输入是否可读
{
memset(buf, 0, sizeof(buf));
ret = read(0, buf, sizeof(buf));//读取标准输入
if(0 == ret)
//侦测对面是否按了ctrl+d或c
{
printf("byebye\n");
close(fdr);
close(fdw);
break;
}
write(fdw, buf, strlen(buf)-1);//写到1号管道
}
if(FD_ISSET(fdr, &rdset))//判断fdr是否可读
{
memset(buf, 0, sizeof(buf));
ret=read(fdr, buf, sizeof(buf));//从2号管道中读取数据
if(0 == ret)
{
printf("byebye\n");
close(fdr);
close(fdw);
break;
}
printf("%s\n", buf);
}
}
}
return 0;
}
a端代码如下
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
//管道的读端
int main(int argc, char* argv[])
{
if(argc != 3)
{
printf("error args\n");
return -1;
}
int fdr, fdw;
fdr = open(argv[1], O_RDONLY);//必须O_RDONLY
if(-1 == fdr)
{
perror("open");
return -1;
}
fdw = open(argv[2], O_WRONLY);//必须O_WRONLY
if(-1 == fdw)
{
perror("open");
return -1;
}
printf("I am O_A\n");
char buf[128] = {0};
fd_set rdset;
int ret;
while(1)
{
FD_ZERO(&rdset);
FD_SET(0, &rdset);
FD_SET(fdr, &rdset);
ret = select(fdr+1, &rdset, NULL, NULL, NULL);//传入传出参数
if (ret > 0)
{
if(FD_ISSET(fdr, &rdset))//如果管道可读
{
memset(buf, 0, sizeof(buf));
ret = read(fdr, buf, sizeof(buf));//如果管道内没有数据,read会阻塞
if(0 == ret)
{
printf("byebye\n");
close(fdr);
close(fdw);
break;
}
printf("%s\n", buf);
}
if (FD_ISSET(0, &rdset))
{
memset(buf, 0, sizeof(buf));
ret = read(0, buf, sizeof(buf));//读取标准输入,写到2号管道
if (ret > 0)
//由于在标准输入发现要结束对话了,所以在这里判断
{
write(fdw, buf, strlen(buf)-1);
}else{
//等于0,按了ctrl+d;或者出错
printf("byebye\n");
close(fdr);
close(fdw);
break;
}
}
}
}
return 0;
}