服务器模型学习

服务器模型大体分为三种:循环模型、并发模型及IO复用模型。

循环模型

循环服务器指的是对客户端的请求和连接,服务器处理完一个之后再处理另一个,即串行处理客户端的请求。循环服务器又叫迭代服务器。循环服务器常用于UDP服务程序。

并发模型

单客户端单进程,统一accept()

该模型是由主进程统一处理客户端的连接,当客户端的连接请求到来时,服务器的accept()函数成功返回,此时服务器端的进程调用fork()分叉。父进程等待客户端的连接,子进程去处理已连接客户端的业务。

单客户端单进程,各线程独立accept()

简单并发服务器可以同时对多个客户端的请求进行处理。原理主要是主进程提前构建多个子进程,当客户端请求来时,系统从进程池中选取一个子进程处理客户端的连接,每个进程处理一个客户端的请求,在全部进程处理能力得到满足之前服务器的网络负载基本不变。


单客户端单线程,统一accept()

该模型是由主进程统一处理客户端的连接,当客户端的连接请求到来时,服务器的accept()函数成功返回,此时服务器端的进程调用pthread_create()函数创建线程。主进程等待客户端的连接,新创建的线程去处理已连接客户端的业务。

单客户端单线程,各线程独立accept(),使用互斥锁

该模型是由主进程提前分别分配多个线程同时处理客户端的连接,也就是每个线程都可以调用accept()函数去处理连接,为了防止冲突,线程处理连接时必须使用线程互斥锁,在调用accept()函数之前锁死,调用完成后释放锁。

 I/O复用模型

原理解析

并发服务器有一个比较大的缺陷,它需要建立多个并行的处理单元。当客户端增加时,随着处理单元的增加,系统的负责会逐渐的转移到并行处理单元的切换上,处理业务能力就会降低。因此出现了一种I/O复用模型,该模型在服务器启动时建立多个不同的处理单元,如:连接处理单元,业务处理单元等。当客户端连接到来时,将客户端的连接放在一个状态池中,对所有的客户端连接在一个处理单元中轮询处理。与之前的并发服务器相比,客户端的增加不会造成处理单元的增加,而处理与cpu和内存的速度直接相关。

该模型首先调用socket()函数建立一个套接字描述符,调用bind()函数将套接字与本地地址和监听端口绑定,然后调用listen()设置侦听队列长度。然后建立两个线程,一个为连接业务处理线程,一个请求业务处理线程。

连接业务处理线程调用accept()函数接收客户端的连接,得到客户端连接套接字文件描述符并将其放入客户端连接状态表中,该状态表是由连接业务处理线程与请求业务处理线程共享。

请求业务处理线程用于处理客户端的业务请求,例如请求的分析、IO数据的读取等。该线程根据连接业务处理线程获取的客户端的套接字文件描述符(状态表),建立文件描述符集合,使用select()函数对文件描述符集合进行超时等待。当有客户端请求到来时,请求处理线程接收并对其进行处理。当请求完毕时,客户端退出时请求业务处理线程将此客户端的文件描述符从客户端连接状态表中取出。

代码实例

服务器端代码 select.c 如下:

/******************************************************************************
            版权所有 (C), 2018-2019, xxx Co.xxx, Ltd.
 ******************************************************************************
    文 件 名 : select.c
    版 本 号 : V1.0
    作    者 : lijd
    生成日期 : 2018年12月24日
    功能描述 : I/O复用之select函数
    修改历史 :
******************************************************************************/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <ctype.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h>
#include <unistd.h>
#include <signal.h>
#include <arpa/inet.h>
#include <errno.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
#define BUFFLEN 	1024
#define SERVER_PORT 8888
#define BACKLOG		5
#define CLIENTNUM	512
 
int connect_host[CLIENTNUM];
int connect_number = 0;
 
