0729------Linux Network programming----------writing client programs using the Select, poll, and Epoll models

Source: Internet
Author: User
Tags epoll readline stdin htons

1.select Models

The 1.1 Select function is prototyped as follows, where Nfds represents the maximum value of the Descriptor plus 1 (because this is the left-right open interval), and the middle three parameters represent a collection of different types of descriptors to listen to, timeout is used to represent the polling interval, where null means infinite wait.

    

1.2 General steps for writing a client program using the Select function:

A) initialize the parameters, including initializing the listener set Read_set and adding the FD, as well as initializing the listener's maximum descriptor maxfd and the return value of select Nready;

b) Assign the Read_set to Ready_set, since each execution of the SELECT function Listener will change, Read_set is used to save the original collection;

c) Perform a select call to check the return value;

d) Check FD sequentially and perform different operations respectively;

1.3 Sample program. 

#include "def.h" #include <sys/select.h>/* * Write client program using SELECT Model * */void do_service (int peerfd); int main (int argc, CO    NST Char *argv[]) {//1. create socket int PEERFD = socket (af_inet, sock_stream, 0);   if (PEERFD = =-1) err_exit ("socket"); 2.    Conncet struct sockaddr_in peeraddr;    peeraddr.sin_family = af_inet;    PEERADDR.SIN_ADDR.S_ADDR = inet_addr ("127.0.0.1");    Peeraddr.sin_port = htons (9999);    if (Connect (peerfd, struct sockaddr*) &peeraddr, sizeof (peeraddr)) = =-1) err_exit ("Connect");    Do_service (PEERFD);    Close (PEERFD); return 0;}    void Do_service (int peerfd) {char sendbuf[1024] = {0};    Char recvbuf[1024] = {0};    1. Initialize the array, add the descriptor to listen fd_set Read_set, Ready_set;    Fd_zero (&read_set);    Fd_set (Stdin_fileno, &read_set);    Fd_set (PEERFD, &read_set); int maxfd = Stdin_fileno > PEERFD?    STDIN_FILENO:PEERFD; int nready;        Receive the return value of Select//2. Perform the listening while (1) {ready_set = Read_set; Nready = SeleCT (maxfd + 1, &ready_set, NULL, NULL, NULL);            if (Nready = =-1) {if (errno = = eintr) continue;        Err_exit ("select");        } else if (Nready = = 0) continue; 3. Check each FD in turn to see if (Fd_isset (Stdin_fileno, &ready_set)) {if (fgets (sendbuf, sizeof (SENDBUF), STDIN) in the collection                ) = = NULL) {shutdown (PEERFD, SHUT_WR);            Removes the FD fd_clr (Stdin_fileno, &read_set) from the Listener collection;        } writen (Peerfd, SendBuf, strlen (SENDBUF));            } if (Fd_isset (PEERFD, &ready_set)) {int ret = ReadLine (PEERFD, Recvbuf, sizeof (RECVBUF));            if (ret = =-1) err_exit ("ReadLine");                else if (ret = = 0) {close (PEERFD);                printf ("Server closed\n");            Break        } printf ("Recv Data:%s", recvbuf); }    }}

  

2.poll Models

2.1 Poll function

The 2.1.1 poll function is prototyped as follows, the first parameter is the descriptor array to listen to, the second is the length of the array, and the third represents the polling interval (a negative value indicates an infinite wait).

    

the first parameter structure of the 2.1.2 Poll is as follows, FD is the descriptor to listen, events represents the waiting event, we generally use pollin,revents is an outgoing parameter, we use it to determine whether the waiting event occurs.

     

2.2 General steps for writing a client program using poll:

A) Prepare the array (that is, the first parameter of the poll function), fill in the corresponding FD and events, define maxi (the maximum value of the array subscript) and Nready (receive poll return value) variable;

b) Enter the while (1) loop, execute the poll function, check its return value;

c) Check two FD through the Revents field;

2.3 Sample program.

