第11章 连接到近端或远端的进程:服务器与Socket

1.编写bc(pipe、fork、dup、exec)

bc/dc程序对是客户/服务器模型程序设计的一个实例,bc/dc对被称之为协同进程。

(1)创建两个管道

(2)创建一个进程来运行dc

(3)在新创建的进程中,重定向标准输入和标准输出到管道,然后运行exec dc

(4)在父进程中,读取并分析用户的输入,将命令传给dc,dc读取响应,并把响应传给用户

/**	tinybc.c	* a tiny calculator that uses dc to do its work
 **			* demonstrates bidirectional pipes
 **			* input looks like number op number which
 **			  tinybc converts into number \n number \n op \n p
 **			  and passes result back to stdout
 **
 **		+-----------+                +----------+
 **	stdin  >0           >== pipetodc ====>          |
 **	        |  tinybc   |                |   dc -   |
 **	stdout <1           <== pipefromdc ==<          |
 **		+-----------+                +----------+
 **
 **			* program outline
 **				a. get two pipes
 **				b. fork (get another process)
 **				c. in the dc-to-be process,
 **					 connect stdin and out to pipes
 **					 then execl dc
 **				d. in the tinybc-process, no plumbing to do
 **					 just talk to human via normal i/o
 **					 and send stuff via pipe
 **				e. then close pipe and dc dies
 **                     * note: does not handle multiline answers
 **/
#include
#include
#include

#define	oops(m,x)	{ perror(m); exit(x); }

void be_dc(int in[2], int out[2]);
void be_bc(int todc[2], int fromdc[2]);

void main()
{
	int	pid, todc[2], fromdc[2];	/* equipment	*/

	/* make two pipes */

	if ( pipe(todc) == -1 || pipe(fromdc) == -1 )
		oops("pipe failed", 1);

	/* get a process for user interface */

	if ( (pid = fork()) == -1 )
		oops("cannot fork", 2);
	if ( pid == 0 )				/* child is dc	*/
		be_dc(todc, fromdc);
	else {
		be_bc(todc, fromdc);		/* parent is ui	*/
		wait(NULL);			/* wait for child */
	}
}

void be_dc(int in[2], int out[2])
/*
 *	set up stdin and stdout, then execl dc
 */
{
    /* setup stdin from pipein	*/
	if ( dup2(in[0],0) == -1 )	/* copy read end to 0   */
		oops("dc: cannot redirect stdin",3);
	close(in[0]);			/* moved to fd 0	*/
	close(in[1]);			/* won't write here	*/

    /* setup stdout to pipeout	*/
	if ( dup2(out[1], 1) == -1 )	/* dupe write end to 1	*/
		oops("dc: cannot redirect stdout",4);
	close(out[1]);			/* moved to fd 1	*/
	close(out[0]);			/* won't read from here	*/

    /* now execl dc with the - option */
	execlp("dc", "dc", "-", NULL );
	oops("Cannot run dc", 5);
}

void be_bc(int todc[2], int fromdc[2])
/*
 *	read from stdin and convert into to RPN, send down pipe
 *	then read from other pipe and print to user
 *	Uses fdopen() to convert a file descriptor to a stream
 */
{
	int	num1, num2;
	char	operation[BUFSIZ], message[BUFSIZ];
	FILE	*fpout, *fpin;

	/* setup */
	close(todc[0]);			/* won't read from pipe to dc  */
	close(fromdc[1]);		/* won't write to pipe from dc */

	fpout = fdopen( todc[1],   "w" );	/* convert file desc-  */
	fpin  = fdopen( fromdc[0], "r" );	/* riptors to streams  */
	if ( fpout == NULL || fpin == NULL )
		perror("Error convering pipes to streams");

	/* main loop */
	while ( printf("tinybc: "), fgets(message,BUFSIZ,stdin) != NULL ){

		/* parse input */
		if ( sscanf(message,"%d%[-+*/^]%d",&num1,operation,&num2)!=3){
			printf("syntax error\n");
			continue;
		}

		if ( fprintf( fpout , "%d\n%d\n%c\np\n", num1, num2, 
				*operation ) == EOF )
					perror("Error writing");
		fflush(  fpout );
		if ( fgets( message, BUFSIZ, fpin ) == NULL )
			break;
		printf("%d %c %d = %s", num1, *operation , num2, message);
	}
	fclose(fpout);		/* close pipe		*/
	fclose(fpin);		/* dc will see EOF	*/
}

第11章 连接到近端或远端的进程:服务器与Socket_第1张图片

在程序中使用了fdopen,fdopen和fopen类似返回一个FILE *类型的值,不同的是此函数以文件描述符而非文件作为参数。

2.popen:让进程 看似文件

fopen打开一个指向文件的带缓存的连接:

FILE *fp;
fp = fopen("file1", "r");
c = getc(fp);
fgets(buf, len, fp);
fscanf(fp, "%d %d %s", &x, &y, &z);
fclose(fp);

popen打开一个指向进程的带缓冲的连接:

FILE *fp;
fp = popen("ls", "r");
fgets(buf, len, fp);
pclose(fp);

popen和fopen类似,popen的第一个参数是要打开的命令的名称,第二个参数可以是"r"或者是"w",但绝不会是"a"。popen和fopen以及fdopen的返回值类型都是FILE *,可以使用标准的缓存I/O操作来对其进行操作了。

/* popendemo.c
 *	demonstrates how to open a program f

你可能感兴趣的:(第11章 连接到近端或远端的进程:服务器与Socket)