2010-05-06 29 views
3

我正在嘗試使用「使用命名管道的進程間通信簡介 - 使用命名管道的全雙工通信」,link;特別是fd_server.c(包括如下供參考)Linux C:具有獨立讀寫命名管道的「交互式會話」?

這是我的信息,並編譯行:

 
:~$ cat /etc/issue 
Ubuntu 10.04 LTS \n \l 
:~$ gcc --version 
gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3 
:~$ gcc fd_server.c -o fd_server 

fd_server.c創建兩個命名管道,一個用於讀取,一個用於寫入。什麼人可以做的,就是:在一個終端,運行在服務器和讀取(通過cat)其寫入管道:

 
:~$ ./fd_server & 2>/dev/null 
[1] 11354 
:~$ cat /tmp/np2 

,並在另一個,寫(使用echo)到服務器的讀取管道:

 
:~$ echo "heeellloooo" > /tmp/np1 

回到第一終端,人們可以看到:

 
:~$ cat /tmp/np2 
HEEELLLOOOO 
0[1]+ Exit 13     ./fd_server 2> /dev/null 

我想什麼做的,是讓不大不小的「互動」(或「殼」類似的)會議;也就是說,服務器像往常一樣運行,但不是運行catecho,我想使用類似的屏幕。我的意思是,屏幕可以稱爲screen /dev/ttyS0 38400,然後它進行一種交互式會話,在終端中鍵入的內容將傳遞到/dev/ttyS0,並將其響應寫入終端。現在,當然,我不能使用screen,因爲在我的情況下,程序有兩個單獨的節點,並且據我所知,screen只能指一個。

如何在這種情況下實現這種「交互式」會話(使用兩個獨立的讀/寫管道)?下面

代碼:

#include <stdio.h> 
#include <errno.h> 
#include <ctype.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
//#include <fullduplex.h> /* For name of the named-pipe */ 
#define NP1  "/tmp/np1" 
#define NP2  "/tmp/np2" 
#define MAX_BUF_SIZE 255 
#include <stdlib.h> //exit 
#include <string.h> //strlen 

int main(int argc, char *argv[]) 
{ 
    int rdfd, wrfd, ret_val, count, numread; 
    char buf[MAX_BUF_SIZE]; 

    /* Create the first named - pipe */ 
    ret_val = mkfifo(NP1, 0666); 

    if ((ret_val == -1) && (errno != EEXIST)) { 
     perror("Error creating the named pipe"); 
     exit (1); 
    } 

    ret_val = mkfifo(NP2, 0666); 

    if ((ret_val == -1) && (errno != EEXIST)) { 
     perror("Error creating the named pipe"); 
     exit (1); 
    } 

    /* Open the first named pipe for reading */ 
    rdfd = open(NP1, O_RDONLY); 

    /* Open the second named pipe for writing */ 
    wrfd = open(NP2, O_WRONLY); 

    /* Read from the first pipe */ 
    numread = read(rdfd, buf, MAX_BUF_SIZE); 

    buf[numread] = '0'; 

    fprintf(stderr, "Full Duplex Server : Read From the pipe : %sn", buf); 

    /* Convert to the string to upper case */ 
    count = 0; 
    while (count < numread) { 
     buf[count] = toupper(buf[count]); 
     count++; 
    } 

    /* 
    * Write the converted string back to the second 
    * pipe 
    */ 
    write(wrfd, buf, strlen(buf)); 
} 

編輯:

權,只是爲了澄清 - 看來我發現了一個document討論非常類似的東西,它是 - 腳本的修改有(」 對於例如,下面的腳本配置設備並啓動一個後臺進程,將從串行設備接收到的所有數據複製到標準輸出...「)爲以下程序:

# stty raw # 
(./fd_server 2>/dev/null;)& 
bgPidS=$! 
(cat < /tmp/np2 ;)& 
bgPid=$! 
# Read commands from user, send them to device 
echo $(kill -0 $bgPidS 2>/dev/null ; echo $?) 
while [ "$(kill -0 $bgPidS 2>/dev/null ; echo $?)" -eq "0" ] && read cmd; do 
    # redirect debug msgs to stderr, as here we're redirected to /tmp/np1 
    echo "$? - $bgPidS - $bgPid" >&2 
    echo "$cmd" 
    echo -e "\nproc: $(kill -0 $bgPidS 2>/dev/null ; echo $?)" >&2 
done >/tmp/np1 
echo OUT 
# Terminate background read process - if they still exist 
if [ "$(kill -0 $bgPid 2>/dev/null ; echo $?)" -eq "0" ] ; 
then 
    kill $bgPid 
fi 
if [ "$(kill -0 $bgPidS 2>/dev/null ; echo $?)" -eq "0" ] ; 
then 
    kill $bgPidS 
fi 
# stty cooked 

所以,保存腳本說starter.sh和調用它,與下一屆會議的結果:

$ ./starter.sh 
0 
i'm typing here and pressing [enter] at end 
0 - 13496 - 13497 
I'M TYPING HERE AND PRESSING [ENTER] AT END 
0~�.N=�(�~� �����}����@������~� [garble] 
proc: 0 
OUT 

