TCP/IP Network Programming Chapter Eleven: Interprocess Communication

Basic concepts of interprocess communication

In the previous chapter, we mentioned that the process itself has its own independent memory space, and the processes exist independently of each other. Therefore, without any mechanism to support it, we can regard processes as existing in isolation from each other.

But to some extent, processes also need to "communicate". The following formally introduces the method of inter-process communication.

Interprocess communication through pipes

In order to accomplish inter-process communication, a pipe needs to be created. The pipe is not a resource belonging to the process, but like a socket, it belongs to the operating system (that is, it is not the copy object of the fork function). So, two processes communicate through the memory space provided by the operating system. The function that creates the pipeline is described below.

#include<unistd.h>
int pipe(int filedes[2]);//成功时返回0,失败时返回-1。
    filedes[0]  //通过管道接收数据时使用的文件描述符,即管道出口。
    filedes[1]  //通过管道传输数据时使用的文件描述符,即管道入口。

When the above function is called with an int array address value of 2 elements as a parameter, there are two file descriptors stored in the array, which will be used as the exit and entry of the pipeline. When the parent process calls this function, it will create a pipe and obtain the file descriptor corresponding to the entry and exit. At this time, the parent process can read and write the same pipe (I believe everyone has done such experiments). But the purpose of the parent process is to exchange data with the child process, so a file descriptor in the entry or exit needs to be passed to the child process. How to complete the transfer? The answer is to call the fork function. Demonstrate with the following example.

#include <stdio.h>
#include <unistd.h>#
define BUF_SIZE 30

int main(int argc, char *argv[]){
    int fds[2];
    char str[]="who are you?";
    char buf[BUF_SIZE];
    pid_t pid;

    pipe(fds);
    pid=fork();
    if(pid==0)write(fds[1], str, sizeof(str));
    else{
       read(fds[0], buf, BUF_SIZE);
       puts(buf);
    }
    return 0;
}

The key point of the above code is that both parent and child processes can access the IO path of the pipeline, but the child process only uses the input path, and the parent process only uses the output path.

Two-way communication between processes through pipes

First give a sample code for a little discussion

#include<stdio.h>
#include<unistd.h>
#define BUF_SIZE 30

int main(int argc, char *argv[]){
    int fds[2];
    char str1[]="who are you?";
    char str2[]="Thank you for your message";
    char buf[BUF_SIZE];
    pid_t pid;

    pipe(fds);
    pid=fork();
    if(pid==0){
       write(fds[1], str1, sizeof(str1));
       sleep(2);
       read(fds[0], buf, BUF_SIZE);
       printf("Child proc output: %s \n",buf);
    }
    else{
       read(fds[0], buf, BUF_SIZE);
       printf("Parent proc output: %s \n",buf);
       write(fds[1], str2, sizeof(str2));
       sleep(3);
    }
return 0;
}

The running results should be consistent with everyone's expectations. This time comment the 18th line of code and run it again (be sure to do it yourself). Although
this line of code only delays the runtime by 2 seconds, it has thrown a runtime error. What is the reason?
"When passing data to the pipe, the process that reads first will take the data away."

Here, the monitor is an address space allocated by the operating system, which does not belong to those two processes. Then when two programs are running, one process puts content into this "no man's land". If the party that put it in does not wait for a few seconds but runs directly down, then the content it put in will be taken away by itself. After the time slice is exhausted, when it is another process' turn, it will be blocked indefinitely in the read function, thus generating an error.

As you can see from the above example, bi-directional communication with only 1 pipe is not trivial. In order to achieve this, the program needs to predict and control the flow of operation, which is different in each system and can be regarded as an impossible task. That being the case, how to carry out two-way communication?
"Create 2 pipelines."

It's very simple, one pipeline cannot complete the two-way communication task, so you need to create two pipelines, each responsible for different data flows
.

Below is the sample code

#include<stdio.h>
#include<unistd.h>
#define BUF_SIZE 30

int main(int argc, char *argv[]){
    int fds1[2], fds2[2];
    char stri[]="who are you?";
    char str2[]="Thank you for your message";
    char buf[BUF_SIZE];
    pidt pid;

    pipe(fds1), pipe(fds2);
    pid=fork();
    if(pid==0){
       write(fds1[1], str1, sizeof(str1));
       read(fds2[0], buf, BUF_SIZE);
       printf("Child proc output: %s \n", buf);
    }
    else{
       read(fds1[0], buf, BUF_SIZE);
       printf("Parent proc output: %s \n", buf);
       write(fds2[1], str2, sizeof(str2));
       sleep(3);
    }
return 0;
}

use interprocess communication

Echo server side that saves messages

Let’s expand the echo server in Chapter 10 and add the following function: “Save the strings transmitted by the echo client into files in sequence.”


I wish to delegate this task to another process. In other words, create a separate process and read character string information from the process that provides services to the client
. Of course, this process requires the creation of a pipeline for receiving data.
Examples are given below. This example will work with any echo client.

#include <'头声明与第10章的示例一致。'>
#define BUF_SIZE 100
void error_handling(char *message);
void read_childproc(int sig);

int main(int argc, char *argv[]){
    int serv_sock, clnt_sock;
    struct sockaddr_in serv_adr, cint_adr;
    int fds[2];
    pid_t pid;
    struct sigaction act;
    socklen_t adr_sz;
    int str_len, state;
    char buf[BUF_SIZE];

    if(argc!=2) {
       printf("Usage : %s <port>\n", argv[0]);
       exit(1);
    }

    act.sa_handler=read_childproc;
    sigemptyset(&act.sa_mask);
    act.sa_flags=0;
    state=sigaction(SIGCHLD, &act, 0);

    serv_sock=socket(PF_INET, SOCK_STREAM, 0);
    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family=AF_INET;
    serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
    serv_adr.sin_port=htons(atoi(argv[1]));

    if(bind(serv_sock,(struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1)
        error_handling("bind() error");
    if(listen(serv_sock,5)==-1)
        error_handiing("listen() error");

    pipe(fds);
    pid=fork();
    if(pid==0){
         FILE *fp=fopen("echomsg.txt","wt");
         char msgbuf[BUF_SIZE];
         int i, len;
         for(i=0;i<10;i++){
             len=read(fds[0], msgbuf, BUF_SIZE);
             fwrite((void*)msgbuf,1, len, fp);
         }
         fclose(fp);
         return 0;
    }


    while(1){
         adr_sz=sizeof(clnt_adr);
         clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_adr, &adr_sz);
         if(clnt_sock==-1)
             continue;
         else
             puts("new client connected...");
         pid=fork();
         if(pid==0){
             close(serv_sock);
             while((str_len=read(clnt_sock, buf, BUF_SIZE))!=0){
             write(clnt_sock, buf, str_len);//回声
             write(fds[1], buf, str_len);   //记录消息
         }
         close(clnt_sock);
         puts("client disconnected...");
         return 0;
         }
         else close(clnt_sock);
         }
         close(serv_sock);
         return 0;
}

void read_childproc(int sig){
//与上一章示例一致,故省略。
}

void error_handling(char* message){
//与上一章示例一致,故省略。
}

Guess you like

Origin blog.csdn.net/Reol99999/article/details/131748024