Selección de servidor de transferencia de E / S multicanal

Ayuda a recordar dar me gusta + seguir

1. Servidor de transferencia de E / S multicanal

El servidor de transferencia de E / S multicanal también se denomina servidor de E / S multitarea . La idea principal de este tipo de implementación de servidor no es monitorear las conexiones del cliente por la aplicación en sí, sino monitorear los archivos de la aplicación por parte del kernel .

Hay tres implementaciones principales. Hoy, presentamos la primera forma SELECT [mecanismo de sondeo]

2. SELECCIONAR 【Mecanismo de sondeo】 

 1. La cantidad de descriptores de archivo que seleccione puede monitorear está limitada por FD_SETSIZE, generalmente 1024, [ esto se debe a razones históricas, porque la red no estaba tan desarrollada antes y la cantidad de simultaneidad no era alta. Cuando diseñé esta selección, pensé que 1024 era Suficiente ]. El simple hecho de cambiar el descriptor de archivos abiertos del proceso no cambia la cantidad de archivos monitoreados por select. Como se muestra en la figura (lo he cambiado a través del archivo de configuración, el valor predeterminado es 1024)

Mire el campo de archivos abiertos, este es el límite superior de descriptores de archivo que el proceso puede abrir

2. Al resolver la cantidad de clientes con menos de 1024, es más apropiado usar select, pero hay más clientes conectados [ pero solo uno o dos clientes envían mensajes ], select usa un mecanismo de sondeo para detectar quién envió el mensaje, lo que reducirá en gran medida el servidor Eficiencia de respuesta, no debería gastar demasiada energía en una selección [pero aún así quiero presentársela a todos]

 

Tres funciones de operación relacionadas.

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

  • nfds [cambiará]: supervisa el descriptor de archivo más grande en el conjunto de descriptores de archivo +1, porque este parámetro le dirá al kernel cuántos descriptores de archivo debe detectar el estado
  • readfds: supervisa los datos leídos en el conjunto de descriptores de archivos y transfiere parámetros de entrada y salida.
  • writefds: monitorea la llegada de datos de escritura al conjunto de descriptores de archivo, parámetros entrantes y salientes
  • exceptfds: monitorea la llegada del conjunto de descriptores de archivo cuando ocurre una excepción, como la llegada de datos externos, los parámetros entrantes y salientes
  • tiempo de espera: tiempo de monitoreo de bloqueo de tiempo, 3 casos, 1. NULL esperar para siempre, 2. establecer timeval, esperar un tiempo fijo, 3. establecer el tiempo en timeval en 0, regresar inmediatamente después de verificar la descripción y sondear
  • struct timeval {
  •     long tv_sec; // segundo
  •     long tv_usec; ​​// microsegundos
  • }

void FD_CLR (int fd, fd_set * set);   // Borrar fd en el descriptor de archivo establecido en 0

int FD_ISSET (int fd, fd_set * set);   // Si fd está en el conjunto de descriptores de archivo

void FD_SET (int fd, fd_set * set);   // agrega fd al conjunto de descriptores de archivo

void FD_ZERO (fd_set * set);   // Borrar todos los conjuntos de descriptores de archivo a 0

 

4. Servidor de transferencia de E / S multicanal: seleccione 【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;
}

 

efecto:

SERVIDOR

CLIENTE

 Diagrama esquemático

 

 

Supongo que te gusta

Origin blog.csdn.net/qq_44065088/article/details/109237642
Recomendado
Clasificación