Resumen de programación de red tres

1. Modelo de servidor concurrente

【1】 Servidor de bucle

1> Solo se puede procesar la solicitud de un cliente a la vez, y el siguiente cliente solo se puede procesar después de que el cliente sale.

2> Desventajas: los clientes procesados ​​por el servidor de bucle no pueden realizar operaciones que requieran mucho tiempo

//*****Modelo******

sfd = conector();
unir();
escuchar();
mientras(1)
{     newfd = aceptar();     mientras(1)     {         recv();         enviar();     }     cerrar(nuevofd); } cerrar(sfd);








【2】 Servidor concurrente

1>Puede manejar múltiples solicitudes de clientes al mismo tiempo

2>El proceso principal/hilo principal es específicamente responsable de las conexiones y crea procesos secundarios/hilos de rama para interactuar con los clientes.

~ subprocesos múltiples:

//*****Modelo******

sfd = conector();
unir();
escuchar();
mientras(1)
{     newfd = aceptar();

    pthread_create();

    pthread_detach(tid);

}

cerrar(sfd);

vacío * devolución de llamada (vacío * arg)

{

      recv();
      enviar();
      cerrar(nuevofd);

      pthread_exit(NULL);
}

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<sys/wait.h>
#include<signal.h>
#include<stdlib.h>
#include<pthread.h>

#define ERR_MSG(msg) do{\
	fprintf(stderr,"liine %d",__LINE__);\
	perror(msg);\
}while(0)

#define IP "192.168.0.79"  //本机IP 
#define PORT 6666          // 1024-49151

void* deal_cli_msg(void *arg);
//需要传递给线程处理函数的参数
struct cli_msg
{
	int newfd;
	struct sockaddr_in cin;
};

int main(int argc, const char *argv[])
{
	//创建流式套接字
	int sfd = socket(AF_INET,SOCK_STREAM,0);
	if(sfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("sfd=%d\n",sfd);
	//允许端口快速重用
	int reuse=1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}
	//填充地址信息结构体
	//真是的地址信息结构体根据地址族制定  AF_INET: man 7 ip
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;//必须填 AF_INET
	sin.sin_port = htons(PORT);//端口号, 1024-49151
	sin.sin_addr.s_addr = inet_addr(IP);//本机IP,ifconfig
	//将IP和端口号绑定到套接字上
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind sucess __%d__\n",__LINE__);

	//将套接字设置为被动监听状态,监听是否有客户端连接成功
	if(listen(sfd,128)<0)
	{
		ERR_MSG("listen");
		return -1;
	}
	printf("listen success __%d__\n",__LINE__);
	struct sockaddr_in cin;        // 存储连接成功的客户端地址信息  
	socklen_t addrlen = sizeof(cin);
	
	pthread_t tid;
	int newfd=-1;
	struct cli_msg info;
	while(1)
	{
		//阻塞函数,从已完成连接的队列头中获取一个客户端信息
		//该文件描述符才是与客户端通信的文件描述符
		newfd=accept(sfd,(struct sockaddr*)&cin,&addrlen);
		if(newfd<0)
		{
			ERR_MSG("accept");
			return -1;
		}
		printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
			inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);
		
		info.newfd=newfd;
		info.cin=cin;
		//能运行到当前位置
		if(pthread_create(&tid,NULL,deal_cli_msg,&info)!=0)
		{
			fprintf(stderr,"line:%d pthread_createfailed\n",__LINE__);
			return -1;
		}
		pthread_detach(tid); //分离线程
	}
	close(sfd);
	return 0;
}

