现在的位置: 首页 > 综合 > 正文

使用 FIFO 的客户/服务器应用程序示例

2018年03月18日 ⁄ 综合 ⁄ 共 4111字 ⁄ 字号 评论关闭

头文件 client.h

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>

#define SERVER_FIFO_NAME "/tmp/serv_fifo"
#define CLIENT_FIFO_NAME "/tmp/cli_%d_fifo"

#define BUFFER_SIZE 20

struct data_to_pass_st {
    pid_t client_pid;
    char some_data[BUFFER_SIZE-1];
};

服务器应用程序 server.c

#include "client.h"
#include <ctype.h>

int main()
{
    int server_fifo_fd, client_fifo_fd;
    struct data_to_pass_st my_data;
    int read_res;
    char client_fifo[256];
    char *tmp_char_ptr;

    mkfifo( SERVER_FIFO_NAME, 0777 ); //创建一个服务器fifo
    server_fifo_fd = open( SERVER_FIFO_NAME, O_RDONLY );  //以只读方式打开fifo
    if( -1 == server_fifo_fd ) {
        fprintf( stderr, "Server fifo failure\n" );
        exit( EXIT_FAILURE );
    }


    do {
        read_res = read( server_fifo_fd, &my_data, sizeof(my_data) ); //当客户端程序以写方式打开服务器fifo后,解除阻塞,开始读取数据
        if( read_res>0 ) {
            tmp_char_ptr = my_data.some_data;
            while( *tmp_char_ptr ) {   //将每个字符进行大写化
                *tmp_char_ptr = toupper( *tmp_char_ptr );
                tmp_char_ptr++;
            }
            sprintf(client_fifo, CLIENT_FIFO_NAME, my_data.client_pid);  //将客户发送的client_pid提取出来,构成客户fifo的名称

            client_fifo_fd = open( client_fifo, O_WRONLY );  //以只写的阻塞方式打开客户fifo
            if( -1 != client_fifo_fd ) {
                write( client_fifo_fd, &my_data, sizeof(my_data) );  //将数据写入客户fifo
                close( client_fifo_fd );
            }
        }
    }while( read_res>0 ); 
    close(server_fifo_fd);
    unlink(SERVER_FIFO_NAME);
    exit(EXIT_SUCCESS);
}

客户程序client.c

#include "client.h"
#include <ctype.h>

int main()
{
    int server_fifo_fd, client_fifo_fd;
    struct data_to_pass_st my_data;
    int times_to_send;
    char client_fifo[256];

    server_fifo_fd = open( SERVER_FIFO_NAME, O_WRONLY ); //以只读方式打开
    if( -1 == server_fifo_fd ) {
        fprintf(stderr, "Sorry, no server\n");
        exit( EXIT_FAILURE );
    }

    my_data.client_pid = getpid(); //获取当前库互进程的pid,作为发送数据的一部分,并且构成客户的fifo名称
    sprintf(client_fifo, CLIENT_FIFO_NAME, my_data.client_pid);
    if( -1 == mkfifo(client_fifo, 0777 ) ) {
        fprintf(stderr, "Sorry, can't make %s\n", client_fifo );
        exit( EXIT_FAILURE );
    }

    for( times_to_send=0; times_to_send<5; times_to_send++ ) {  //发送一次数据,读取一次服务器处理后的数据,连续循环5次
        sprintf(my_data.some_data, "Hello from %d", my_data.client_pid);
        printf("%d sent %s, ",my_data.client_pid, my_data.some_data);
        write( server_fifo_fd, &my_data, sizeof(my_data) );
        client_fifo_fd = open( client_fifo, O_RDONLY );
        if( -1 != client_fifo_fd ) {
            if( read(client_fifo_fd, &my_data, sizeof(my_data))>0 ) {
                printf("received: %s\n", my_data.some_data);
            }
            close(client_fifo_fd);
        }
    }
    close(server_fifo_fd);
    unlink(client_fifo);
    exit(EXIT_SUCCESS);
}

运行结果:

[zhang@localhost 客户服务器应用]$ ./server &
[1] 6048
[zhang@localhost 客户服务器应用]$ for i in 1 2 3 4 5
> do
> ./client &
> done
[2] 6055
[3] 6056
[4] 6057
[5] 6058
[6] 6059
[zhang@localhost 客户服务器应用]$ 6058 sent Hello from 6058, received: HELLO FROM 6058
6058 sent Hello from 6058, received: HELLO FROM 6058
6058 sent Hello from 6058, received: HELLO FROM 6058
6058 sent Hello from 6058, received: HELLO FROM 6058
6059 sent Hello from 6059, received: HELLO FROM 6059
6058 sent Hello from 6058, received: HELLO FROM 6058
6059 sent Hello from 6059, received: HELLO FROM 6059
6059 sent Hello from 6059, received: HELLO FROM 6059
6059 sent Hello from 6059, received: HELLO FROM 6059
6059 sent Hello from 6059, received: HELLO FROM 6059
6057 sent Hello from 6057, received: HELLO FROM 6057
6055 sent Hello from 6055, received: HELLO FROM 6055
6057 sent Hello from 6057, received: HELLO FROM 6057
6055 sent Hello from 6055, received: HELLO FROM 6055
6057 sent Hello from 6057, received: HELLO FROM 6057
6055 sent Hello from 6055, received: HELLO FROM 6055
6057 sent Hello from 6057, received: HELLO FROM 6057
6055 sent Hello from 6055, received: HELLO FROM 6055
6057 sent Hello from 6057, received: HELLO FROM 6057
6056 sent Hello from 6056, received: HELLO FROM 6056
6055 sent Hello from 6055, received: HELLO FROM 6055
6056 sent Hello from 6056, received: HELLO FROM 6056
6056 sent Hello from 6056, received: HELLO FROM 6056
6056 sent Hello from 6056, received: HELLO FROM 6056
6056 sent Hello from 6056, received: HELLO FROM 6056

[1]   Done                    ./server
[2]   Done                    ./client
[3]   Done                    ./client
[4]   Done                    ./client
[5]-  Done                    ./client
[6]+  Done                    ./client

不同的客户请求交错在一起,但每个客户都获得了正确的服务器返回给它的处理数据。要注意的是客户请求的交错顺序是随机的。

服务器以只读模式创建它的 FIFO 并阻塞,直到第一个客户以写方式打开同一个 FIFO 来建立连接为止。此时,服务器进程解除阻塞。

与此同时,在客户打开了服务器 FIFO 后,它创建自己唯一的一个命名管道来读取服务器的返回的数据。完成这些工作后,客户发送数据给服务器(如果管道满就阻塞),然后阻塞在对自己的 FIFO 的read调用上。等待服务器的相应。

接收到来自客户的数据后,服务器处理它,然后以写方式打开客户管道并将处理后的数据返回,这将解除客户的阻塞状态。客户被解除阻塞后,它即可从自己的管道中读取服务器返回的数据。

整个过程不断重复,直到最后一个客户关闭服务器管道为止,这将使服务器的 read 调用失败,因为已经没有进程以写方式打开服务器管道了。

抱歉!评论已关闭.