#include "def.h"/* * Use poll model to write client program * */void do_service (int peerfd); int main (int argc, const char *argv[]) {//1. Create so    cket int PEERFD = socket (af_inet, sock_stream, 0);   if (PEERFD = =-1) err_exit ("socket"); 2.    Conncet struct sockaddr_in peeraddr;    peeraddr.sin_family = af_inet;    PEERADDR.SIN_ADDR.S_ADDR = inet_addr ("127.0.0.1");    Peeraddr.sin_port = htons (9999);    if (Connect (peerfd, struct sockaddr*) &peeraddr, sizeof (peeraddr)) = =-1) err_exit ("Connect");    Do_service (PEERFD);    Close (PEERFD); return 0;}    void Do_service (int peerfd) {char sendbuf[1024] = {0};    Char recvbuf[1024] = {0};    1. Initialize descriptor array struct POLLFD fds[2]; FDS[0].FD = Stdin_fileno;//fileno (STDIN);    //?    Fds[0].events = Pollin;    FDS[1].FD = PEERFD;    Fds[1].events = Pollin; int maxi = 1; The array's maximum subscript int nready; Receive the return value of the poll//2. Execute poll, check the return value while (1) {Nready = poll (FDS, Maxi + 1,-1);//Negative value indicates a permanent wait if (Nready = = 1 ) {if (errno= = Eintr) continue;        Else Err_exit ("poll");        } else if (Nready = = 0) continue;                3. Check if (fds[0].revents & Pollin) {//bit operation if (fgets (sendbuf, sizeof (SENDBUF), stdin) = = NULL) { Shutdown (PEERFD, SHUT_WR); Closing the write end can still read data from the socket FDS[0].FD =-1;        No longer listens for Stdin_fileno} else writen (PEERFD, SendBuf, strlen (SENDBUF));            } if (Fds[1].revents & pollin) {int ret = ReadLine (PEERFD, RECVBUF, 1024);            if (ret = =-1) {Err_exit ("ReadLine");                } else if (ret = = 0) {//peer-closed connection printf ("Server closed\n");                Close (PEERFD);            Break        } printf ("Recv Data:%s", recvbuf); }    }}

3.epoll Models

Functions commonly used in 3.1 epoll models.

The 3.1.1 Epoll_create function, which is used to create a epoll handle, is the size of the handle, which is the number of descriptors to listen on. For example, if we were to listen for Stdin_fileno and PEERFD, the value of passing in size would be 2.

      

     3.1.2 The Epoll_ctl function, depending on the second parameter, performs a different operation, a commonly used to register a descriptor and remove a descriptor from the handle, where the second parameter is Epoll_ctl_add and Epoll_ctl_del, respectively, Note that each of these new (or removed) FD is associated with a struct epoll_event struct. The structure is as follows:

      

      

The 3.1.3 epoll_wait function is used to wait for the prepared FD. Returns the number of prepared FD.

      

3.2 The general steps of the Epoll function to write the client program:

A) Create a EPOLLFD handle using the epoll_create function;

b) Use the epoll_ctl function to register the descriptor to be monitored in the EPOLLFD;

c) prepare an array of events (outgoing parameter, Epoll_wait's third parameter) to receive the event that needs to be handled;

D) Enter the while (1) loop and execute the epoll_wait function, traversing the events array in turn based on the return value Nready (the number of FD prepared), for processing.

3.3 program example.

#include "def.h" #include <sys/epoll.h>/* * Write client program using Epoll model * */void do_service (int peerfd); int main (int argc, cons    T Char *argv[]) {//1. create socket int PEERFD = socket (af_inet, sock_stream, 0);   if (PEERFD = =-1) err_exit ("socket"); 2.    Conncet struct sockaddr_in peeraddr;    peeraddr.sin_family = af_inet;    PEERADDR.SIN_ADDR.S_ADDR = inet_addr ("127.0.0.1");    Peeraddr.sin_port = htons (9999);    if (Connect (peerfd, struct sockaddr*) &peeraddr, sizeof (peeraddr)) = =-1) err_exit ("Connect");    Do_service (PEERFD);    Close (PEERFD); return 0;}    void Do_service (int peerfd) {char sendbuf[1024] = {0};    Char recvbuf[1024] = {0};    1. Generate a Epoll handle size of 2 because you want to listen Stdin_fileno and peerfd int epollfd = epoll_create (2);    if (EPOLLFD = =-1) err_exit ("Epoll_create");    2. Register the FD struct epoll_event ev to be monitored in the EPOLLFD;    Ev.events = Pollin;    EV.DATA.FD = Stdin_fileno; if (Epoll_ctl (EPOLLFD, Epoll_ctl_add, Stdin_fileno, &ev) = =-1) err_exIT ("Epoll_ctl");    Ev.events = Pollin;    EV.DATA.FD = PEERFD;    if (Epoll_ctl (EPOLLFD, Epoll_ctl_add, PEERFD, &ev) = =-1) err_exit ("Epoll_ctl"); 3. Prepare an array struct epoll_event events[2];//to store the prepared FD information int nready;            The return value of the Receive epoll_wait while (1) {Nready = Epoll_wait (EPOLLFD, events, 2,-1);//-1 Infinite wait if (Nready = = 1) {            if (errno = = eintr) continue;        else Err_exit ("epoll_wait");        } else if (Nready = = 0) continue;        4. Start checking each FD int i;            for (i = 0; i < Nready; i++) {int FD = EVENTS[I].DATA.FD; if (fd = = Stdin_fileno) {if (fgets (sendbuf, sizeof (SENDBUF), STDIN) = = NULL) {Shutdown (pee                    RFD, SHUT_WR);                    Remove this fd struct epoll_event ev;                    EV.DATA.FD = Stdin_fileno;                    if (Epoll_ctl (EPOLLFD, Epoll_ctl_del, Stdin_fileno, &ev) = =-1)    Err_exit ("Epoll_ctl");                } else{writen (PEERFD, SendBuf, strlen (SENDBUF));                }} if (fd = = PEERFD) {int ret = ReadLine (PEERFD, RECVBUF, 1024);                if (ret = =-1) err_exit ("ReadLine");                    else if (ret = = 0) {close (PEERFD);                    printf ("Server closed\n");                Exit (exit_success);            } printf ("Recv Data:%s", recvbuf); }        }    }}