void* deal_cli_msg(void *arg)
{
	int newfd=((struct cli_msg*)arg)->newfd;
	struct sockaddr_in cin=((struct cli_msg*)arg)->cin;
	char buf[128]="";
	ssize_t res=0;
	while(1)
	{
		//接收
		res=recv(newfd,buf,sizeof(buf),0);
		if(res<0)
		{
			ERR_MSG("recv");
			break;
		}
		else if(0==res)
		{
			printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
			inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);
			break;
		}
		printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
			inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);
		//发送
		strcat(buf,"*_*");
		if(send(newfd,buf,sizeof(buf),0)<0)
		{
			ERR_MSG("send");
			break;
		}
		printf("send sucess\n");
	}
	close(newfd);
	pthread_exit(NULL);
}

2. modelo IO

【1】Bloqueo de E/S

Después de crear un descriptor de archivo de socket, está en modo de bloqueo de E/S de forma predeterminada (lectura, escritura, recepción, envío, recepción desde, envío a).

【2】 IO sin bloqueo

1> Evite que el proceso se bloquee en la función IO, pero si desea obtener datos válidos, debe realizar un sondeo

2> Cuando un programa utiliza un socket en modo IO sin bloqueo, necesita utilizar un bucle para determinar constantemente si el descriptor de archivo tiene datos legibles, lo que se denomina sondeo.

3> La aplicación sigue sondeando el kernel para monitorear si se generan eventos de IO y el consumo de CPU es alto

1) función central

 【3】 E/S impulsada por señal

1>Método de comunicación asincrónica

2> IO impulsada por señal significa: decirle al kernel de antemano que cuando ocurre un evento IO en un determinado descriptor de archivo, el kernel notificará al proceso relevante: SIGIO

3> Para TCP, la IO controlada por señales no es útil para TCP porque las señales se generan con demasiada frecuencia y no pueden distinguir qué descriptor de archivo se envía.

【4】 Multiplexación IO (puntos clave)

1> Si necesita procesar múltiples flujos de entrada y salida al mismo tiempo en un proceso, use un solo proceso y un solo subproceso para procesar múltiples solicitudes de entrada y salida al mismo tiempo.

2> Si no se pueden utilizar multiprocesos y subprocesos múltiples, puede optar por utilizar multiplexación IO

3> Dado que no es necesario crear nuevos procesos y subprocesos, reduce la sobrecarga de recursos del sistema y la cantidad de cambios de contexto.

4> Permite realizar múltiples operaciones de IO al mismo tiempo. Una vez que el kernel descubre que un proceso realiza uno o más eventos de IO, notificará al proceso.

1)seleccionar  

 Modelo:

 código:

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<sys/time.h>
#include<sys/select.h>

#define ERR_MSG(msg) do{\
	fprintf(stderr,"liine %d",__LINE__);\
	perror(msg);\
}while(0)

#define IP "192.168.0.211"  //本机IP 
#define PORT 6666          // 1024-49151

