#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <libgen.h>
#include <stdlib.h>
#define FIFO1 ".fifo_chat1"
#define FIFO2 ".fifo_chat2"
int g_stop=0;//创建一个全局变量,以便每个函数都可以使用
void sig_pipe(int signum)
{
if(SIGPIPE == signum)
{
printf("get pipe broken signal and let programe exit\n");
g_stop=1;
}
}
int main(int argc,char** argv)
{
int mode=0;
int fdr_fifo;
int fdw_fifo;
fd_set rdset;
char buf[1024];
int rv;
if(argc!=2)
{
printf("Usage :%s [0/1]\n",basename(argv[0]));
printf("this chat program need run twice,1st time run with [0] and 2nd time with [1]\n");
return -1;
}
mode = atoi(argv[1]);
if(access(FIFO1,F_OK))//判断是否存在,不存在则使用mkfifo创建一个管道文件;
{
printf("FIFO file \"%s\"not exist and create it noe\n,FIFO1");
mkfifo(FIFO1,0666);
}
if(access(FIFO2,F_OK))
{
printf("FIFO file \%s\not exist and create it noe\n,FIFO1");
mkfifo(FIFO2,0666);
}
signal(SIGPIPE,sig_pipe);
if(0 == mode)
{
printf("start open '%s' for read and it will blocked untill write endpoint opened...\n",FIFO1);
if((fdr_fifo=open(FIFO1,O_RDONLY))<0)
{
printf("open fifo[%s] for chat read endpoint failure:%s\n",FIFO1,strerror(errno));
return -2;
}
printf("start open '%s' for read...\n",FIFO2);
if((fdw_fifo=open(FIFO2,O_WRONLY))<0)
{
printf("Open fifo[%s] for chat read endpint failure:%s\n",FIFO2,strerror(errno));
return -3;
}
}
else
{
printf("start open '%s' for write and it will blocked untill read endpoint opened ...\n",FIFO1);
if((fdw_fifo=open(FIFO1,O_WRONLY))<0)
{
printf("open fifo[%s] for chat write endpoint failure:%s\n",FIFO1,strerror(errno));
return -4;
}
printf("start open '%s' for read'...\n",FIFO2);
if((fdr_fifo=open(FIFO2,O_RDONLY))<0)
{
printf("open fifo[%s] for chat readd endpoint failure:%s\n",FIFO2,strerror(errno));
return -5;
}
}
printf("start chating with another program now,please input message now:\n");
while(!g_stop)
{
FD_ZERO(&rdset);
FD_SET(STDIN_FILENO,&rdset);
FD_SET(fdr_fifo,&rdset);
rv=select(fdr_fifo+1,&rdset,NULL,NULL,NULL);
if(rv<=0)
{
printf("Select get timeout or eror:%s\n",strerror(errno));
continue;
}
if(FD_ISSET(fdr_fifo,&rdset))//监听是否还在集合中,如果是,测返回1,执行if中的语句
{
memset(buf,0,sizeof(buf));
rv=read(fdr_fifo,buf,sizeof(buf));
if(rv<0)
{
printf("Another side of FIFO get closed and program will exit now\n");
break;
}
else if(0==rv)//返回0表示另外一端已经断开
{
printf("Another side of FIFO get closed and program will wxit now\n");
break;
}
printf("<-- %s",buf);
}
if(FD_ISSET(STDIN_FILENO,&rdset))
{
memset(buf,0,sizeof(buf));
fgets(buf,sizeof(buf),stdin);
write(fdw_fifo,buf,strlen(buf));
}
}
return 0;
}
代码
如上
其他函数请自行百度或者查看上一篇文章,应其他文章已经介绍过,在此不再讲解。
命名管道
前面讲到的未命名的管道只能在两个具有亲缘关系的进程之间通信,通过命名管道(Named PiPe)FIFO,不相关的进程也能 交换数据。FIFO不同于管道之处在于它提供一个路径与之关联,以FIFO的文件形式存在于系统中。它在磁盘上有对应的节点,但 没有数据块——换言之,只是拥有一个名字和相应的访问权限,通过mknode()系统调用或者mkfifo()函数来建立的。一旦建 立,任何进程都可以通过文件名将其打开和进行读写,而不局限于父子进程,当然前提是进程对FIFO有适当的访问权。当不再被 进程使用时,FIFO在内存中释放,但磁盘节点仍然存在。 下面这个例程创建了两个掩藏的命名管道文件(.fifo_chat1和.fifo_chat2)在不同的进程间进行双向通信。该程序需要运行两 次(即两个进程),其中进程0(mode=0)从标准输入里读入数据后通过命名管道2(.fifo_chat2)写入数据给进程 1(mode=1);而进程1(mode=1)则从标准输入里读入数据后通过命名管道1(.fifo_chat1)写给进程0,这样使用命名管 道实现了一个进程间聊天的程序。
SIGPIPE
这个信号是很常见的。当往一个写端关闭的管道或socket连接中连续写入数据时会引发SIGPIPE信号,引发SIGPIPE信号的写操作将设置errno为EPIPE。在TCP通信中,当通信的双方中的一方close一个连接时,若另一方接着发数据,根据TCP协议的规定,会收到一个RST响应报文,若再往这个服务器发送数据时,系统会发出一个SIGPIPE信号给进程,告诉进程这个连接已经断开了,不能再写入数据。
signal函数
void(* signal(int sig,void(* func)(int)))(int);
指定使用sig指定的信号编号处理信号的方法。 参数func指定程序可以处理信号的三种方式之一:
默认处理(SIG_DFL):信号由该特定信号的默认动作处理。
忽略信号(SIG_IGN):忽略信号,即使没有意义,代码执行仍将继续。
函数处理程序:定义一个特定的函数来处理信号。
该程序用的是第三个,接受到该信号之后用一个特定的函数来处理该信号。
access函数
该函数用来确定文件或者文件的访问 权限。
参数:
pathname:文件目录,当前文件内则可以直接使用文件名
mode:
R_OK 只判断是否有读权限
W_OK 只判断是否有写权限
X_OK 判断是否有执行权限
F_OK 只判断是否存在
在宏定义里面分别对应:
00 只存在
02 写权限
04 读权限
06 读和写权限
select函数
该函数是用来监视我们需要监视的文件描述符
int maxfdp:集合中所有文件描述符的范围,为所有文件描述符的最大值加1。
fd_set *readfds:要进行监视的读文件集。
fd_set *writefds :要进行监视的写文件集。
fd_set errorfds:用于监视异常数据。
struct timeval timeout:select的超时时间,它可以使select处于三种状态:
第一,若将NULL以形参传入,即不传入时间结构,就是 将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;
第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数, 不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;
第三,timeout的值大于0,这就是等待的超时时间,即 select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回
其文件描述字的使用规则如下
fd_set fdset要在前面定义一个该类型的文件描述符
- FD_ZERO(fd_set *fdset);将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始 化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。
- FD_SET(fd_set *fdset);用于在文件描述符集合中增加一个新的文件描述符。
- FD_CLR(fd_set *fdset);用于在文件描述符集合中删除一个文件描述符。
- FD_ISSET(int fd,fd_set *fdset);用于测试指定的文件描述符是否在该集合中。