APUE course source code
guowenxue
2 days ago 68826376ee5f47783c644c6604f4411ec747cd7e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/*********************************************************************************
 *      Copyright:  (C) 2025 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  fifo.c
 *    Description:  This file is FIFO chat example program
 *
 *        Version:  1.0.0(11/07/2025)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "11/07/2025 02:34:43 PM"
 *
 ********************************************************************************/
#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 <getopt.h>
#include <libgen.h>
#include <stdlib.h>
 
#define FIFO_FILE1      "/tmp/fifo_chat1"
#define FIFO_FILE2      "/tmp/fifo_chat2"
 
enum
{
    MODE_WRITER = 1,
    MODE_READER,
};
 
int g_stop = 0;
void sig_handler(int signum)
{
    switch(signum)
    {
        case SIGTERM:
            printf("SIGTERM signal detected(kill/killall)\n");
            break;
 
        case SIGPIPE:
            printf("SIGPIPE signal detected(socket error)\n");
            break;
 
        case SIGINT:
            printf("SIGINT signal detected(Ctrl+C)\n");
            break;
 
        default:
            break;
    }
 
    g_stop = 1;
}
 
void print_usage(char *progname)
{
    printf("%s usage: \n", progname);
    printf("  -w(--writer) writer mode\n");
    printf("  -r(--reader) reader mode\n");
    printf("  -h(--Help): print this help information\n");
 
    return ;
}
 
int main(int argc, char **argv)
{
    int            fdr_fifo;
    int            fdw_fifo;
    int            rv;
    char           buf[1024];
    int            mode = 0; /* 1: Writer 2: Reader */
    fd_set         rdset;
 
    int                     ch;
    struct option           opts[] = {
        {"writer", no_argument, NULL, 'w'},
        {"reader", no_argument, NULL, 'r'},
        {"help", no_argument, NULL, 'h'},
        {NULL, 0, NULL, 0}
    };
 
    /* parser command line input arguments */
    while( (ch=getopt_long(argc, argv, "wrh", opts, NULL)) != -1 )
    {
        switch(ch)
        {
            case 'w':
                mode = MODE_WRITER;
                break;
 
            case 'r':
                mode = MODE_READER;
                break;
 
            case 'h':
                print_usage(argv[0]);
                return 0;
        }
    }
 
    if( !mode )
    {
        print_usage(argv[0]);
        return 1;
    }
 
    /* 管道是一种半双工的通信方式,如果要实现两个进程间的双向通信则需要两个管道,即两个管道分别作为两个进程的读端和写端 */
    if( access(FIFO_FILE1 , F_OK) )
    {
        printf("FIFO file \"%s\" not exist and create it now\n", FIFO_FILE1);
        mkfifo(FIFO_FILE1, 0666);
    }
 
    if( access(FIFO_FILE2 , F_OK) )
    {
        printf("FIFO file \"%s\" not exist and create it now\n", FIFO_FILE2);
        mkfifo(FIFO_FILE2, 0666);
    }
 
    signal(SIGINT,  sig_handler);
    signal(SIGTERM, sig_handler);
    signal(SIGPIPE, sig_handler);
 
    if( MODE_READER == mode )
    {
        /* 这里以只读模式打开命名管道FIFO_FILE1的读端,默认是阻塞模式;如果另外一端(写端)没被打开则open()将会一直阻塞,
           所以另外一个进程必须先以写模式打开该文件FIFO_FILE1,否则会出现死锁  */
        printf("start open '%s' for read and it will blocked untill write endpoint opened...\n", FIFO_FILE1);
        if( (fdr_fifo=open(FIFO_FILE1, O_RDONLY)) < 0 )
        {
            printf("Open fifo[%s] for chat read endpoint failure: %s\n", FIFO_FILE1, strerror(errno));
            return 2;
        }
 
        printf("start open '%s' for write...\n", FIFO_FILE2);
        if( (fdw_fifo=open(FIFO_FILE2, O_WRONLY)) < 0 )
        {
            printf("Open fifo[%s] for chat write endpoint failure: %s\n", FIFO_FILE2, strerror(errno));
            return 2;
        }
    }
    else if( MODE_WRITER == mode )
    {
        /* 这里以只写模式打开命名管道FIFO_FILE1的写端,默认是阻塞模式;如果另外一端(读端)没被打开则open()将会一直阻塞,
           因为前一个进程是以读模式打开该管道文件的读端,所以这里必须以写模式打开该文件的写端,否则会出现死锁  */
        printf("start open '%s' for write and it will blocked untill read endpoint opened...\n", FIFO_FILE1);
        if( (fdw_fifo=open(FIFO_FILE1, O_WRONLY)) < 0 )
        {
            printf("Open fifo[%s] for chat write endpoint failure: %s\n", FIFO_FILE1, strerror(errno));
            return 2;
        }
 
        printf("start open '%s' for read...\n", FIFO_FILE2);
        if( (fdr_fifo=open(FIFO_FILE2, O_RDONLY)) < 0 )
        {
            printf("Open fifo[%s] for chat read endpoint failure: %s\n", FIFO_FILE2, strerror(errno));
            return 2;
        }
    }
 
    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);
 
        /* select多路复用监听标准输入和作为输入的命名管道读端 */
        rv = select(fdr_fifo+1, &rdset, NULL, NULL, NULL);
        if(  rv <= 0 )
        {
            printf("Select get timeout or error: %s\n", strerror(errno));
            continue;
        }
 
        /* 如果是作为输入的命名管道上有数据到来则从管道上读入数据并打印到标注输出上 */
        if( FD_ISSET(fdr_fifo, &rdset) )
        {
            memset(buf, 0, sizeof(buf));
            rv=read(fdr_fifo, buf, sizeof(buf));
            if( rv < 0)
            {
                printf("read data from FIFO get errorr: %s\n", strerror(errno));
                break;
            }
            else if( 0==rv )  /* 如果从管道上读到字节数为0,说明管道的写端已关闭 */
            {
                printf("Another side of FIFO get closed and program will exit 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));
        }
    }
 
    /* 删除管道文件  */
    unlink(FIFO_FILE1);
    unlink(FIFO_FILE2);
}