Multiplex I/O - select

Posted by oops73 on Mon, 27 Sep 2021 04:52:24 +0200

1. Principle
Compared with multi thread and multi process I/O multiplexing, it solves the blocking problem of the server accept(). When the client needs to connect, use select to listen, and the server enables accept(). Select receives the listening socket created by the server to listen for new client connections and connect events, At the same time, the cfd returned by the server accept is handed over to select to monitor whether data is transmitted and listen for read and write events. The server does not need to block the reading and writing, and processes a large number of concurrent I / OS at the same time
2. Function

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

nfds: add 1 to the maximum file descriptor in the monitored file descriptor set
readfds: monitors the collection of file descriptors with read data arriving. It is an incoming and outgoing parameter. It is the collection of file descriptors to listen to and the collection of file descriptors with events
writefds: monitors the collection of file descriptors where write data arrives, and passes in and out parameters
exceptfds: monitors the collection of file descriptors where exception data reaches, and passes in and out parameters
timeout: timed blocking monitoring time, 3 cases
① NULL, blocking and waiting
② Set timeval and wait for a fixed time
③ Set timeval=0, rotation inspection
Return value:
Greater than 0: the total number of events that meet the corresponding requirements in all listening sets
0: there is no file descriptor that meets the listening conditions
-1: Abnormal
3. Operation function
Define a listening set: fd_set set;
Empty a set of file descriptors:

void FD_ZERO(fd_set *set)

Add the file descriptor to be monitored to the collection:

void FD_SET(int fd, fd_set *set)

Remove a file descriptor from the listener collection:

void FD_CLR(int fd, fd_set *set)

Judge whether a file descriptor is in the listening set;
Return value: 1 when, 0 when not

int FD_ISSET(int fd, fd_set *set)

4.select implementation server model
Idea analysis: using select to monitor can monitor whether there is lfd connected by connect and cfd for data communication at the same time. Each time new data is sent, it will be added to the monitoring set. At the same time, the cfd in the monitoring set can realize data communication

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<ctype.h>

#define SERV_PORT 6666

int main()
{
    int i, j, n;
    int lfd, cfd;
    char buf[BUFSIZ];
    struct sockaddr_in clie_addr, serv_addr;
    socklen_t clie_addr_len;
    lfd = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(serv_addr));  //Set port multiplexing
    bzero(&serv_addr, sizeof(serv_addr));    //Define server address structure
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    listen(lfd, 128);

    fd_set rset, allset;   //Define the listening collection of select
    FD_ZERO(&allset);
    FD_SET(lfd, &allset);

    int maxfd = 0, ret;
    maxfd = lfd;
    while(1){
        rset = allset;          //Back up allset, ensure that allset does not change, and continue listening next time
        ret = select(maxfd+1, &rset, NULL, NULL, NULL);    //rset sends out a collection that actually meets the listening conditions
        if(ret>0){
            if(FD_ISSET(lfd, &rset)){
                clie_addr_len = sizeof(clie_addr);
                cfd = accept(lfd, (struct sockaddr *)&clie_addr, &clie_addr_len);
                FD_SET(cfd, &allset);
                if(maxfd<cfd){
                    maxfd = cfd;
                }
                if(ret==1)   //select outgoing only has lfd and no subsequent execution is required
                    continue;
            }
            for(i=lfd+1; i<=maxfd; i++){
               if( FD_ISSET(i, &rset)){
					n = read(i, buf, sizeof(buf));
                    if(n==0){
                        close(i);
						FD_CLR(i, &allset);
                    }
                    for(j=0; j<n; j++){
                        buf[j] = toupper(buf[j]); //Lowercase to uppercase
                    }
                    write(i, buf, n); //Send to client
                    write(STDOUT_FILENO, buf, n); //Print to server screen
                }
            }
        }
    }
    close(lfd);
    return 0;
}

Topics: Linux