当前位置: 首页 > news >正文

嵌入式学习第二十四天--网络 服务器


服务器模型

tcp服务器:
socket
bind
listen
accept
recv/send
close

1.支持多客户端访问 

//单循环服务器 
socket
bind
listen
while(1)
{    
    accept
    while(1)
    {
      recv/send
    }
}
close

2.支持多客户端同时访问 (并发能力) 
并发服务器
socket
bind
listen
while(1)
{    
    connf = accept
    pid_t pid = fork();
    
    if(pid > 0)
    {
        continue;
    }else if (pid == 0)
    {
        while(1) //负责 与 客户端通信的 
        { 
          recv/send
        }
    }
}
close

2.1进程
socket
bind
listen
while(1)
{    
    connf = accept
    pid_t pid = fork();
    //出错处理
    if (pid == 0)
    {
        while(1) //负责 与 客户端通信的 
        { 
          recv/send
        }
    }
}
close

2.2线程

void *handle_client(void *arg)
{
       while(1) //子线程中 负责 与 客户端通信的 
        { 
          recv/send
        }
}


socket
bind
listen
while(1)
{    
    connf = accept
    pthread_create();
    //出错处理
}
close
-------------------------------------------------------------------------


三种服务器模型比较

1.单循环服务器 
2.并发服务器 
  进程 
  线程 
  

        1、简单循环服务器
            http 
            web 服务器,apache--》cgi,php,perl,IIS--》asp,NGIX,Nlighty
            
            while(1)
            {
                newfd = accept();
                    recv();
                close(newfd);
            }

            特点:可以接入多个客户端的信息。
            缺点:数据通信过程短,客户端只能一次有效。
                  实时性效果差。

        2、fork循环服务器===>每次有链接则fork一个子进程为该
                            链接处理通信过程,父进程继续等待新链接。

            while(1)
            {
                newfd  = accept();
                pid = fork()
                if(pid  == 0)
                {
                    ///接收数据
                }
                if(pid < 0)
                {
                    perror("fork");
                    return -1;
                }
                waitpid()
            }
            
            特点:可以完成多个进程的实时交互,信息的完整性可以保证。

            缺点:回收资源不方便,每次fork 占用系统资源多。
                  可能出现僵尸进程
                  
            多线程:
               特点:
                   创建速度快,调度快 
               缺点:
                   线程共享进程资源,稳定性,安全性 较差 
                   
      3.并发的服务器模型 ---更高程度上的并发 
      
      IO模型 
      阻塞IO
      非阻塞IO 
      
    1、阻塞IO  
       用的最多。
       读阻塞。
       写阻塞。
    2、非阻塞IO 
         -1 errno EAGAIN  whild(1){read()break;}忙等待
        control

        cntl        
    3、IO多路复用
    4、信号驱动IO  SIGIO  ---异步 
    5, 并行模型 进程,线程

  
    #include <unistd.h>
    #include <fcntl.h>
    int fcntl(int fd, int cmd, ... /* arg */ );
    功能:修改指定文件的属性信息。
    参数:fd 要调整的文件描述符
          cmd 要调整的文件属性宏名称
          ... 可变长的属性值参数。
    返回值:成功  不一定,看cmd
            失败  -1;
              
              


    int fcntl(int fd, int cmd, ... /* arg */ );

     //驱动:     
     //1.驱动程序 ---- 驱使硬件工作起来的程序 
     让灯亮起来 
     
    eg:修改文件的非阻塞属性:
        int flag ;
        flag  = fcntl(fd,F_GETFL,0);  ///获取fd文件的默认属性到flag变量中。
        flag  = flag | O_NONBLOCK;    ///将变量的值调整并添加非阻塞属性
        fcntl(fd,F_SETFL,flag);       ///将新属性flag设置到fd对应的文件生效。

        以上代码执行后的阻塞IO将变成非阻塞方式。


    信号驱动IO

    //signal
    1.fcntl  --- 设置 信号接受者 
    flags = fcntl(fd,F_GETFL); //获得当前标志 
    fcntl(fd,F_SETFL,flags|O_ASYNC); //异步通信的标志 
    //同步 通信
    //异步 通信 
    2.将该程序 和 SIGIO信号关联起来 
    fcntl(fd,F_SETOWN,pid);
    3.设置信号处理函数 
    signal 

    owner //所有者 

    缺点:
        处理的数量有限  (超过了1个不好判断了)


    
                              /---client1
                             / 
    server <--------------->|-----client2
                             \ 
                              \---client3
                             
                             
                                                         | --->A
                              /---client1(子进程/线程)-- | --->B
                             /                           | --->C
    server <--------------->|-----client2(子进程/线程)-- | --->D
                             \                           | --->E
                              \---client3(子进程/线程)-- | --->F
                                                         | --->G
                                                         
      
    stdin   //读 
    stdout  //写 
    stderr  //--写出错信息 
     
     IO多路服用 
     
     多路 --- 多个输入输出 
     复用 --- 复用同一个进程或线程

     


   select    //linux提供实现Io多路复用函数
   
   

       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

       int select(int nfds, 
                  fd_set *readfds, 
                  fd_set *writefds,
                  fd_set *exceptfds, 
                  struct timeval *timeout);
                  
       功能:
           io多路复用函数 
       参数:
          nfds      //三个集合中最大的文件描述符 + 1
          readfds     //关心的 读操作的 文件描述符的集合   
          writefds    //关心的 写操作的 文件描述符的集合  
          exceptfds //关心的 异常操作的 文件描述符的集合 
          timeout   //
                    NULL  --- select 阻塞操作 
                    timeout --- 设定超时时间  > 0  等 这么长时间
                                              == 0 不阻塞 
                    struct timeval {
                           long    tv_sec;         /* seconds */
                           long    tv_usec;        /* microseconds */
                       };    
                                        
       返回值:
          成功 表示 就绪的文件描述符的数量
          失败 -1 && errno设置 
          
       void FD_CLR(int fd, fd_set *set); //clear --- 将fd从 set中清除
       int  FD_ISSET(int fd, fd_set *set); //判断 set 表中 fd是否就绪
       void FD_SET(int fd, fd_set *set); //将fd设置(添加)到set中
       void FD_ZERO(fd_set *set);        //表示将set表清零 

       
       
       注意:
          1. select 函数在监控到 有fd就绪后,
             它会把未就绪的fd清除掉,每次需要重新获取 
          2. 超时一次后,时间的变量值为为0
             如果,需要每次都有超时时间,需要每次重新给值 
             
          

       