static void *handle_request(void *argv)
{
    time_t now;
    char buff[BUFFLEN];
    int n = 0;
    int maxfd = -1;
    fd_set scanfd;
    struct timeval timeout;
	
    int i = 0;
    int err = -1;
	
    while(1)
    {
        // timeout变量传入select时会被内核程序实时刷新,再次循环调用select时需重新赋值,设置select程序阻塞时间
        timeout.tv_sec = 1;
        timeout.tv_usec = 0;
 
        maxfd = -1;
        // 由于fd_set集合传入select函数,即是传入参数,又是传出参数,内核程序会把就绪队列标志带入fd_set集合中带出,再次调用select时需要重新初始化fd_set集合
        FD_ZERO(&scanfd);
        for(i = 0; i < CLIENTNUM; i++)
        {
            if(connect_host[i] != -1)
            {
                FD_SET(connect_host[i], &scanfd);
                if(connect_host[i] > maxfd)
                {
                    maxfd = connect_host[i];
                }
            }
        }
		
        err = select(maxfd+1, &scanfd, NULL, NULL, &timeout);
        switch(err)
        {
            case 0:    break;
			case -1:    printf("error~~~~~\n");    break;
			default:
			{
				if(connect_number <= 0)    break;
				for(i = 0; i < CLIENTNUM; i++)
				{
					if(connect_host[i] != -1)
					{
						if(FD_ISSET(connect_host[i], &scanfd))    // 判断描述符是否可读写
						{
							memset(buff, 0, BUFFLEN);
							n = recv(connect_host[i], buff, BUFFLEN, 0);
										
							if(n > 0 && !strncmp(buff, "TIME", 4))
							{
								memset(buff, 0, BUFFLEN);
								now = time(NULL);
								sprintf(buff, "%24s\r\n", ctime(&now));
								printf("buff : %s\n", buff);
								send(connect_host[i], buff, strlen(buff), 0);
							}
									
							close(connect_host[i]);
							connect_host[i] = -1;
							connect_number--;
						}
					}
				}
				break;
			}
		}
    }
}
 
static void *handle_connect(void *argv)
{    
    int s_s = *((int *)argv);
    struct sockaddr_in from;
    socklen_t len = sizeof(from);
    while(1)
    {
        int i = 0;
        int s_c = accept(s_s, (struct sockaddr *)&from, &len);
        printf("from client link IP : %s\n", inet_ntoa(from.sin_addr));
        for(i = 0; i < CLIENTNUM; i++)
        {
            if(connect_host[i] == -1)
            {
                connect_host[i] = s_c;
				connect_number++;
				break;
			}
		}
    }
}
 
int main(int argc, char* argv[]) 
{
    int s_s;
    struct sockaddr_in local;
    int i = 0;
    memset(connect_host, -1, CLIENTNUM);
	
    s_s = socket(AF_INET, SOCK_STREAM, 0);
	
    memset(&local, 0, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = htonl(INADDR_ANY);
    local.sin_port = htons(SERVER_PORT);
	
    bind(s_s, (struct sockaddr *)&local, sizeof(local));
    listen(s_s, BACKLOG);
	
    pthread_t thread_do[2];
    pthread_create(&thread_do[0], NULL, handle_connect, (void *)&s_s);
	
    pthread_create(&thread_do[1], NULL, handle_request, NULL);
	
    for(i = 0; i < 2; i++)
    {
		pthread_join(thread_do[i], NULL);
    }
	
    close(s_s);
	
    return 0;
}

服务端程序运行平台IP为:10.0.13.111,IP客户端连接平台:10.0.13.222。运行程序执行结果如下:

 对于此篇博客存在的问题,欢迎各位大佬指正留言。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您好!对于云服务器部署深度学习模型,您可以按照以下步骤进行操作: 1. 选择云服务提供商:首先,您需要选择一个云服务提供商,如阿里云、腾讯云或亚马逊AWS等。不同的云服务提供商可能会有不同的服务和定价方案,您可以根据自己的需求选择最适合的。 2. 创建虚拟机实例:在所选的云服务提供商中,您需要创建一个虚拟机实例作为您的云服务器。在创建实例时,您需要选择适当的配置,包括计算资源、存储容量和操作系统等。 3. 安装深度学习框架:在您的云服务器上安装所需的深度学习框架,如TensorFlow、PyTorch或Keras等。您可以通过命令行或者包管理器来安装这些框架。 4. 数据和模型上传:将您的深度学习模型和训练数据上传到云服务器。您可以使用SCP或者SFTP等工具进行文件传输。 5. 配置环境和依赖项:根据您的深度学习模型的需求,安装所需的依赖项和库。这些依赖项可能包括GPU驱动、CUDA和cuDNN等。 6. 运行深度学习模型:在您的云服务器上运行深度学习模型。您可以使用命令行或者编写脚本来执行训练或推理任务。 7. 监控和优化:监控您的云服务器的性能和资源利用情况。根据需要,您可以进行调优和优化,以提高模型的训练速度或推理性能。 请注意,具体的步骤可能因云服务提供商和深度学习框架的不同而有所差异。此外,还需要确保您有足够的计算资源和存储空间来支持您的深度学习任务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值