Unix网络编程-管道和FIFO

本文介绍了Unix/Linux系统中进程间通信的管道和FIFO(命名管道)机制,以及如何利用它们进行文件内容的传递。在管道示例中,客户端通过管道将文件路径发送给服务端,服务端读取文件内容并回传给客户端。FIFO示例展示了无亲缘关系进程间的通信,服务端接收客户端的文件路径,读取文件内容并回传。此外,还给出了处理多个客户端请求的服务端示例,服务端通过结构体消息与各个客户端进行交互。
摘要由CSDN通过智能技术生成

管道

/**
 * @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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橙子砰砰枪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值