linux程式設計——使用FIFO的客戶/伺服器的應用程式(第十三章)

來源:互聯網
上載者:User

標籤:

13.6.2    使用FIFO的客戶/伺服器應用程式作為學習FIFO的最後一部分內容,現在考慮怎樣通過具名管道來編寫一個非常簡答的客戶/伺服器應用程式。用一個伺服器處理序來接受請求,對它們進行處理,最後把結果資料返回給發送請求的一方:客戶。
如果想要允許多個客戶進行都可以向伺服器發送資料,假設被處理的資料可以被拆分為一個個資料區塊,每個的長度都小於PIPE_BUF位元組。
因為伺服器每次只能處理一個資料區塊,所以只使用一個FIFO應該是合乎邏輯的,伺服器通過它讀取資料,每個客戶向它寫資料。只要將FIFO以阻塞模式開啟,伺服器和客戶就會根據需要自動被阻塞。
將處理後的資料返回給客戶稍微困難,需要為每個客戶安排第二個管道來接受返回的資料。通過在傳遞給伺服器的原先資料中加上客戶的進程標識符PID,雙方就可以使用它來為返回資料的管道產生一個唯一的名字。
首先編寫標頭檔client.h,它定義了客戶和伺服器程式都會用到的資料。
/************************************************************************* > File Name:    client.h > Description:  client.h定義了客戶和伺服器程式都會用到的資料. > Author:       Liubingbing > Created Time: 2015年07月15日 星期三 22時21分46秒 > Other:        client.h ************************************************************************/#ifndef _CLIENT_H#define _CLIENT_H#endif#include <stdio.h>#include <stdlib.h>#include <unistd.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 20struct data_to_pass_st {pid_t client_pid;char some_data[BUFFER_SIZE - 1];};
編寫伺服器程式server.c,它建立並開啟伺服器管道,設定為唯讀阻塞模式。接著,伺服器開始讀取客戶發送來的資料,這些資料採用的data_to_pass_st結構
/************************************************************************* > File Name:    server.c > Description:  server.c程式建立並開啟伺服器管道,設定為唯讀阻塞模式. > Author:       Liubingbing > Created Time: 2015年07月15日 星期三 22時22分49秒 > Other:        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) */mkfifo(SERVER_FIFO_NAME, 0777);/* open函數開啟SERVER_FIFO_NAME檔案,設定為O_RDONLY的阻塞模式 * 如果成功,則返迴文件描述符 */server_fifo_fd = open(SERVER_FIFO_NAME, O_RDONLY);if (server_fifo_fd == -1) {fprintf(stderr, "Server fifo failure\n");exit(EXIT_FAILURE);}sleep(10);do {/* read函數從server_fifo_)fd指向的檔案讀入sizeof(my_data)個位元組的資料到my_data指向的記憶體 * 如果成功,則返回實際讀入資料的位元組數 */read_res = read(server_fifo_fd, &my_data, sizeof(my_data));if (read_res > 0) {/* 對剛從客戶那裡讀到的資料進行處理,把some_data中的所有字元全部轉換為大寫*/tmp_char_ptr = my_data.some_data;while (*tmp_char_ptr) {*tmp_char_ptr = toupper(*tmp_char_ptr);tmp_char_ptr++;}/* sprintf把格式化的資料寫入到client_fifo指向的記憶體中 */sprintf(client_fifo, CLIENT_FIFO_NAME, my_data.client_pid);/* open函數開啟檔案client_fifo,以O_WRONLY唯寫的阻塞模式 */client_fifo_fd = open(client_fifo, O_WRONLY);if (client_fifo_fd != -1) {/* write函數從my_data指向的記憶體寫入sizeof(my_data)個位元組的資料到client_fifo_fd指向的檔案中 * 如果成功,返回實際寫入資料的位元組數 */write(client_fifo_fd, &my_data, sizeof(my_data));close(client_fifo_fd);}}} while (read_res > 0);close(server_fifo_fd);/* 關閉伺服器管道的檔案描述符,刪除FIFO檔案 */unlink(SERVER_FIFO_NAME);exit(EXIT_SUCCESS);}
編寫客戶程式client.c,這個程式的第一部分先檢查伺服器FIFO檔案是否存在,如果存在就開啟它,然後它擷取自己的進程ID,該進程ID構成要發送給伺服器的資料的一部分。接下來,它建立客戶FIFO,為下一部分內容做好準備
/************************************************************************* > File Name:    client.c > Description:  client.c程式先檢查伺服器FIFO檔案是否存在,如果存在就開啟,然後擷取自己的進程ID,該進程ID構成要發送給伺服器的資料的一部分. > Author:       Liubingbing > Created Time: 2015年07月15日 星期三 22時23分55秒 > Other:        client.c ************************************************************************/#include <stdio.h>#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];/* open函數開啟檔案SERVER_FIFO_NAME,設定為O_WRONLY的阻塞模式 * 如果成功,則返迴文件描述符 */server_fifo_fd = open(SERVER_FIFO_NAME, O_WRONLY);if (server_fifo_fd == -1) {fprintf(stderr, "Sorry, no server\n");exit(EXIT_FAILURE);}/* 獲得進程ID */my_data.client_pid = getpid();/* sprintf函數寫入格式的資料到client_fifo指向的記憶體中 */sprintf(client_fifo, CLIENT_FIFO_NAME, my_data.client_pid);if (mkfifo(client_fifo, 0777) == -1) {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++) {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 (client_fifo_fd != -1) {if (read(client_fifo_fd, &my_data, sizeof(my_data)) > 0) {printf("reveived: %s\n", my_data.some_data);}close(client_fifo_fd);}}close(server_fifo_fd);unlink(client_fifo);exit(EXIT_SUCCESS);}

