Linux C:“交互式会话”,具有单独的读写命名管道?

时间:2010-05-06 13:06:43

标签: c named-pipes interactive

我正在尝试使用“使用命名管道的进程间通信简介 - 使用命名管道的全双工通信”,link;特别是fd_server.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

我想做的是做一个“互动”(或“shell”式)会话;也就是说,服务器照常运行,但我不想运行catecho,而是想使用类似 screen 的东西。我的意思是,该屏幕可以像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

这就是我所谓的“交互式会话”(忽略调试语句) - 服务器等待我输入命令;它在收到命令后给出它的输出(并且在这种情况下它在第一个命令之后退出,启动脚本也是如此)。除此之外,我希望没有缓冲输入,但是逐个字符发送(意味着上面的会话应该在第一次按键后退出,并打印出一个字母 - 这就是我所期望的stty原始的帮助,但它不会:它只会杀死对 Enter Ctrl - C :)的反应。)

如果已经有一个现有的命令(类似于screen关于串行设备的话,我猜)我只是会徘徊,它会接受两个这样的命名管道作为参数,并建立一个“终端”或“shell” “喜欢通过他们的会话;或者我是否必须使用上述脚本和/或编程自己的'客户',它将作为终端..

1 个答案:

答案 0 :(得分: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 ... */
} 

在手册页中,读取EOF返回0,错误返回-1(您已阅读手册页,对吗?http://linux.die.net/man/2/read)。所以这样做是继续从读取管道抓取字节,直到它达到EOF或一些错误,在这种情况下你(可能)打印一条消息并退出。也就是说,当你得到一个EOF时,你可能只是重新打开,这样你就可以得到更多的输入。

一旦您修改了程序以便连续阅读,交互式输入多行很简单。只需执行:

cat - > /tmp/np1

' - '明确告诉cat从stdin读取(这是默认值,所以你实际上并不需要破折号)。因此,cat会将您输入的所有内容传递给管道程序。您可以使用Ctrl + D插入EOF,这将导致cat停止读取标准输入。您的管道程序会发生什么取决于您在读取循环中如何处理EOF。

现在,如果你想要另一个程序来完成所有io,没有cat,(所以你最终得到一个stdio echo程序),伪代码看起来会像这样:

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。读取,写入和打开管道都应该与服务器程序完全相同,除了读/写都是相反的。