4. Summary (transferred from http://www.cnblogs.com/bigwangdi/p/3182958.html)

a)the file descriptor monitored by the Select function is divided into 3 classes, Writefds, Readfds, and Exceptfds, respectively. After the call, the Select function blocks until a description is ready (with data readable, writable, or except), or timed out (timeout Specifies the wait time, and if the return is set to null immediately), the function returns. When the Select function returns, you can find the ready descriptor by traversing Fdset.

Select is currently supported on almost all platforms, and its good cross-platform support is one of its advantages. A disadvantage of select is that the maximum number of file descriptors that a single process can monitor is 1024 on Linux, which can be improved by modifying the macro definition or even recompiling the kernel, but this also results in a decrease in efficiency.

b) The POLLFD structure contains the event to be monitored and the eventreturned by the function, and no longer uses the select "parameter-value" delivery method. At the same time, POLLFD does not have the maximum number of limits (but the performance will also decrease if the number is too large). As with the Select function, poll returns, you need to poll the POLLFD to get the ready descriptor.

From the above, select and poll need to traverse the file descriptor to get a ready socket after returning. In fact, a large number of clients connected at the same time may only be in a very small state of readiness at a time, so their efficiency will decrease linearly as the number of monitored descriptors increases.

c) epoll model is mainly epoll_create,epoll_ctl and epoll_wait three functions. The Epoll_create function creates a Epoll file descriptor, and the parameter size does not limit the maximum number of descriptors that epoll can listen to, but is a recommendation for the kernel to initially allocate internal data structures. The return is a Epoll descriptor. -1 indicates that the creation failed. The Epoll_ctl controls the OP operation on the specified descriptor FD, which is the listener event associated with the FD. There are three OP operations: Add Epoll_ctl_add, delete Epoll_ctl_del, modify Epoll_ctl_mod. Add, delete, and modify the listener events for FD, respectively. Epoll_wait waits for an IO event on EPFD and returns up to Maxevents events.

  In Select/poll, the kernel scans all monitored file descriptors only after a certain method is called , and Epoll registers a file descriptor beforehand with Epoll_ctl (), once it is ready based on a file descriptor, The kernel uses a callback mechanism like callback to quickly activate the file descriptor and be notified when the process calls Epoll_wait ().

The main advantages of epoll are a few aspects:

1. The number of monitored descriptors is unrestricted, it supports the maximum number of open files, this number is generally far greater than 2048, for example, in the 1GB memory of the machine is about 100,000, the specific number can be cat/proc/sys/fs/file-max to see, In general, this number is very much related to system memory. The biggest disadvantage of select is that there is a limit to the number of FD that the process opens. This is not sufficient for servers with a larger number of connections. Although it is possible to choose a multi-process solution (as Apache does), although the cost of creating a process on Linux is relatively small, it is still not negligible, and data synchronization between processes is far less efficient than synchronization between threads, so it is not a perfect solution.

2. The efficiency of IO does not decrease as the number of monitored FD increases. Epoll is not the same as select and poll polling, but is implemented by each FD-defined callback function. Only the ready FD will execute the callback function.

3. Support for level triggering and edge triggering (just tell the process which file descriptor has just become ready, it only says it again, if we do not take action, then it will not be told again, this way is called edge triggering) two ways, the theoretical edge trigger performance is higher, but the code implementation is quite complex.

4.mmap accelerates the transfer of information between the kernel and user space. The Epoll is mmap the same piece of memory through the kernel in the user space, avoiding the fearless memory copy.

5. Appendix (other code)