int main(int argc, const char *argv[])
{
	//创建流式套接字
	int sfd = socket(AF_INET,SOCK_STREAM,0);
	if(sfd<0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind sucess __%d__\n",__LINE__);

	//允许端口快速使用
	int reuse=-1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}
	printf("允许端口快速重用成功\n");

	struct sockaddr_in sin;
	sin.sin_family =AF_INET;
	sin.sin_port=htons(PORT);//端口号
	sin.sin_addr.s_addr=inet_addr(IP);//本机号

	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind sucess__%d__\n",__LINE__);

	//将套接字设置为被动监听状态,监听是否有客户端连接成功
	if(listen(sfd,128)<0)
	{
		ERR_MSG("listen");
		return -1;
	}
	printf("listen success __%d__\n",__LINE__);

	//设置一个读集合
	fd_set  readfds,tmpfds;

	//由于readfds中需要防止要检测的文件描述符,所以不能让他是随机值
	//所以需要将readfds清空
	FD_ZERO(&readfds);
	FD_ZERO(&tmpfds);

	//将需要的文件描述符添加到集合中
	FD_SET(0,&readfds);
	FD_SET(sfd,&readfds);

	int s_res=0;
	char buf[128]="";
	struct sockaddr_in cin;        // 存储连接成功的客户端地址信息  
	socklen_t addrlen = sizeof(cin);

	struct sockaddr_in saveCin[1024-4];//另存客户端地址信息,0,1,2,sfd不可能有对应的客户端
	int newfd =-1;
	ssize_t res=0;
	int maxfd=sfd;//最大文件描述符

	while(1)
	{
		tmpfds=readfds;
		s_res=select(maxfd+1,&tmpfds,NULL,NULL,NULL);
		if(s_res<0)
		{
			ERR_MSG("select");
			return -1;
		}
		else if(0==res)
		{
			printf("time out\n");
			return -1;
		}

		//能运行到当前位置,则代表有文件描述符准备就绪
		//走触发事件的文件描述符对应的处理函数

		//当前集合中有文件描述符准备就绪了
		//当准备就绪,集合会只保留0
		//当sfd准备就绪,集合就会只保留sfd
		//当0和sfd都准备就绪,集合中保留0和sfd

		for(int i=0;i<=maxfd;i++)
		{
			if(!FD_ISSET(i,&tmpfds))//如果不在集合中,则直接往后继续变量
			{
				continue;
			}
			//能运行到当前位置,则说明i代表的文件描述符在tmpfds中
			//要判断i所代表的文件描述符需要走什么对应的函数
			if(0==i)
			{
				printf("触发键盘输入事件>>");
				int sndfd=-1;
				res=scanf("%d %s",&sndfd,buf);
				while(getchar()!=10);
				if(res!=2)
				{
					fprintf(stderr,"请输入正确格式:int string\n");
					continue;
				}
				//能运行到当前位置,则代表输入的格式整数
				if(sndfd<=sfd||sndfd>=1024||!FD_ISSET(sndfd,&readfds))
				{
					fprintf(stderr,"sndfd=%d文件描述符\n",sndfd);
					continue;
				}
				if(send(sndfd,buf,sizeof(buf),0)<0)
				{
					ERR_MSG("send");
					return -1;
				}
			}
			else if(sfd==i)
			{	
				printf("触发客户端连接事件>>");
				fflush(stdout);
				int newfd=accept(sfd,(struct sockaddr*)&cin,&addrlen);
				if(newfd<0)
				{
					ERR_MSG("accept");
					return -1;
				}
				printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
						inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);

				saveCin[newfd-4]=cin;

				//将newfd添加到读集合中
				FD_SET(newfd,&readfds);

				//更新maxfd
				maxfd=maxfd>newfd?maxfd:newfd;

			}
			else
			{
				bzero(buf,sizeof(buf));
				//接收
				res=recv(i,buf,sizeof(buf),0);
				if(res<0)
				{
					ERR_MSG("recv");
					return -1;
				}
				else if(0==res)
				{
					printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
							inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);

					//关闭文件描述符
					close(i);
					//从集合中剔除该文件描述符
					FD_CLR(i,&readfds);

					//更新maxfd
					//从目前最大的文件描述符中往小的判断
					int j=0;
					for(int j=maxfd;j>=0;j--)
					{
						if(FD_ISSET(j,&readfds))
						{
							maxfd=j;
							break;
						}
					}
					if(j<0)
						maxfd=-1;

					continue;
				}
				printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
						inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);

				strcat(buf,"*_*");
				if(send(i,buf,sizeof(buf),0)<0)
				{
					ERR_MSG("send");
					return -1;
				}
				printf("send sucess\n");
			}
		}
	}
	close(sfd);
	return 0;
}

2) encuesta

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<sys/time.h>
#include<sys/select.h>
#include<poll.h>

#define ERR_MSG(msg) do{\
	fprintf(stderr,"liine %d",__LINE__);\
	perror(msg);\
}while(0)

#define IP "192.168.0.211"  //本机IP 
#define PORT 6666          // 1024-49151

