Basic example Namedpipe server and client

  • In this program, you are going to learn

  • How to open namedpipes for reading and writing ?

  • How to read data ?

  • How to write data ?

Let us answer few basic questions in this namedpipe

What is the purpose of mkfifo in the code?

Why is O_RDONLY used when opening server_fifo for the first time?

What happens if the server tries to open a non-existent named pipe using O_RDONLY?

Why is O_WRONLY used when reopening server_fifo for writing?

What is the significance of using O_WRONLY for the second open call?

How does the server handle errors when opening the named pipe for reading or writing?

How does the server handle cases where the named pipe is never opened for writing by the client?

What is the significance of using unlink(FIFO_PATH) in the signal handler?

Why are there multiple calls to close(server_fifo) in the code?

https://www.plantuml.com/plantuml/svg/ZP1F2u9044Vl-occsme6TV5W10OX1P4XGNIILMTfQNLbTZNeqwVz0Ji47OVtVkymaNM2N5jfDd4Nh11i8mhK8cTFmCBgAfKqFDx6INPO7JS-p88Wy1Oij4WTKZPWM89fi1vRIPQka_tkF9Y4ekGZtOUybHB91wlkQ2H_dHvCOVvF-PH-oZTI3d-a7Ub--4uFCk-CAhr26_irwP5rfc4HrcN_Y0S=
  • There are many functions used in namedpipe. We can classify those functions based on functionalities.

    • mkfifo

    • open

    • read

    • write

    • close

  • mkfifo is used to create a named pipe at a specified path (FIFO_PATH) with a given permission mode. For example,

mkfifo(FIFO_PATH, 0666);
  • open is used to opening the named pipe with a given permission mode. For example,

server_fifo = open(FIFO_PATH, O_RDONLY);
server_fifo = open(FIFO_PATH, O_WRONLY);
  • read is used to read data from the named pipe, expecting input from a client. For example,

ret = read(server_fifo, buffer, sizeof(buffer) - 1);
  • write is used to write a data to the named pipe, writing input to the client. For example,

ret = write(server_fifo, buffer, strlen(buffer));
  • close is used to close the namepipe To free up system resources associated with the namedpipe. For example,

(void)close(server_fifo);
  • See the full program below,

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>

#define FIFO_PATH "/tmp/my_named_pipe"
#define BUFFER_SIZE 1024

int main(void) 
{
  int ret;
  int server_fifo;
  char buffer[BUFFER_SIZE];

  mkfifo(FIFO_PATH, 0666);

  server_fifo = open(FIFO_PATH, 
  O_RDONLY);

  if (server_fifo == -1) {
    perror("open server FIFO");
    return -1;
  }

  printf("Server is ready\n");

  ret = read(server_fifo, 
  buffer, sizeof(buffer) - 1);

  if (ret < 0) {
     (void)close(server_fifo);
     unlink(FIFO_PATH);
     printf("Server is shutting down.\n");
     return -2;
  } else {
     buffer[ret] = '\0';
     printf("Received : %s\n", 
     buffer);
     (void)close(server_fifo);
  }

  snprintf(buffer, 
  sizeof(buffer), "Hello from server!\n");

  server_fifo = open(FIFO_PATH, 
  O_WRONLY);
    
  if (server_fifo != -1) {
      ret = write(server_fifo, 
      buffer, strlen(buffer));
        
      if (ret == -1) {
         perror("write error");
         (void)close(server_fifo);
         return -3;
      }
      printf("sent = %s\n", buffer);
  }
  (void)close(server_fifo);
    
  return 0;
}

1$ gcc -o server server.c
2
3$ sudo ./server
4
5Server is ready
6Received : Hello from client!
7
8sent = Hello from server!
https://www.plantuml.com/plantuml/svg/SoWkIImgAStDuL8ioKZDJLKeBaXCJir9JE82It1P9UTafkQLnATbfkKhs2ha5wJcQRXvkld71pY6UEWe-CU71_dx-KHgMdDPbHTbbgHg8AdNKKWgJKjBBT9HACwiIirFqu1mDHLq5Gn16WWP7UG2CxeeDJ6559Crol8pKpIJS_ABKHm4b2ekoI_WSaZDIm5w6G0=
  • There are many functions used in namedpipe. We can classify those functions based on functionalities.

    • open

    • write

    • read

    • close

  • open is used to opening the named pipe with a given permission mode. For example,

client_fifo = open(FIFO_PATH, O_WRONLY);
client_fifo = open(FIFO_PATH, O_RDONLY);
  • write is used to write a data to the named pipe, writing input to the client. For example,

ret = write(client_fifo, buffer, sizeof(buffer) - 1);
  • read is used to read data from the named pipe, expecting input from a client. For example,

ret = read(client_fifo, buffer, sizeof(buffer) - 1);
  • close is used to close the namepipe To free up system resources associated with the namedpipe. For example,