//单循环服务器 


//第1路IO  fgets --收键盘 --- 打印出来 --- stdin 能不能读? 
//第2路IO  读管道数据 -- 修改 --- 发回去  --- 管道 能不能读?

select 
1.建一张表 
  放 要监控 的文件描述符
  readfds();  
2.添加 要监控的文件描述符 到表中 
  FD_SET()
3.select 
 
 
eg:
1. 管道双向通信:

    A.c                                B.c
         ---------A2B----------------->
         <--------B2A-----------------


    A.c代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>
//./a.out fifo_A2B fifo_B2A
int main(int argc, const char *argv[])
{
	if (argc != 3)
	{
		printf("Usage: %s <fifo_A2B> <fifo_B2A>\n",argv[0]);
		return -1;
	}

	if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)
	{
		perror("mkfifo fail");
		return -1;
	}
	if(mkfifo(argv[2],0666) < 0 && errno != EEXIST)
	{
		perror("mkfifo fail");
		return -1;
	}

	int fd1 = open(argv[1],O_WRONLY);
	int fd2 = open(argv[2],O_RDONLY);

	if (fd1 < 0 || fd2 < 0)
	{
		perror("open fail");
		return -1;
	}


	//1.准备表 
	fd_set readfds;
	FD_ZERO(&readfds);
    //2.添加要监控的 fd2
	FD_SET(0,&readfds);
	FD_SET(fd2,&readfds);

    int nfds = fd2 + 1;
    int i = 0;
	char buf[1024];
	while (1)
	{
		fd_set backfds = readfds;
		int ret = select(nfds,&backfds,NULL,NULL,NULL);

		if (ret < 0)
		{
			perror("select fail");
			return -1;
		}

		if (ret > 0)
		{
			for (i = 0; i < nfds; ++i)
			{
				if (FD_ISSET(i,&backfds))
				{
					if (i == 0)
					{
						printf("> ");
						fgets(buf,sizeof(buf),stdin);
						buf[strlen(buf)-1] = '\0';
						write(fd1,buf,strlen(buf)+1);

						if (strncmp(buf,"quit",4) == 0)
						{
							printf("1 exit......\n");
							exit(0);
						}

					}else if (i == fd2)
					{
						printf("c> ");
						read(fd2,buf,sizeof(buf));
						printf("%s \n",buf);
						if (strncmp(buf,"quit",4) == 0)
						{
							printf("2 exit......\n");
							exit(0);
						}

					}
				}

			}
			
		}

	}
	return 0;
}