int main(int argc, const char *argv[])
{
	//创建流式套接字
	int sfd = socket(AF_INET,SOCK_STREAM,0);
	if(sfd<0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind sucess __%d__\n",__LINE__);

	//将套接字设置为被动监听状态,监听是否有客户端连接成功
	if(listen(sfd,128)<0)
	{
		ERR_MSG("listen");
		return -1;
	}
	printf("listen success __%d__\n",__LINE__);

	//设置一个读集合
	fd_set  readfds,tmpfds;

	//由于readfds中需要防止要检测的文件描述符,所以不能让他是随机值
	//所以需要将readfds清空
	FD_ZERO(&readfds);
	FD_ZERO(&tmpfds);

	//将需要的文件描述符添加到集合中
	FD_SET(0,&readfds);
	FD_SET(sfd,&readfds);

	int s_res=0;
	char buf[128]="";
	struct sockaddr_in cin;        // 存储连接成功的客户端地址信息  
	socklen_t addrlen = sizeof(cin);

	int newfd =-1;
	ssize_t res=-1;
	int maxfd=sfd;

	while(1)
	{
		
		tmpfds=readfds;
		s_res=select(maxfd+1,&tmpfds,NULL,NULL,NULL);
		if(s_res<0)
		{
			ERR_MSG("select");
			return -1;
		}
		if(0==res)
		{
			printf("time out\n");
			return -1;
		}
		
		//能运行到当前位置,则代表有文件描述符准备就绪
		//走触发事件的文件描述符对应的处理函数

		for(int i=0;i<=maxfd;i++)
		{
			if(!FD_ISSET(i,&tmpfds))//如果不在集合中,则直接往后继续变量
			{
				continue;
			}
		//能运行到当前位置,则说明i代表的文件描述符在tmpfds中
		//要判断i所代表的文件描述符需要走什么对应的函数
			if(0==i)
			{
				printf("触发键盘输入事件>>");
				fgets(buf,sizeof(buf),stdin);
				buf[strlen(buf)-1]=0;
				printf("%s\n",buf);
			}
			else if(sfd==1)
			{	
				printf("触发客户端连接事件>>");
				fflush(stdout);
				int newfd=accept(sfd,(struct sockaddr*)&cin,&addrlen);
				if(newfd<0)
				{
					ERR_MSG("accept");
					return -1;
				}
				printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
					inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);
	
				//将newfd添加到读集合中
				FD_SET(newfd,&readfds);

				//更新maxfd
				maxfd=maxfd>newfd?maxfd:newfd;

			}
			else
			{
				bzero(buf,sizeof(buf));
			//接收
				res=recv(i,buf,sizeof(buf),0);
				if(res<0)
				{
					ERR_MSG("recv");
					return -1;
				}
				else if(0==res)
				{
					printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
						inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);
				
					//关闭文件描述符
					close(i);
					//从集合中剔除该文件描述符
					FD_CLR(i,&readfds);
				
					//更新maxfd
					//从目前最大的文件描述符中往小的判断
					int j=0;
					for(int j=maxfd;j>=0;j--)
					{
						if(FD_ISSET(j,&readfds))
						{
							maxfd=j;
							break;
						}
					}
					if(j<0)
						maxfd=-1;

					continue;
				}
				printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
					inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);

				strcat(buf,"*_*");
				if(send(i,buf,sizeof(buf),0)<0)
				{
					ERR_MSG("send");
					return -1;
				}
				printf("send sucess\n");
			}
		}
	}
	return 0;
}

3. Biblioteca de funciones

[1] El concepto de biblioteca

1> La biblioteca es un archivo ejecutable binario. En comparación con un programa ejecutable binario, la biblioteca no se puede ejecutar sola .

        a. Todas las funciones almacenadas en la biblioteca son funciones funcionales y solo se pueden almacenar funciones funcionales.

        B. No se permite almacenar la función principal en la biblioteca.

        C. La biblioteca es un código funcional escrito que se puede utilizar.

2> La biblioteca debe cargarse en la memoria para su uso.

