Multi-channel IO transfer server-select

Help remember to like + follow

1. Multi-channel IO transfer server

Multi-channel IO transfer server is also called multi-task IO server . The main idea of ​​this type of server implementation is not to monitor client connections by the application itself, but instead to monitor files for the application by the kernel .

There are three main implementations. Today, let’s introduce the first way SELECT [polling mechanism]

2. SELECT【Polling mechanism】 

 1. The number of file descriptors that select can monitor is limited by FD_SETSIZE, generally 1024, [ This is caused by historical reasons, because the network was not so developed before and the amount of concurrency was not high. When I designed this select, I thought that 1024 was already Enough ]. Simply changing the process open file descriptor does not change the number of files monitored by select. As shown in the figure (I have changed it through the configuration file, the default is 1024)

Look at the open files field, this is the upper limit of file descriptors that the process can open

2. When solving the number of clients with less than 1024, it is more appropriate to use select, but there are more connected clients [ but only one or two clients send messages ]. Select uses a polling mechanism to detect who sent the message, which will greatly reduce the server Response efficiency, should not spend too much energy on select [but I still want to introduce it to everyone]

 

Three.select related operation functions

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

  • nfds [changeable]: monitor the largest file descriptor in the file descriptor set +1, because this parameter will tell the kernel how many file descriptors the state to detect
  • readfds: Monitor the read data to the file descriptor set, and pass in and out parameters.
  • writefds: monitor the write data arriving at the file descriptor set, incoming and outgoing parameters
  • exceptfds: monitor the arrival of the file descriptor set when an exception occurs, such as the arrival of external data, the incoming and outgoing parameters
  • timeout: timing blocking monitoring time, 3 cases, 1. NULL wait forever, 2. set timeval, wait for a fixed time, 3. set the time in timeval to 0, return immediately after checking the description word, and poll
  • struct timeval{
  •     long tv_sec; // second
  •     long tv_usec; ​​// microseconds
  • }

void FD_CLR(int fd, fd_set *set);   // Clear fd in the file descriptor set to 0

int FD_ISSET(int fd, fd_set* set);   // Whether fd is in the file descriptor set

void FD_SET(int fd, fd_set* set);   // add fd to the file descriptor set

void FD_ZERO(fd_set * set);   // Clear all file descriptor sets to 0

 

4. Multi-channel IO transfer server-select【Code Demo】

#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>



using namespace std;

#ifndef FD_SETSIZE
#define FD_SETSIZE 1024
#endif

#define SERVER_PORT 7777
#define LISTEN_NUM  	200
#define BUF_SIZE 1024


int
main(int argc, char*argv[])
{
	int i, j, n, maxi;
	int nready, client[FD_SETSIZE];
	int maxfd, listenfd, connfd, sockfd;
	struct sockaddr_in clie_addr, serv_addr;
	socklen_t clie_addr_len;
	fd_set r_set, all_set;
	char buf[BUF_SIZE], str[INET_ADDRSTRLEN];   // 16

	listenfd = socket(AF_INET, SOCK_STREAM, 0);
	memset(&serv_addr, 0, sizeof(serv_addr));

	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(SERVER_PORT);

	int opt = 1;
	setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));	
	bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));	
	listen(listenfd, LISTEN_NUM);

	maxfd = listenfd;  // 3
	maxi = -1;

	for(i=0; i<FD_SETSIZE; i++) client[i] = -1;  // 我们自己维护一个 client fd  数组
	FD_ZERO(&all_set);
	FD_SET(listenfd, &all_set);

	// printf("*******listen******\n");
	
	while(true){
		r_set = all_set; // 因为是传入传出参数所以要 赋值
		nready = select(maxfd+1, &r_set, NULL, NULL, NULL);
		if(nready < 0){
			perror("select err");
			exit(1);
		}

		if(FD_ISSET(listenfd, &r_set)){  // 有用户连接
			clie_addr_len = sizeof(clie_addr);
			connfd = accept(listenfd, (struct sockaddr*)&clie_addr, &clie_addr_len);
			printf("received from %s at port %d\n",
				inet_ntop(AF_INET, &clie_addr.sin_addr, str, sizeof(str)),
				ntohs(clie_addr.sin_port));

			for(i=0; i<FD_SETSIZE; i++){
				if(client[i] < 0){   // 找到下标最小的  没有被占用的
					client[i] = connfd;
					break;
				}
			}
			
			if(i == FD_SETSIZE){ // 用户已经达到 规定的上限了
				fputs("too many client\n", stderr);
				exit(1);
			}

			FD_SET(connfd, &all_set);  // 加入所有用户文件描述符集合的 
			if(connfd > maxfd)  // 设置好最大的 maxfd
				maxfd = connfd;
			
			if(i > maxi)   // 下标赋值
				maxi = i;

			if(--nready == 0)  // only has listen event
				continue;
	
		}

		for(i=0; i<=maxi; ++i){
			if((sockfd = client[i]) < 0)
				continue;

			if(FD_ISSET(sockfd, &r_set)){
				if((n = read(sockfd, buf, sizeof(buf) )) == 0){// 清理资源
					close(sockfd);
					FD_CLR(sockfd, &all_set);
					client[i] = -1;
				}else if(n > 0){
					for(j=0; j<n; j++)
						buf[j] = toupper(buf[j]);
					sleep(2);
					write(sockfd, buf, n);
				}

				if(--nready == 0)break;
			}

		}
	}
	
	close(listenfd);
	return 0;
}

 

effect:

SERVER

CLIENT

 Schematic diagram

 

 

Guess you like

Origin blog.csdn.net/qq_44065088/article/details/109237642