B.c代码如下:
  

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>

//./a.out fifo_A2B fifo_B2A
int main(int argc, const char *argv[])
{
	if (argc != 3)
	{
		printf("Usage: %s <fifo_A2B> <fifo_B2A>\n",argv[0]);
		return -1;
	}

	if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)
	{
		perror("mkfifo fail");
		return -1;
	}
	if(mkfifo(argv[2],0666) < 0 && errno != EEXIST)
	{
		perror("mkfifo fail");
		return -1;
	}

	int fd1 = open(argv[1],O_RDONLY);
	int fd2 = open(argv[2],O_WRONLY);

	if (fd1 < 0 || fd2 < 0)
	{
		perror("open fail");
		return -1;
	}



	//1.准备表 
	fd_set readfds;
	FD_ZERO(&readfds);
    //2.添加要监控的 fd2
	FD_SET(0,&readfds);
	FD_SET(fd1,&readfds);

    int nfds = fd1 + 1;
    int i = 0;
	char buf[1024];
	while (1)
	{
		fd_set backfds = readfds;
		int ret = select(nfds,&backfds,NULL,NULL,NULL);

		if (ret < 0)
		{
			perror("select fail");
			return -1;
		}

		if (ret > 0)
		{
			for (i = 0; i < nfds; ++i)
			{
				if (FD_ISSET(i,&backfds))
				{
					if (i == 0)
					{
						printf("> ");
						fgets(buf,sizeof(buf),stdin);
						buf[strlen(buf)-1] = '\0';
						write(fd2,buf,strlen(buf)+1);

						if (strncmp(buf,"quit",4) == 0)
						{
							printf("1 exit......\n");
							exit(0);
						}

					}else if (i == fd1)
					{
						printf("c> ");
						read(fd1,buf,sizeof(buf));
						printf("%s \n",buf);
						if (strncmp(buf,"quit",4) == 0)
						{
							printf("2 exit......\n");
							exit(0);
						}

					}
				}

			}
			
		}

	}

	return 0;
}

 
      
 

相关文章:

  • 如何使用postman来测试接口
  • 联核科技AGV无人叉车有哪些安全防护措施?
  • 「Unity3D」UGUI将元素固定在,距离屏幕边缘的某个比例,以及保持元素自身比例
  • 概念|RabbitMQ 消息生命周期 待消费的消息和待应答的消息有什么区别
  • 手机号实名认证接口:数字时代的安全与便捷保障
  • 设计模式-结构型模式-桥接模式
  • GB28181视频平台LiveGBS在设置公网IP收流时,如何自定义修改收流端口区间
  • Docker Compose 部署 steamcmd 安装奈斯服务端
  • Kafka,Mq,Redis作为消息队列使用时的差异?|消息队列
  • 计算机视觉应用|自动驾驶的感知革命:多传感器融合架构的技术演进与落地实践
  • 从零开始的python学习(五)P71+P72+P73+P74
  • Redis6.2.6下载和安装
  • ①Modbus TCP转Modbus RTU/ASCII网关同步采集无需编程高速轻松组网
  • 广告营销反欺诈
  • 【Linux】38.网络基础(2.1)
  • K8S学习之基础二十一:k8s的持久化存储之emptyDir
  • 【AI深度学习网络】Transformer时代,RNN(循环神经网络)为何仍是时序建模的“秘密武器”?
  • 【开源】OpenAL、OpenCL、OpenCV 和 OpenGL
  • [machine learning] DP(Data Parallel) vs DDP(Distributed Data Parallel)
  • 25、C++中的多线程同步机制【中高频】
  • 邯郸建设局网站/十大seo公司
  • b站 网站建设/seo顾问是什么
  • 万网怎么建立网站/搜索量用什么工具查询
  • 家具网站开发任务书/外贸网站哪个比较好
  • 大连哪个公司做网站开发的/太原seo哪家好
  • 网站建设 自学 电子版 pdf下载/北京seoqq群