(void)close(client_fifo);
  • See the full program below,

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>

#define FIFO_PATH "/tmp/my_named_pipe"
#define BUFFER_SIZE 1024

int main(void) 
{
  int ret;
  int client_fifo;
  char buffer[BUFFER_SIZE];

  client_fifo = open(FIFO_PATH, 
  O_WRONLY);
  
  if (client_fifo == -1) {
    perror("open client FIFO");
    return -1;
  }

  printf("Client is ready\n");
        
  snprintf(buffer, 
  sizeof(buffer), "Hello from client!\n");
        
  ret = write(client_fifo, 
  buffer, sizeof(buffer) - 1);
        
  if (ret < 0) {
    printf("Client is shutting down.\n");
    (void)close(client_fifo);
    return -2;
  } else {
    buffer[ret] = '\0';
    printf("sent = %s\n", buffer);
    (void)close(client_fifo);
  }
    
  client_fifo = open(FIFO_PATH, 
  O_RDONLY);
    
  if (client_fifo == -1) {
    perror("open FIFO error");
    return -4;
  }

  memset(buffer, 0, 
  sizeof(buffer));
  
  ret = read(client_fifo, 
  buffer, sizeof(buffer) - 1);
        
  if (ret < 0) {
    (void)close(client_fifo);
    printf("Client is shutting down.\n");
    return -5;
  } else {
    buffer[ret] = '\0';
    printf("Received: %s", 
    buffer);
  }        
  (void)close(client_fifo);
  
  return 0;
}
1$ gcc -o client client.c
2
3$ sudo ./client
4
5Client is ready
6sent = Hello from client!
7
8Received: Hello from server!
https://www.plantuml.com/plantuml/svg/XP31Qi9048Rl-nJJsowce9SS4WiArIfKKumYFOLePidW9XjsLwLz-cRJ5lIWdeRX__uFPaRMvSOTAyMUN8aLWctoFQfynxg0HTL1ajHyEfycsSTuFGkW7uQXY5b4y0Bzc9rBKWYy6vnW2ClD8bjCqdJyDac50BB0lz0A5baq9pIPzxLLtM1zgKsorMkoVF_qQeDvmI_m07P7AT44OEaRjUI_gu1d67Z-aNcx-ZUV3JcyeNP6jSK_jOVvILCXzahRgugFUZsA6TP5T_OzqZhTi55BjZ_-0G==
  • There are many functions used in namedpipe. We can classify those functions based on functionalities.

    • mkfifo

    • open

    • read

    • write

    • close

  • mkfifo is used to create a named pipe at a specified path (FIFO_PATH) with a given permission mode. For example,

mkfifo(FIFO_PATH, 0666);
  • open is used to opening the named pipe with a given permission mode. For example,

server_fifo = open(FIFO_PATH, O_RDONLY);
server_fifo = open(FIFO_PATH, O_WRONLY);
  • read is used to read data from the named pipe, expecting input from a client. For example,

ret = read(server_fifo, buffer, sizeof(buffer) - 1);
  • write is used to write a data to the named pipe, writing input to the client. For example,

ret = write(server_fifo, buffer, strlen(buffer));
  • close is used to close the namepipe To free up system resources associated with the namedpipe. For example,

(void)close(server_fifo);
  • See the full program below,

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>

#define FIFO_PATH "/tmp/my_named_pipe"
#define BUFFER_SIZE 1024
#define NUM_MESSAGES 10


int main(void) 
{
  int ret, i;
  int server_fifo;
  char buffer[BUFFER_SIZE];

  mkfifo(FIFO_PATH, 0666);

  i = 0;
  while (i < NUM_MESSAGES) {

  server_fifo = open(FIFO_PATH, 
  O_RDONLY);

  if (server_fifo == -1) {
    perror("open server FIFO");
    break;
  }

  printf("Server %d is ready\n",
  i);

  ret = read(server_fifo, 
  buffer, sizeof(buffer) - 1);

  if (ret < 0) {
     perror("read error");
     break;
  } else {
     buffer[ret] = '\0';
     printf("Received : %s\n", 
     buffer);
     (void)close(server_fifo);
  }

  snprintf(buffer, 
  sizeof(buffer), "Hello from server!");

  server_fifo = open(FIFO_PATH, 
  O_WRONLY);
    
  if (server_fifo != -1) {
      ret = write(server_fifo, 
      buffer, strlen(buffer));
        
      if (ret == -1) {
         perror("write error");
         break;
      }
      printf("sent = %s\n", buffer);
  }
  (void)close(server_fifo);
  ++i;
  }
  (void)close(server_fifo);

  return 0;
}

 1$ gcc -o server server.c
 2
 3$ sudo ./server
 4
 5Server 0 is ready
 6Received : Hello from client!
 7sent = Hello from server!
 8Server 1 is ready
 9Received : Hello from client!