3>Cada sistema operativo tiene su propia biblioteca, que es incompatible

Clasificación de bibliotecas:

Biblioteca estática, biblioteca dinámica.

 【2】Biblioteca estática

1) Principio de la biblioteca estática: las funciones encapsuladas por la biblioteca estática se heredarán en el programa ejecutable cuando el programa se compile en el paso de la biblioteca de enlaces .

Ventajas: 1> Cuando el programa se está ejecutando, no tiene nada que ver con la biblioteca estática, lo que facilita el trasplante.

            2>Las bibliotecas estáticas se ejecutarán más rápido

Desventajas: 1> La actualización e implementación del programa es problemática

           2> Grande, desperdicia espacio en disco al almacenar y desperdicia espacio de memoria al cargar y ejecutar.

2) Instrucciones de producción de biblioteca estática

 paso:

1>Dividir en archivos y separar la función funcional de la función principal, func.c main.c

2> Escriba un archivo de encabezado para la función funcional: func.h

3> Compile main.c y func.c juntos y pruebe si la división del archivo es exitosa.

4>Utilice las instrucciones anteriores para encapsular func.c en una biblioteca estática

5>Nota: libxxx.a, xxx es el nombre de la biblioteca

3) Uso de bibliotecas estáticas

【3】 Biblioteca dinámica (biblioteca compartida)

 1) El principio de la biblioteca dinámica: la biblioteca dinámica pospone el paso de vinculación de la función de la biblioteca hasta que el programa se esté ejecutando . Cuando el programa ejecuta la función de la biblioteca, encontrará la función de la biblioteca dinámica.

1> Si la función de la biblioteca dinámica no existe en la memoria, la función de la biblioteca dinámica se cargará en la memoria para su ejecución.

2> Si la función de biblioteca dinámica existe en la memoria, se llamará directamente y no se cargará la segunda copia.

3> Entonces solo habrá una copia de la función de biblioteca dinámica en la memoria

ventaja:

1> Pequeño, ahorra espacio en disco al almacenar, ahorra espacio en memoria al ejecutar

2> Es más conveniente actualizar e implementar el programa y no es necesario volver a compilar el programa ejecutable.

defecto:

1> Cuando el programa se está ejecutando, si no se encuentra la biblioteca dinámica, el programa fallará, por lo que usar una biblioteca dinámica para generar un programa binario tiene baja portabilidad.

2>Baja eficiencia operativa

2) Instrucciones de producción de biblioteca dinámica

  paso:

1>Dividir en archivos y separar la función funcional de la función principal, func.c main.c

2> Escriba un archivo de encabezado para la función funcional: func.h

3> Compile main.c y func.c juntos y pruebe si la división del archivo es exitosa.

4>Utilice las instrucciones anteriores para encapsular func.c en una biblioteca estática

5>Nota: libxxx.so, xxx es el nombre de la biblioteca

3) Uso de bibliotecas dinámicas

 4 ) Configuración de variables de entorno de bibliotecas dinámicas.

1> Mueva la biblioteca dinámica al directorio /lib/ o /usr/lib

        a. sudo mv ./libmyfunc.so /usr/lib/

        b. sudo mv libmyfunc.so/lib/

2>Configurar variables de entorno: LD_LIBRARY_PATH

 3> Modificar el archivo de configuración de la variable de entorno Liu Ang

        a. cd /ec/ld.so.conf.d/

        B. sudo touch my.conf Crea un archivo que termina en .conf

        C. sudo vim mi.conf

        d. Complete la ruta absoluta de la carpeta donde se encuentra la biblioteca dinámica. Tenga en cuenta que solo puede completar la ruta absoluta de una biblioteca dinámica en una línea.

        e. Después de guardar y salir, ejecute sudo ldconfig para actualizar las variables de entorno

Supongo que te gusta

Origin blog.csdn.net/weixin_57039874/article/details/130462614
Recomendado
Clasificación