管道
/**
* @file pipe.c
* 1.客户端传送给服务端一个路径名字
* 2.服务端读取文件内容通过ipc传送给客户端
* 3.客户端将读取的内容显示到标准输出
*
* 管道本身是半双工,fork之后是可以全双工,但是只有一个管道缓冲区,根本没法进行通信
* 全双工的通信还是像下面这样,用两个管道,用两个缓冲区实现
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILEPATH "/home/adce/mycode/vscode/unix_process/pipe/file/pipeFile.txt"
#define PATHSIZE 1024
void Client(int writefd, int readfd);
void Server(int writefd, int readfd);
int main(int argc, char **argv)
{
int pipe_client[2]; // 客户端写的管道
int pipe_server[2]; // 服务端写的管道
if (pipe(pipe_client) < 0 || pipe(pipe_server) < 0)
{
perror("pipe");
exit(-1);
}
// 让子进程运行服务端程序,父进程运行客户端程序
pid_t server_pid;
if ((server_pid = fork()) < 0)
{
perror("fork");
exit(-1);
}
if (server_pid == 0) // 子进程运行服务端程序
{
close(pipe_client[1]); // 关掉客户端的写通道
close(pipe_server[0]); // 关掉服务端的读通道
Server(pipe_server[1], pipe_client[0]); // 执行服务端程序
exit(0); // 执行完就结束
}
// 父进程运行客户端程序
close(pipe_client[0]); // 关掉客户端的读端
close(pipe_server[1]); // 关掉服务端的写端
Client(pipe_client[1], pipe_server[0]);
waitpid(server_pid, NULL, 0); // 收尸子进程
exit(0);
}
void Client(int writefd, int readfd)
{
char pathbuf[PATHSIZE];
strncpy(pathbuf, FILEPATH, PATHSIZE);
size_t pathlen = strlen(pathbuf); // 得到路径的真实长度
ssize_t writesize = write(writefd, pathbuf, pathlen);
if (writesize < pathlen) // 如果写入的字节不够,就报错,默认是阻塞写,肯定会够,不够就是出错了
{
fprintf(stderr, "write size less than pathlen.\n");
exit(-1);
}
// 循环读
ssize_t readsize;
char buf[1024];
printf("client begin read:\n");
while ((readsize = read(readfd, buf, 1024)) > 0)
{
write(STDOUT_FILENO, buf, readsize); // 将读到的内容输出到标准输出
}
printf("client read over.\n");
exit(0);
}
void Server(int writefd, int readfd)
{
char buf[PATHSIZE];
ssize_t readsize;
if((readsize = read(readfd, buf, PATHSIZE)) < 0) // 先把路径读出来
{
// 读出错
perror("read");
exit(-1);
}
int filefd;
filefd = open(buf, O_RDONLY);
if (filefd < 0)
{
fprintf(stderr, "open() failed.");
exit(-1);
}
while ((readsize = read(filefd, buf, PATHSIZE)) > 0)
{
write(writefd, buf, readsize);
}
write(writefd, "send over!\n", 12);
exit(0);
}
/**
* @file pipe-popen.c
* 1.使用popen调用cat命令读取文件
* 我们在用shell命令的时候,有一个管道通配符 |
* 这个popen就是这个作用,启用一个shell命令,并预留管道通信
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FILEPATH "/home/adce/mycode/vscode/unix_process/pipe/file/pipeFile.txt"
#define PATHSIZE 1024
int main(int argc, char **argv)
{
char command[PATHSIZE];
char buf[1024];
FILE *fp;
sprintf(command, "cat %s", FILEPATH);
fp = popen(command, "r");
int readsize;
while (fgets(buf, 1024, fp) != NULL)
{
printf(buf);
}
exit(0);
}
FIFO
两个无关系的进程之间通信。
/**
* @file fifo-server.c
* 使用命名管道通信
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
// 最宽松的权限
#define FILE_MODE (S_IRWXU | S_IRWXG | S_IRWXO)
#define FIFOCLIENT "/home/adce/mycode/vscode/unix_process/fifo/file/fifo.client"
#define FIFOSEVER "/home/adce/mycode/vscode/unix_process/fifo/file/fifo.server"
#define FILEPATH "/home/adce/mycode/vscode/unix_process/fifo/file/fifoFile.txt"
#define PATHSIZE 1024
void Client(int writefd, int readfd);
int main(int argc, char **argv)
{
int readfd, writefd;
writefd = open(FIFOCLIENT, O_WRONLY, 0);
readfd = open(FIFOSEVER, O_RDONLY, 0);
if (readfd < 0 || writefd < 0)
{
perror("open");
exit(-1);
}
Client(writefd, readfd);
unlink(FIFOCLIENT);
unlink(FIFOSEVER);
exit(0);
}
void Client(int writefd, int readfd)
{
char pathbuf[PATHSIZE];
strncpy(pathbuf, FILEPATH, PATHSIZE);
size_t pathlen = strlen(pathbuf); // 得到路径的真实长度
ssize_t writesize = write(writefd, pathbuf, pathlen);
if (writesize < pathlen) // 如果写入的字节不够,就报错,默认是阻塞写,肯定会够,不够就是出错了
{
fprintf(stderr, "write size less than pathlen.\n");
exit(-1);
}
// sleep(1); // 等待服务端先把内容写入fifo,否则可能会读空
// 循环读
ssize_t readsize;
char buf[1024];
printf("client begin read:\n");
while ((readsize = read(readfd, buf, 1024)) > 0)
{
write(STDOUT_FILENO, buf, readsize); // 将读到的内容输出到标准输出
}
printf("client read over.\n");
exit(0);
}
/**
* @file fifo-server.c
* 使用命名管道通信
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
// 最宽松的权限
#define FILE_MODE (S_IRWXU | S_IRWXG | S_IRWXO)
#define FIFOSEVER "/home/adce/mycode/vscode/unix_process/fifo/file/fifo.server"
#define FIFOCLIENT "/home/adce/mycode/vscode/unix_process/fifo/file/fifo.client"
#define PATHSIZE 1024
void Server(int writefd, int readfd);
int main(int argc, char **argv)
{
if ((mkfifo(FIFOSEVER, FILE_MODE) < 0) && (errno != EEXIST))
{
perror("mkfifo");
exit(-1);
}
if ((mkfifo(FIFOCLIENT, FILE_MODE) < 0) && (errno != EEXIST))
{
perror("mkfifo");
exit(-1);
}
int readfd, writefd;
readfd = open(FIFOCLIENT, O_RDONLY, 0);
writefd = open(FIFOSEVER, O_WRONLY, 0);
if (readfd < 0 || writefd < 0)
{
perror("open");
exit(-1);
}
Server(writefd, readfd);
exit(0);
}
void Server(int writefd, int readfd)
{
char buf[PATHSIZE];
ssize_t readsize;
if((readsize = read(readfd, buf, PATHSIZE)) < 0) // 先把路径读出来
{
// 读出错
perror("read");
exit(-1);
}
int filefd;
filefd = open(buf, O_RDONLY);
if (filefd < 0)
{
fprintf(stderr, "open() failed.");
exit(-1);
}
while ((readsize = read(filefd, buf, PATHSIZE)) > 0)
{
write(writefd, buf, readsize);
}
write(writefd, "send over!\n", 12);
exit(0);
}
一个服务端,多个客户端通信:
/**
* @file proto.h
* 客户端和服务端共同遵守的协议
* 定义消息体数据结构
* 为了简化,这里服务器就统一回复:[pid] OK.
*/
#ifndef PROTO_H_
#define PROTO_H_
#include <limits.h>
#include <unistd.h>
#include <string.h>
#define FIFO_MODE (S_IRWXU | S_IRWXG | S_IRWXO)
#define SERVER_FIFO "/home/adce/mycode/vscode/unix_process/fifo/file/serverfifo"
#define CLIENT_FIFO "/home/adce/mycode/vscode/unix_process/fifo/file/clientfifo."
#define MEGDATESIZE (PIPE_BUF - sizeof(int))
struct msg_st
{
int pid; // > 0
char data[MEGDATESIZE]; // date
};
#endif
/**
* @file server.c
* 可以处理多个客户端的通信
* 使用结构体消息
* 有一个众所周知的server fifo,服务端从这里面读取数据
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#include "proto.h"
#include <signal.h>
void handle(int p)
{
printf("logout.\n");
exit(0);
}
int main(int agrc, char **argv)
{
signal(SIGINT, handle);
// 尝试创建并打开fifo
if (mkfifo(SERVER_FIFO, FIFO_MODE) < 0 && errno != EEXIST)
{
perror("mkfifo");
exit(-1);
}
// 自己先打开写fifo,再打开读fifo,防止客户端关闭的时候,服务器整个被阻塞
int readfd = open(SERVER_FIFO, O_RDONLY);
if (readfd < 0)
{
perror("open");
exit(-1);
}
// 这个只有在readfd阻塞之后才会执行,readfd只会阻塞一次,防止readfd失效,一直占用写端,但是这个fd不用,用nofd表示。
int nofd = open(SERVER_FIFO, O_WRONLY);
if (nofd < 0)
{
perror("open");
exit(-1);
}
// 死循环执行任务
char buf[1024];
char client_path[1024];
struct msg_st msg;
printf("server begin recv:\n");
while (read(readfd, &msg, sizeof(msg)) > 0)
{
printf("[%d]:%s\n", msg.pid, msg.data); // 服务器把接受到的消息打印到终端上
sprintf(client_path, "%s%d", CLIENT_FIFO, msg.pid);
sprintf(buf, "[%d]:OK.", msg.pid);
int sendfd = open(client_path, O_WRONLY);
if (sendfd < 0)
{
perror("open");
exit(-1);
}
strncpy(msg.data, buf, sizeof(buf));
write(sendfd, &msg, sizeof(msg));
}
exit(0);
}
/**
* @file client.c
* 客户端有自己独立的client fifo
* fifo的地址通过结构体传送给服务器
* 服务器通过这个fifo传回数据
*/
#include "proto.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
void handle(int p)
{
printf("logout.\n");
exit(0);
}
int main(int argc, char **argv)
{
signal(SIGINT, handle);
pid_t pid = getpid();
char path[1024];
sprintf(path, "%s%d", CLIENT_FIFO, pid);
if (mkfifo(path, FIFO_MODE) < 0 && errno != EEXIST) // 新建自己的fifo
{
perror("mkfifo");
exit(-1);
}
// 打开服务端的fifo
int sendfd = open(SERVER_FIFO, O_WRONLY);
struct msg_st msg;
char buf[1024];
gets(buf); // 手动输入消息
msg.pid = pid;
strncpy(msg.data, buf, strlen(buf));
write(sendfd, &msg, sizeof(msg)); // 发送消息
int recvfd = open(path, O_RDONLY); // 打开自己的fifo,读内容,注意,不能打开早了,否则会阻塞
while(1)
{
read(recvfd, &msg, sizeof(msg));
puts(msg.data);
gets(buf);
strncpy(msg.data, buf, strlen(buf));
write(sendfd, &msg, sizeof(msg)); // 发送消息
}
exit(0);
}