Namedpipe server client program with Epoll system call ======================================================= .. tab-set:: .. tab-item:: Namedpipe * In this program, you are going to learn .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow * How to open namedpipes for reading and writing ? .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow * How to read data ? .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow * How to write data ? .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow * How to use Namedpipe APIs ? * `mkfifo `_ * `open `_ * `epoll_create1 `_ * `epoll_ctl `_ * `epoll_wait `_ * `read `_ * `write `_ .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow Topics in this section, * :ref:`namedpipe socket FAQs ` * :ref:`Step 1: Sequence Diagram for SERVER.c ` * :ref:`Step 2: Program for Server.c ` * :ref:`Step 3: Compile and Execute Server.c ` * :ref:`Step 4: Sequence Diagram for CLIENT.c ` * :ref:`Step 5: Program for Client.c ` * :ref:`Step 6: Compile and Execute Client.c ` * :ref:`Summary ` .. _epoll_namedpipe: .. tab-set:: .. tab-item:: namedpipe : FAQs .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow Let us answer few basic questions in this namedpipe .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow What is the purpose of ``mkfifo`` in the code? .. dropdown:: See Answer ``mkfifo`` is used to create a named pipe with a specific path and permission mode. .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow Why is ``O_RDONLY`` used when opening server_fifo for the first time? .. dropdown:: See Answer ``O_RDONLY`` indicates that the server is opening the named pipe for reading, expecting input from a client. .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow What happens if the server tries to open a non-existent named pipe using ``O_RDONLY``? .. dropdown:: See Answer If the named pipe doesn't exist, the ``open`` call will block until another process creates it. This is common in inter-process communication. .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow Why is ``O_WRONLY`` used when reopening client_fifo for writing? .. dropdown:: See Answer After receiving data from the client, the server reopens the named pipe for writing (O_WRONLY) to send a response back. .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow What is the significance of using ``O_WRONLY`` for the second ``open`` call? .. dropdown:: See Answer It designates the server's intention to write to the named pipe, indicating a shift from reading to writing mode. .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow How does the server handle errors when opening the named pipe for reading or writing? .. dropdown:: See Answer The code checks the return value of the ``open`` call and prints an error message using ``perror`` if the operation fails. .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow How does the server handle cases where the named pipe is never opened for writing by the client? .. dropdown:: See Answer The server would block indefinitely at the ``write`` operation until the client opens the named pipe for reading. .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow What is the significance of using ``unlink(FIFO_PATH)`` in the signal handler? .. dropdown:: See Answer It removes the named pipe from the file system, ensuring that resources are properly cleaned up when the server exits. .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow Why are there multiple calls to ``close(server_fifo)`` and ``close(client_fifo)`` in the code? .. dropdown:: See Answer File descriptors need to be closed after usage to release system resources. Multiple calls ensure all relevant file descriptors are closed. .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow What is the primary purpose of the epoll system call? .. dropdown:: See Answer To efficiently monitor multiple file descriptors for I/O events .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow What types of file descriptors can be monitored using epoll? .. dropdown:: See Answer sockets, files, timerfd, socketpair, message_queue, Namedpipes and shared_memory. .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow What data structure is used by epoll to store events? .. dropdown:: See Answer Hash table .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow How do you handle errors when using the epoll system call? .. dropdown:: See Answer Check the return value for -1 to detect errors, Use perror to print error messages. .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow How does epoll handle a set of file descriptors with different states (e.g., reading, writing, exception)? .. dropdown:: See Answer Create the epoll Instance: Before monitoring file descriptors, the application creates an epoll instance using the epoll_create system call. .. code-block:: c int epoll_fd = epoll_create1(0); Register File Discriptors: The application registers file descriptors with the epoll instance using the epoll_ctl system call. It specifies the file descriptor, the events it is interested in (EPOLLIN for readability, EPOLLOUT for writability, etc.), and a user-defined data associated with the file descriptor. .. code-block:: c struct epoll_event event; event.events = EPOLLIN | EPOLLOUT; // Interested in readability and writability event.data.fd = my_file_descriptor; // File descriptor to monitor epoll_ctl(epoll_fd, EPOLL_CTL_ADD, my_file_descriptor, &event); Wait for Events: The application enters a loop where it calls epoll_wait to wait for events. This call blocks until one or more registered file descriptors become ready or until a timeout occurs. .. code-block:: c #define MAX_EVENTS 10 struct epoll_event events[MAX_EVENTS]; int num_events = epoll_wait(epoll_fd, events, MAX_EVENTS, timeout_ms); Modify or Remove File Descriptors: The application can dynamically modify or remove file descriptors from the epoll set using the epoll_ctl system call. For example, to modify events for an existing file descriptor: .. code-block:: c struct epoll_event new_event; new_event.events = EPOLLOUT; // Modify to be interested in writability epoll_ctl(epoll_fd, EPOLL_CTL_MOD, my_file_descriptor, &new_event); To remove a file descriptor from the epoll set: .. code-block:: c epoll_ctl(epoll_fd, EPOLL_CTL_DEL, my_file_descriptor, NULL); .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow How does epoll Checking Ready File Descriptors? .. dropdown:: See Answer After epoll_wait returns, the application iterates through the returned events to identify which file descriptors are ready and for what types of events. .. code-block:: c for (int i = 0; i < num_events; ++i) { if (events[i].events & EPOLLIN) { // File descriptor i is ready for reading } if (events[i].events & EPOLLOUT) { // File descriptor i is ready for writing } // Check other events if needed (e.g., EPOLLERR, EPOLLHUP) } .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow What does it mean if epoll returns 0? .. dropdown:: See Answer No file descriptors are ready within the specified timeout. .. _epoll_namedpipe_server_sequence_diagram: .. tab-set:: .. tab-item:: Step 1: Sequence Diagram for SERVER.c .. plantuml:: @startuml !theme spacelab start :mkfifo(FIFO_PATH, 0666); while (while(1)) is (yes) :server_fifo = open(FIFO_PATH, O_RDONLY); :epoll_fd = epoll_create1(0); :epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fifo, &event); :epoll_wait(epoll_fd, events, MAX_EVENTS, -1); if (events[0].data.fd == server_fifo) then (yes) :read(server_fifo, buffer, sizeof(buffer) - 1); :(void)close(server_fifo); :(void)close(epoll_fd); :client_fifo = open(FIFO_PATH, O_WRONLY); if (client_fifo != -1) then (yes) :write(client_fifo, buffer, strlen(buffer)); endif :(void)close(client_fifo); else (no) endif endwhile (CTRL+c) :(void)close(server_fifo); :(void)close(client_fifo); stop @enduml .. _epoll_namedpipe_server_code: .. tab-set:: .. tab-item:: Step 2: Program for Server.c * There are many functions used in namedpipe. We can classify those functions based on functionalities. * mkfifo * open * Epoll create1 * Epoll_ctl * Epoll_wait * read * write * close * ``mkfifo`` is used to create a named pipe at a specified path (FIFO_PATH) with a given permission mode. For example, .. code-block:: c mkfifo(FIFO_PATH, 0666); * ``open`` is used to opening the named pipe with a given permission mode. For example, .. code-block:: c server_fifo = open(FIFO_PATH, O_RDONLY); client_fifo = open(FIFO_PATH, O_WRONLY); * ``epoll_create1()`` creating an epoll instance using epoll_create1, The size parameter is an advisory hint for the kernel regarding the number of file descriptors expected to be monitored, For example, .. code-block:: c epoll_fd = epoll_create1(0)); * ``epoll_ctl()`` After creating an epoll instance, file descriptors are added to it using epoll_ctl, For example, .. code-block:: c ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fifo, &event); * ``epoll_wait()`` The application then enters a loop where it waits for events using epoll_wait, For example, .. code-block:: c ret = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); * ``read`` is used to read data from the named pipe, expecting input from a client. For example, .. code-block:: c 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, .. code-block:: c ret = write(client_fifo, buffer, strlen(buffer)); * ``close`` is used to close the namepipe To free up system resources associated with the namedpipe. For example, .. code-block:: c (void)close(server_fifo); (void)close(client_fifo); * See the full program below, .. literalinclude:: Namedpipe/server/server.c :language: c :emphasize-lines: 54, 57, 58, 68, 78, 79, 86, 87, 95, 96, 112, 113, 116, 117, 126, 130, 131 .. _epoll_namedpipe_server_side_compile_and_execute: .. tab-set:: .. tab-item:: Step 3: Compile and Execute Server.c .. code-block:: c :linenos: :emphasize-lines: 1, 3 $ gcc -o server server.c $ sudo ./server Server is ready ---------------- Received : Hello from client! sent = Hello from server! Server is ready ---------------- Received : Hello from client! sent = Hello from server! Server is ready ---------------- Received : Hello from client! sent = Hello from server! Server is ready ---------------- Received : Hello from client! sent = Hello from server! Server is ready ---------------- Received : Hello from client! sent = Hello from server! ^CCaught sigINT! .. _epoll_namedpipe_client_sequence_diagram: .. tab-set:: .. tab-item:: Step 4: Sequence Diagram for CLIENT.c .. plantuml:: @startuml !theme spacelab start while (while(1)) is (yes) :client_fifo = open(FIFO_PATH, O_WRONLY); :write(client_fifo, buffer, sizeof(buffer) - 1); :(void)close(client_fifo); :client_fifo = open(FIFO_PATH, O_RDONLY); :epoll_fd = epoll_create1(0); :epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fifo, &event); :epoll_wait(epoll_fd, events, MAX_EVENTS, -1); if (events[0].data.fd == server_fifo) then (yes) :read(server_fifo, buffer, sizeof(buffer) - 1); :(void)close(server_fifo); :(void)close(epoll_fd); else (no) endif endwhile (CTRL+c) :(void)close(server_fifo); :(void)close(client_fifo); stop @enduml .. _epoll_namedpipe_client_code: .. tab-set:: .. tab-item:: Step 5: Program for Client.c * There are many functions used in namedpipe. We can classify those functions based on functionalities. * open * write * Epoll create1 * Epoll_ctl * Epoll_wait * read * close * ``open`` is used to opening the named pipe with a given permission mode. For example, .. code-block:: c client_fifo = open(FIFO_PATH, O_WRONLY); server_fifo = open(FIFO_PATH, O_RDONLY); * ``write`` is used to write a data to the named pipe, writing input to the client. For example, .. code-block:: c ret = write(client_fifo, buffer, sizeof(buffer) - 1); * ``epoll_create1()`` creating an epoll instance using epoll_create1, The size parameter is an advisory hint for the kernel regarding the number of file descriptors expected to be monitored, For example, .. code-block:: c epoll_fd = epoll_create1(0)); * ``epoll_ctl()`` After creating an epoll instance, file descriptors are added to it using epoll_ctl, For example, .. code-block:: c ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fifo, &event); * ``epoll_wait()`` The application then enters a loop where it waits for events using epoll_wait, For example, .. code-block:: c ret = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); * ``read`` is used to read data from the named pipe, expecting input from a client. For example, .. code-block:: c ret = read(server_fifo, buffer, sizeof(buffer) - 1); * ``close`` is used to close the namepipe To free up system resources associated with the namedpipe. For example, .. code-block:: c (void)close(server_fifo); (void)close(client_fifo); * See the full program below, .. literalinclude:: Namedpipe/client/client.c :language: c :emphasize-lines: 55, 56, 69, 70, 78, 81, 82, 89, 99, 100, 107, 108, 119, 120, 130, 131, 135, 136 .. _epoll_namedpipe_client_side_compile_and_execute: .. tab-set:: .. tab-item:: Step 6: Compile and Execute Client.c .. code-block:: c :linenos: :emphasize-lines: 1, 3 $ gcc -o client client.c $ sudo ./client Client is ready ---------------- sent = Hello from client! Received: Hello from server! Client is ready ---------------- sent = Hello from client! Received: Hello from server! Client is ready ---------------- sent = Hello from client! Received: Hello from server! Client is ready ---------------- sent = Hello from client! Received: Hello from server! Client is ready ---------------- sent = Hello from client! Received: Hello from server! ^CCaught sigINT! .. _epoll_namedpipe_summary: .. tab-set:: .. tab-item:: Summary =============== ========================================================================================== 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. epoll handles a set of file descriptors with different states, such as reading, writing, and exceptions, by using the struct epoll_event structure and the associated event flags.. write write a data to the named pipe, writing input to the client. =============== ========================================================================================== .. card:: See Also * Previous topic * :doc:`../../../Message_queues/Message_queues` * Current topic * :doc:`../../../NamedPipes/NamedPipes` * Other IPCs * :doc:`../../../Netlink/Netlink` * :doc:`../../../Shared_Memory/Shared_Memory` * :doc:`../../../Shared_Memory_2_FDS/Shared_Memory_2_FDS` * :doc:`../../../SocketPair/SocketPair` * :doc:`../../../Timerfd/Timerfd`