#ifndef __def_h__#define __def_h__#include <stdio.h> #include <stdlib.h> #include <string.h># Include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <poll.h> #include <signal.h> #define ERR_EXIT (M        ) do {perror (M);    Exit (Exit_failure);  }while (0) ssize_t readn (int fd, void *usrbuf, size_t n), ssize_t writen (int fd, void *usrbuf, size_t n); ssize_t readline (int SOCKFD, void *usrbuf, size_t maxline); ssize_t readpeek (int sockfd, void *usrbuf, size_t n); #endif # include "def.h"/* * after encapsulation    The Read class function * */ssize_t readn (int fd, void *usrbuf, size_t n) {size_t nleft = n;    char *bufptr = usrbuf;    ssize_t nread;        while (Nleft > 0) {nread = Read (FD, bufptr, nleft);            if (nread = =-1) {if (errno = = eintr) continue;        else return-1; } else if (nread = = 0) bReak;        BufPtr + = Nread;    Nleft-= nread; } return (N-nleft);}    ssize_t writen (int fd, void *usrbuf, size_t n) {size_t nleft = n;    char *bufptr = usrbuf;    size_t Nwrite;        while (Nleft > 0) {nwrite = write (fd, bufptr, nleft);            if (nwrite <= 0) {if (errno = = eintr) nwrite = 0;        else return-1;        } bufptr + = Nwrite;    Nleft-= Nwrite; } return n;} Recv_peek completes the correct read process ssize_t recv_peek (int sockfd, void *usrbuf, size_t n) {/////////////////Data pre-read int recv with Msg_peek function ;        Receive the return value of the RECV function while (1) {//use while loop because it is continue when interrupted;        Recv is only successfully called once nread = Recv (SOCKFD, Usrbuf, N, Msg_peek);            if (Nread < 0) {if (errno = = eintr) continue;        else return-1;    } break; } return nread;}    ssize_t readline (int sockfd, void *usrbuf, size_t maxline) {char *bufptr = usrbuf;  size_t nleft = maxline-1;  ssize_t ret;    ssize_t nread; size_t total = 0;        while (Nleft > 0) {//preview data does not take data away from the buffer ret = recv (SOCKFD, BufPtr, Nleft, Msg_peek);        if (ret <= 0)//The bytes read here are not error return ret;        int i;        Nread = ret;  for (i = 0; i < nread; i++) {if (bufptr[i] = = ' \ n ') {//TRUNCATE RET = READN when encountering \ n (sockfd, bufptr, i +                1);                if (ret! = i+1)//Not read enough error return-1; Total + = ret;                If a pre-read does not contain \ n At this point, use total to save a number of bytes read BufPtr + = ret;                *bufptr = 0;            return total;        }}//did not find \ nthe all received ret = READN (SOCKFD, BufPtr, nread);        if (ret! = nread) return-1;        Total + = Nread;        Nleft-= nread;    BufPtr + = Nread;    } *bufptr = 0; return maxline-1;} #include "def.h" int listenfd_init (); Returns a socket descriptor in the Listening state void do_service (int peerfd);    Request to process client int main (int argc, const char *argv[]) {if (Signal (sigpipe, sig_ign) = = Sig_err) err_exit ("signal"); int listenfd = Listenfd_init ();    Generates a socket and puts it in a listening state struct sockaddr_in peeraddr;    int len = sizeof (PEERADDR);    int peerfd;    if (Peerfd = Accept (LISTENFD, (struct sockaddr*) &peeraddr, &len)) = =-1)//accept a TCP connection request Err_exit ("Accpet"); Do_service (PEERFD);    Processing request close (PEERFD);    Close (LISTENFD); return 0;}    int Listenfd_init () {int LISTENFD = socket (af_inet, sock_stream, 0);    int on = 1;    if (setsockopt (LISTENFD, Sol_socket, so_reuseaddr, &on, sizeof (ON)) = =-1) err_exit ("setsockopt");    struct sockaddr_in serveraddr;    serveraddr.sin_family = af_inet;    SERVERADDR.SIN_ADDR.S_ADDR = htonl (Inaddr_any);    Serveraddr.sin_port = htons (9999);    if (LISTENFD = =-1) err_exit ("socket");    if (Bind (LISTENFD, (struct sockaddr*) &serveraddr, sizeof (serveraddr)) = =-1) err_exit ("bind");    if (Listen (LISTENFD, ten) = =-1) err_exit ("Listen"); Return LISTENFD;}    void Do_service (int peerfd) {char recvbuf[1024] = {0};    int ret;       while (1) {ret = ReadLine (PEERFD, RECVBUF, 1024);       if (ret <= 0) break;       printf ("Recv Data:%s", recvbuf);    Writen (PEERFD, Recvbuf, strlen (RECVBUF)); }}

  

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.