select of socket programming

Keywords: socket Programming Linux network

select for Linux Programming:

The function of select is to listen for readable, writable and abnormal events on the file descriptor that users are interested in for a specified period of time.

 

1. socket blocking mode

Usually in socket programming, we are used to writing blocking programs such as connect, accept, recv, recvfrom. If the event does not occur, the program will be blocked and unable to return.

 

2. socket non-blocking mode: select

When a process or thread executes this function, it does not have to wait for an event to occur. When it is executed here, it will reflect the execution according to the result of select ion. If an event occurs, it is executed according to the logic at the time of occurrence, and if it does not occur, it will continue to execute. It mainly monitors changes in file descriptors that we need to monitor -- read, write, or exceptions.

 

Related API:

Parametric 1: The total number of file descriptors monitored is 1 larger than the maximum number of file descriptors in all file descriptor sets, since the file descriptors are counted from zero.

PARAMETER 2: READABLE FILE DESCRIPTOR SET

Parametric 3: Writable file descriptor set

Parametric 4: Exception Event File Descriptor Set

Parameter 5: Setting timeout time. NULL means unlimited waiting, similar to blocking.

Return values: 0: timeout; - 1: failure; successful return of integers greater than 0, which is the number of ready descriptors.

#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int maxfdp,fd_set *readset,fd_set *writeset,fd_set *exceptset,struct timeval *timeout);

 

fd_set can be understood as a collection in which file descriptors need to be monitored are stored. It can be operated by FD_ZERO, fd_set, FD_CLR and FD_ISSET.

 

The struct timeval structure:

struct timeval
{
    long tv_sec; /*second */
    long tv_usec; /*Microsecond */
};

 

Several macros that operate on fd_set:

#include <sys/select.h>
int FD_ZERO(int fd, fd_set *fdset); //All bits of a variable of type fd_set are set to 0
int FD_CLR(int fd, fd_set *fdset); //Clearing a bit can be used
int FD_SET(int fd, fd_set *fd_set); //Setting a location of a variable
int FD_ISSET(int fd, fd_set *fdset); //Testing whether a bit is positioned

 

Use examples:

fd_set rset;
int fd;
FD_ZERO(&rset);
FD_SET(fd, &rset);

 

Then use the select function:

select(fd+1, &rset, NULL, NULL, NULL);

 

When select returns, FD_ISSET is used to test whether it is positioned:

if(FD_ISSET(fd, &rset))
{
    ...
    //do something
}

 

Principle analysis:

The server will need to add sockets for IO operations to the select and wait for the return of the select system call. When the data arrives, the socket is activated and the select function returns. The server reads the data.

As you can see, the use of select functions for IO requests and synchronous blocking model is not much different, and there are even more additional operations to add monitoring sockets and call select functions. It reduces efficiency. However, with select, the server can process multiple socket requests simultaneously in one thread. After the server registers the socket, it can read the activated socket through select, so that it can process multiple IO requests simultaneously in the same thread. If it is in a synchronous blocking thread, it must be implemented through multithreading.

 

server.c

#include <sys/types.h> 
#include <sys/socket.h> 
#include <stdio.h> 
#include <netinet/in.h> 
#include <sys/time.h> 
#include <sys/ioctl.h> 
#include <unistd.h> 
#include <stdlib.h>
#define LINTEN_QUEUE 5
#define PORT 8888
#define MAXLEN 1024

int socket_bind_listen()
{
    struct sockaddr_in server_address;
    int server_sockfd;
    server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    server_address.sin_port = htons(PORT);

    bind(server_sockfd, (struct sockaddr*) & server_address, sizeof(server_address));
    listen(server_sockfd, LINTEN_QUEUE); 

    return server_sockfd;
}
void socket_select(int server_sockfd)
{
    fd_set readfds, testfds;
    int client_sockfd;
    struct sockaddr_in client_address;
    int client_len;
    FD_ZERO(&readfds);
    FD_SET(server_sockfd, &readfds);//Add server-side socket s to the collection

    struct timeval tv;
    while (1)
    {
	tv.tv_sec = 5;
	tv.tv_usec = 0;

	int fd;
	int nread;
	testfds = readfds;//The set of descriptors that will need to be monitored is copied into the select query queue, which is modified by the select, so you must use variables separately. 

	int result = select(FD_SETSIZE, &testfds, (fd_set*)0, (fd_set*)0, NULL); //FD_SETSIZE: The default maximum file descriptor for the system
	if (result < 0)
	{
    	    perror("server selelct error");
	    exit(1);
	}
	else if (result == 0)
	{
	    printf("time out\n");
	    //continue;
	}

	/*Scan all file descriptors*/
	for (fd = 0; fd < FD_SETSIZE; fd++)
	{
	    /*Find the relevant file descriptor*/
	    if (FD_ISSET(fd, &testfds))
	    {
	        /*To determine whether it is a server socket or not is to request a connection for a customer.*/
	        if (fd == server_sockfd)
		{
		    client_len = sizeof(client_address);
		    client_sockfd = accept(server_sockfd, (struct sockaddr*) & client_address, &client_len );

		    FD_SET(client_sockfd, &readfds);//Add the client socket to the collection
		    printf("adding client on fd %d\n", client_sockfd);
		}
		/*When there is a data request in the client socket*/
		else
		{
		    ioctl(fd, FIONREAD, &nread);//Get the amount of data and hand it to nread

		    /*When the client data request is complete, close the socket and clear the corresponding descriptor from the collection */
	            if (nread == 0)
	     	    {
	                close(fd);
			FD_CLR(fd, &readfds); //Remove closed fd
			printf("removing client on fd %d\n", fd);
		    }
		    /*Processing customer data requests*/
		    else
		    {
			char buf[MAXLEN] = "";
			recv(fd, buf, MAXLEN, 0);
			printf("buf:%s\n", buf);
			printf("serving client on fd %d\n", fd);
		    }
		}
	    }
	}
    }
}
int main() 
{ 
    int server_sockfd; 
    
    server_sockfd = socket_bind_listen();

    socket_select(server_sockfd);
    
    return 0;
}

client.c

#include <sys/types.h> 
#include <sys/socket.h> 
#include <stdio.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <unistd.h> 
#include <stdlib.h>
#include <sys/time.h>
#include <string.h>
int main() 
{ 
    int client_sockfd; 
    int len; 
    struct sockaddr_in address;//Server-side Network Address Architecture 
    int result; 

    client_sockfd = socket(AF_INET, SOCK_STREAM, 0);//Establishing client socket 
    address.sin_family = AF_INET; 
    address.sin_addr.s_addr = inet_addr("127.0.0.1");
    address.sin_port = htons(8888); 

    len = sizeof(address); 
    result = connect(client_sockfd, (struct sockaddr *)&address, len); 

    if(result == -1) 
    { 
         perror("oops: client2"); 
         exit(1); 
    } 
    char buf[1024] = "hello";
    send(client_sockfd, buf, strlen(buf), 0);
    close(client_sockfd); 

    return 0; 
}

 

Reference resources:

https://blog.csdn.net/weixin_41010318/article/details/80257177

 

https://www.cnblogs.com/skyfsm/p/7079458.html

https://blog.csdn.net/piaojun_pj/article/details/5991968

Posted by littlepeg on Thu, 29 Aug 2019 23:07:21 -0700