這是我會叫「交互會話」(忽略調試語句) - 服務器等待我輸入命令;它在接收到一個命令後給出它的輸出(在這種情況下,它在第一個命令之後退出,起始腳本也一樣)。除此之外,我想沒有緩衝輸入,但逐字發送(這意味着上面的會話應該在第一次按鍵後退出,並且只打印出單個字母 - 這是我期望raw有幫助,但它不是:它只是殺死反應既輸入按Ctrl - ç :))

我只是遊蕩,如果已經有一個現有的命令(對於串行設備類似於screen,我猜)會接受兩個這樣的命名管道作爲參數,並通過它們建立像會話那樣的「終端」或「shell」;或者我將不得不使用上面的腳本和/或編程自己的「客戶端」,它將起到終端的作用。

+0

使用Ctrl + K將代碼縮進四個空格,這將創建一個語法高亮的代碼塊。我爲你做了,停止編輯。 ;-) – 2010-05-06 13:12:45

+0

謝謝,約翰Kugelman,建議和編輯:) – sdaau 2010-05-06 13:18:13

回答

3

如果您只是希望能夠接收多行,而不是一個退出,這很簡單。你只需要放置一個循環在你的讀/寫代碼,像這樣(快速和骯髒的):

while(1) { 
    numread = read(rdfd, buf, MAX_BUF_SIZE); 

    fprintf(stderr, "Full Duplex Server : Read From the pipe : %sn", buf); 

    /* Convert to the string to upper case */ 
    count = 0; 
    while (count < numread) { 
     buf[count] = toupper(buf[count]); 
     count++; 
    } 

    /* 
    * Write the converted string back to the second 
    * pipe 
    */ 
    write(wrfd, buf, strlen(buf)); 
} 

當然,現在你有一個應用程序,它永遠不會退出,將盡快開始做什麼它得到一個EOF等,所以,你可以重新組織來檢查錯誤:

numread = read(rdfd, buf, MAX_BUF_SIZE); 
while(numread > 0) { 
    /* ... etc ... */ 
    numread = read(rdfd,buf, MAX_BUF_SIZE); 
} 
if(numread == 0) { 
    /* ... handle eof ... */ 
} 
if(numread < 0) { 
    /* ... handle io error ... */ 
} 

從man頁面,閱讀錯誤回報0 EOF和-1(您已經閱讀手冊頁,右?http://linux.die.net/man/2/read)。所以這樣做會持續抓取讀取管道中的字節,直到達到EOF或某個錯誤,在這種情況下,您(可能)會打印一條消息並退出。也就是說,當你獲得EOF時,你可能會重新打開,這樣你就可以獲得更多的輸入。

將程序修改爲連續讀取後,交互式輸入多行很簡單。只需執行:

cat - > /tmp/np1 

的「 - 」明確地告訴貓從標準輸入讀取(這是默認的,所以你實際上並不需要破折號)。所以貓會把你輸入的所有東西都傳遞給你的管道程序。您可以使用Ctrl + D插入EOF,這會導致cat停止讀取stdin。管道程序會發生什麼情況取決於您在讀取循環中如何處理EOF。

現在,如果你想另一個程序做所有IO,沒有貓,(所以你結束了一個標準輸入輸出回波程序),僞代碼會看起來有點像這樣:

const int stdin_fd = 0; // known unix constant! 
int readpipe_fd = open the read pipe, as before 
int writepipe_fd = open the write pipe, as before 
read stdin into buffer 
while(stdin is reading correctly) { 
    write data from stdin to read pipe 
     check write is successful 
    read write pipe into buffer 
     check read is successful 
    write buffer to stdout (fprintf is fine) 
    read stdin into buffer. 
} 

如果您喜歡,可以使用read系統調用來讀取stdin,但也可以使用stdio。讀取,寫入和打開管道應全部與服務器程序相同,除了讀/寫全部反向。

+0

嗨ipeet - 感謝您的迴應!我並沒有太在意從服務器端處理多個命令 - 我感興趣的正是「另一個完成所有io的程序」;一些可以接受來自另一個程序的命名管道作爲參數,並建立一個「終端樣」會話(編輯:...而不是我在不同的終端上運行貓和回聲:)) - 我已經添加上面的編輯進行演示,希望能夠澄清一點。 乾杯! – sdaau 2010-05-06 14:39:30

+0

我不知道有任何此類已存在的程序。我已經在C中提供了這樣一個程序的大綱,看起來你已經有了一個shell腳本(我無法解釋這個亂碼)。 我相信用O_NONBLOCK打開一個'普通'文件將允許你逐字逐個輸入,如果你查詢的速度相對較快的話。 (它應該只是得到任何可用並立即返回,它不會等待填滿所請求的緩衝區空間)。您也可以使用fcntl在打開的文件(例如stdin)上設置O_NONBLOCK。儘管如此,O_NONBLOCK對管道有一些奇怪的影響(請參見mkfifo手冊頁) – ipeet 2010-05-06 20:21:11

+0

另外,如果您希望在shell腳本中輸入char-by-char,則可以使用read -n 1 – ipeet 2010-05-06 20:22:15