10sent = Hello from server!
11Server 2 is ready
12Received : Hello from client!
13sent = Hello from server!
14Server 3 is ready
15Received : Hello from client!
16sent = Hello from server!
17Server 4 is ready
18Received : Hello from client!
19sent = Hello from server!
20Server 5 is ready
21Received : Hello from client!
22sent = Hello from server!
23Server 6 is ready
24Received : Hello from client!
25sent = Hello from server!
26Server 7 is ready
27Received : Hello from client!
28sent = Hello from server!
29Server 8 is ready
30Received : Hello from client!
31sent = Hello from server!
32Server 9 is ready
33Received : Hello from client!
34sent = Hello from server!
https://www.plantuml.com/plantuml/svg/bP1D2y8m38Rl-HNnrk84lMuA2dw2Ed6AU1fpIp5Grx5MHN-zSoBeIJo5aETzIDAtBYdT-QIXuOvq8hH5af9E3b0Fm6ViOJk0wv4reQYBOEpYShk85wCe6an6aPJ85iMDh0G_rKovYnKhKqLDGRaOpyPXl1fifXw6yMuTBkTx6O1_BTcH-E0zF9oLej93otSoIhnQYItiF0E_rElXMrrIalrZRZOv0Cgp-ZxmnSLm9bDjxDT-5McTAQ1VaTNB7W==
  • There are many functions used in namedpipe. We can classify those functions based on functionalities.

    • open

    • write

    • read

    • close

  • open is used to opening the named pipe with a given permission mode. For example,

client_fifo = open(FIFO_PATH, O_WRONLY);
client_fifo = open(FIFO_PATH, O_RDONLY);
  • write is used to write a data to the named pipe, writing input to the client. For example,

ret = write(client_fifo, buffer, sizeof(buffer) - 1);
  • read is used to read data from the named pipe, expecting input from a client. For example,

ret = read(client_fifo, buffer, sizeof(buffer) - 1);
  • close is used to close the namepipe To free up system resources associated with the namedpipe. For example,

(void)close(client_fifo);
  • See the full program below,

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>

#define FIFO_PATH "/tmp/my_named_pipe"
#define BUFFER_SIZE 1024
#define NUM_MESSAGES 10

int main(void) 
{
  int ret, i;
  int client_fifo;
  char buffer[BUFFER_SIZE];

  i = 0;
  while (i < NUM_MESSAGES) {

  client_fifo = open(FIFO_PATH, 
  O_WRONLY);
  
  if (client_fifo == -1) {
    perror("open client FIFO");
    break;
  }

  printf("Client %d is ready\n",
  i);
        
  snprintf(buffer, 
  sizeof(buffer), "Hello from client!");
        
  ret = write(client_fifo, 
  buffer, sizeof(buffer) - 1);
        
  if (ret < 0) {
    perror("write error");
    break;
  } else {
    buffer[ret] = '\0';
    printf("sent = %s\n", buffer);
    (void)close(client_fifo);
  }
    
  client_fifo = open(FIFO_PATH, 
  O_RDONLY);
    
  if (client_fifo == -1) {
    perror("open FIFO error");
    break;
  }

  memset(buffer, 0, 
  sizeof(buffer));
  
  ret = read(client_fifo, 
  buffer, sizeof(buffer) - 1);
        
  if (ret < 0) {
    perror("read error");
    break;
  } else {
    buffer[ret] = '\0';
    printf("Received: %s\n", 
    buffer);
  }
  ++i;
  }
  (void)close(client_fifo);

  return 0;
}
 1$ gcc -o client client.c
 2
 3$ sudo ./client
 4
 5Client 0 is ready
 6sent = Hello from client!
 7Received: Hello from server!
 8Client 1 is ready
 9sent = Hello from client!
10Received: Hello from server!
11Client 2 is ready
12sent = Hello from client!
13Received: Hello from server!
14Client 3 is ready
15sent = Hello from client!
16Received: Hello from server!
17Client 4 is ready
18sent = Hello from client!
19Received: Hello from server!
20Client 5 is ready
21sent = Hello from client!
22Received: Hello from server!
23Client 6 is ready
24sent = Hello from client!
25Received: Hello from server!
26Client 7 is ready
27sent = Hello from client!
28Received: Hello from server!
29Client 8 is ready
30sent = Hello from client!
31Received: Hello from server!
32Client 9 is ready
33sent = Hello from client!
34Received: Hello from server!

Socket API

Learning

mkfifo

create a named pipe at a specified path (FIFO_PATH) with a given permission mode.

open

opening the named pipe with a given permission mode.

read

read data from the named pipe, expecting input from a client.

write

write a data to the named pipe, writing input to the client.

See Also