結果如下所示:

不同的客戶請求交錯在一起,但每個客戶都獲得了正確的伺服器返回給它的處理資料.要注意的是客戶請求的交錯順序是隨機的,伺服器接收到客戶請求的順序隨機器的不同而不同,即使在同一台機器上,每次啟動並執行情況也可能發生變化.
程式解析
伺服器以唯讀模式建立它的FIFO並阻塞,直到第一個客戶以寫方式開啟同一個FIFO來簡曆串連為止.此時,伺服器處理序解除阻塞並執行sleep語句,這使得來自客戶的資料排隊等候
.在實際的應用程式中,應該把sleep與幾乎刪除.這裡使用它只是為了示範有多個客戶的請求同時到達時程式的正確操作方法.
與此同時,在客戶開啟了伺服器FIFO後,它建立自己唯一的一個具名管道來讀取伺服器返回的資料.完成這些工作後,客戶發送資料給伺服器(如果管道滿或伺服器仍在休眠中就阻塞),然後阻塞在對自己的FIFO的read調用上,等待伺服器的響應.
接收到來自客戶的資料後,伺服器處理它,然後以寫方式開啟客戶管道並將處理後的資料返回,這將接觸客戶的阻塞狀態.客戶被接觸阻塞後,它即可從自己的管道中讀取伺服器返回的資料.
整個處理過程不斷重複,直到最後一個客戶關閉伺服器管道為止,這將使伺服器的read調用失敗(返回0),因為已經沒有進程以寫方式開啟伺服器管道了.
如果這是一個真正的伺服器處理序,它還需要繼續等待客戶的請求,就需要對它進行修改,有兩種方法,如下所示:
1.對它自己的伺服器管道開啟一個檔案描述符,這樣read調用將總是阻塞而不是返回0
2.當read調用返回0時,關閉並重新開啟伺服器管道,使伺服器處理序阻塞在open調用處以等待客戶的到來,就像它最初啟動那樣.

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

linux程式設計——使用FIFO的客戶/伺服器的應用程式(第十三章)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.