基本shell功能实现(exec系列程序替换函数练习)

shell

  • 功能描述
  • 思路介绍
    • 1.实现常驻进程功能
    • 2.实现命令读取功能
    • 3. 实现命令解析功能
    • 4.实现子进程执行命令功能
    • 5.完善功能
  • 补充内容
    • 让父进程运行内置命令
    • 实现子进程能够获得父进程的环境变量功能(export命令)
    • shell实现重定向功能
  • 全部代码如下:

功能描述

实现一个类似于shell的命令行解释器。通过让子进程执行命令,父进程等待等待并解析命令,从而可以执行类似于“ls”,“ls -a -l -i”,'pwd"等linux指令。

思路介绍

1.实现常驻进程功能

在这里将要实现一个死循环,并且打印出提示信息。

while(1)
{
	//命令行解释器一定是一个常驻内存的进程,不退出
	//打印出提示信息[]
   23     printf("[xty@localhost myshell]#] ");
   24     fflush(stdout);
}

运行如图:
在这里插入图片描述

2.实现命令读取功能

使用fgets函数读取输入的内容,注意要注意把回车给删除(因为fgets会把回车也读取进来)

	 #define NUM 1024
	 //保存完整的命令字符串
	 char cmd_line[NUM];

     //2.获取用户键盘的输入[输入的是各种指令和选项:"ls -a -l -i"]
     memset(cmd_line, '\0', sizeof(cmd_line));
     if(fgets(cmd_line, sizeof cmd_line, stdin) == NULL)
     {
       continue;
     }
     cmd_line[strlen(cmd_line)-1] = '\0';
     //发现有"ls -a -l\n'\o'" 回车换行,我们应该删除这个
     //printf("echo:%s \n", cmd_line);

3. 实现命令解析功能

需要将我们输入的命令行字符串,变成shell能理解的语言。
将命令行选项使用strtok分开,把 "ls -a -l"变成 “ls”, “-a”, “-l”, “NULL”
为后面的execvp作准备。

#define SIZE 32
//保存打散之后的命令行字符串
char *g_argv[SIZE];

//3. 将命令行选项使用strtok分开, "ls -a -l"-> "ls", "-a", "-l", "NULL"
g_argv[0] = strtok(cmd_line, " ");
int index = 1;
while(g_argv[index++] = strtok(NULL, " "));


//检查一下g_argv对不对
for(index = 0; g_argv[index];index++)
{
  //虽然存入的是地址,但是%s,会将它看成字符串打印出来
  printf("g_argv[%d] = %s\n", index, g_argv[index]);
}

结果如下:
在这里插入图片描述

4.实现子进程执行命令功能

子进程执行命令,父进程等待子进程返回。

     //4.让子进程执行命令,执行完后给父进程返回值
     pid_t id = fork();
     if(id==0)
     {
       //子进程,执行命令 
       printf("子进程开始执行任务\n");                                                                     
       execvp(g_argv[0], g_argv);
       exit(1);
     }
    
     //father
     int status = 0;
     pid_t ret = waitpid(id, &status, 0);
     if(ret>0) printf("exit code:%d\n", WEXITSTATUS(status));

5.完善功能

让"ls"有颜色,并且让shell认识"ls"命令。
完整代码如下:

1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<string.h>
5 #include<sys/wait.h>
6 #include<sys/types.h>
7 
8 #define NUM 1024
9 #define SIZE 32
10 //保存完整的命令字符串
11 char cmd_line[NUM];
12 //保存打散之后的命令行字符串
13 char *g_argv[SIZE];
14 
15 
16 //shell运行原理:通过让子进程执行命令,父进程等待&&解析命令
17 int main()
18 {                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
19   //0.命令行解释器一定是一个常驻内存的进程,不退出
20   while(1)
21   {
22     //1.打印出提示信息[]
23     printf("[xty@localhost myshell]#] ");
24     fflush(stdout);
25 
26     //2.获取用户键盘的输入[输入的是各种指令和选项:"ls -a -l -i"]
27     memset(cmd_line, '\0', sizeof(cmd_line));
28     if(fgets(cmd_line, sizeof cmd_line, stdin) == NULL)
29     {
30       continue;
31     }
32     if(cmd_line[0] == '\n')
33     {
34       continue;
35     }
36     cmd_line[strlen(cmd_line)-1] = '\0';
37     //发现有"ls -a -l\n'\o'" 回车换行,我们应该删除这个
38     //printf("echo:%s \n", cmd_line);
39 
40     //3. 将命令行选项使用strtok分开, "ls -a -l"-> "ls", "-a", "-l", "NULL"
41     g_argv[0] = strtok(cmd_line, " ");
42     int index = 1;
43 
44     if(strcmp(g_argv[0], "ls")==0)
45     {
46       g_argv[index++]="--color=auto";
47     }
48     //还可以让编译器支"ll"
49     if(strcmp(g_argv[0],"ll")==0)
50     {
51       g_argv[0] = "ls";
52       g_argv[index++] = "-l";
53       g_argv[index++] = "--color=auto";
54     }
55 
56     //让ls命令有颜色
57     while(g_argv[index++] = strtok(NULL, " "));
58 
59     //检查一下g_argv对不对
60     for(index = 0; g_argv[index];index++)
61     {
62       //虽然存入的是地址,但是%s,会将它看成字符串打印出来
63       printf("g_argv[%d] = %s\n", index, g_argv[index]);
64     }
65 
66     //4.让子进程执行命令,执行完后给父进程返回值
67     pid_t id = fork();
68     if(id==0)
69     {
70       //子进程,执行命令 
71       printf("子进程开始执行任务\n");
72       execvp(g_argv[0], g_argv);
73       exit(1);
74     }
75 
76     //father
77     int status = 0;
78     pid_t ret = waitpid(id, &status, 0);
79     if(ret>0) printf("exit code:%d\n", WEXITSTATUS(status));
80   }
81   return 0;
82 }

这样我们一个简易的shell就完成了。

补充内容

shell执行的命令,通常有两种:

  1. 第三方提供的对应在磁盘中有具体二进制文件的可执行程序(由子进程执行)
  2. shell内部,自己实现的方法,由(父进程)来执行,有些命令会影响到shell本身,比如:“cd”,"export"命令等。

让父进程运行内置命令

当我们执行cd命令时,发现程序并没有改变目录。如下图:
原因是:cd命令被子进程执行了,子进程的当前目录被修改了,但是子进程立马就退出了。但是并没有影响到父进程的当前目录,所以第二次再次运行的时候,子进程还是在父进程的目录创建的,因此没有改变。
在这里插入图片描述

使用chdir函数改变父进程的工作目录。
代码如下:

   66     //让父进程执行cd 命令
   67     if(strcmp(g_argv[0], "cd")==0)
   68     {
   69       if(g_argv[1]!=NULL) chdir(g_argv[1]); // cd .., cd path
   70       continue;                                                                                                                                                   
   71     }    

结果如下图:
在这里插入图片描述

实现子进程能够获得父进程的环境变量功能(export命令)

因export和其他的命令行功能不一样,所以需要再判断一下该功能。并且需要创建新数组保存一下环境变量,因为cmd_line再第二次读取时会清空,会导致getenv时得不到被清空的环境变量(因为存的是指针地址,清空后变成了野指针),因此需要创建数组存储一下!

代码如下:


//myshell.c
 //写一个环境变量的bufer,用来存储临时的环境变量,防止cmd_line被清空
 char my_num[64];
 
 // export MYNUM=111222333
 if(strcmp(g_argv[0], "export")==0 && g_argv[1]!=NULL)
 {
   strcpy(my_num, g_argv[1]);
   int ret = putenv(my_num);
   if (ret == 0) printf("%s export sucess\n", my_num);  //查看一下导入的是什么
   continue;
 }
 

 //env_test.c
  1 #include<stdio.h>
  2 #include<stdlib.h>                                                                                                                                                  
  3 
  4 int main()
  5 {
  6   printf("我是测试环境变量是否成功导入进程\n");
  7   printf("MYNUM= %s \n",getenv("MYNUM"));
  8 
  9 }



在这里插入图片描述

shell实现重定向功能

先检查是否有重定向的功能,然后再执行命令。

//检查命令模块
    //定义标志位的含义                                                                                                                                                                                                                         
   22 #define INPUT_REDIR 1
   23 #define OUTPUT_REDIR 2
   24 #define APPEND_REDIR 3
   25 #define NONE_REDIR 0
   26 int redir_status = NONE_REDIR;
   27 
   28 char *CheckRedir(char *start)
   29 {
   30   assert(start);
   31   char *end = start + strlen(start) - 1;// ls -a -l
   32 
   33   //从后往前找
   34   while(end >= start)
   35   {
   36     if(*end == '>')
   37     {
   38       if(*(end - 1) == '>')
   39       {
   40         redir_status = APPEND_REDIR;
   41         *(end - 1) = '\0';
   42         end++;  //重定向后字符串的首地址
   43         break;
   44       }
   45       redir_status = OUTPUT_REDIR;
   46       *end = '\0';
   47       end++;   // ls -a>myfile.txt   ->ls -a\0myflie.txt
   48       break;
   49     }
   50     else if(*end == '<')
   51     {
   52       redir_status = INPUT_REDIR;
   53       *end = '\0';
   54       end++;
   55       break;
   56     }
   57     else{
   58       end--;
   59     }
   60   }
   61   if(end>=start)
   62   {
   63     return end; //要打开的文件名
   64   }
   65   else{
   66     return NULL;  //没有重定向功能
   67   }
   68 } 





		//main函数内部,子进程执行命令前,多一个重定向打开文件的逻辑:

  142     //4.让子进程执行命令,执行完后给父进程返回值
  143     pid_t id = fork();
  144     if(id==0)
  145     {
  146       //子进程,执行命令 
  147       printf("子进程开始执行任务\n");
  148       if(sep!=NULL)
  149       {
  150         int fd = -1;
  151         //有重定向
  152         switch(redir_status)
  153         {
  154           case INPUT_REDIR:
  155             fd = open(sep, O_RDONLY);
  156             dup2(fd, 0);
  157             break;
  158           case OUTPUT_REDIR:
  159             fd = open(sep, O_WRONLY | O_TRUNC | O_CREAT, 0666);
  160             dup2(fd, 1);
  161             break;
  162           case APPEND_REDIR:
  163             fd = open(sep, O_WRONLY | O_APPEND | O_CREAT, 0666);
  164             dup2(fd, 1);
  165             break;
  166           default:
  167             printf("erro????????\n");
  168             break;
  169         }
  170       }
  171       
  172       execvp(g_argv[0], g_argv);
  173       exit(1);
  174     }
  175    


全部代码如下:

    1 #include<stdio.h>
    2 #include<unistd.h>
    3 #include<stdlib.h>
    4 #include<string.h>
    5 #include<assert.h>
    6 #include<fcntl.h>
    7 #include<sys/wait.h>
    8 #include<sys/stat.h>
    9 #include<sys/types.h>
   10 
   11 #define NUM 1024
   12 #define SIZE 32
   13 //保存完整的命令字符串
   14 char cmd_line[NUM];
   15 //保存打散之后的命令行字符串
   16 char *g_argv[SIZE];
   17 
   18 //写一个环境变量的bufer,用来存储临时的环境变量,防止cmd_line被清空
   19 char my_num[64];
   20 
   21 //定义标志位的含义
   22 #define INPUT_REDIR 1
   23 #define OUTPUT_REDIR 2
   24 #define APPEND_REDIR 3
   25 #define NONE_REDIR 0
   26 int redir_status = NONE_REDIR;
   27 
   28 char *CheckRedir(char *start)
   29 {
   30   assert(start);
   31   char *end = start + strlen(start) - 1;// ls -a -l
   32   
   33   //从后往前找
   34   while(end >= start)
   35   {
   36     if(*end == '>')
   37     {
   38       if(*(end - 1) == '>')
   39       {
   40         redir_status = APPEND_REDIR; 
   41         *(end - 1) = '\0';
   42         end++;  //重定向后字符串的首地址
   43         break;
   44       }
   45       redir_status = OUTPUT_REDIR;
   46       *end = '\0';
   47       end++;   // ls -a>myfile.txt   ->ls -a\0myflie.txt
   48       break;
   49     }
   50     else if(*end == '<')
   51     {
   52       redir_status = INPUT_REDIR;
   53       *end = '\0';
   54       end++;
   55       break;
   56     }
   57     else{
   58       end--;
   59     }
   60   }
   61   if(end>=start)
   62   {
   63     return end; //要打开的文件名
   64   }
   65   else{
   66     return NULL;  //没有重定向功能
   67   }
   68 } 
   69 
   70 //shell运行原理:通过让子进程执行命令,父进程等待&&解析命令
   71 int main()
   72 {
   73   //0.命令行解释器一定是一个常驻内存的进程,不退出
   74   while(1)
   75   {
   76     //1.打印出提示信息[]
   77     printf("[xty@localhost myshell]#] ");
   78     fflush(stdout);
   79 
   80     //2.获取用户键盘的输入[输入的是各种指令和选项:"ls -a -l -i"]
   81     memset(cmd_line, '\0', sizeof(cmd_line));
   82     if(fgets(cmd_line, sizeof cmd_line, stdin) == NULL)
   83     {
   84       continue;
   85     }
   86     if(cmd_line[0] == '\n')
   87     {
   88       continue;
   89     }
   90     cmd_line[strlen(cmd_line)-1] = '\0';
   91     //发现有"ls -a -l\n'\o'" 回车换行,我们应该删除这个
   92     //printf("echo:%s \n", cmd_line);
   93     
   94 
   95     //在分析指令之前就检查有没有重定向
   96     char* sep = CheckRedir(cmd_line);
   97     
   98     //3. 将命令行选项使用strtok分开, "ls -a -l"-> "ls", "-a", "-l", "NULL"
   99     g_argv[0] = strtok(cmd_line, " ");
  100     int index = 1;
  101 
  102     if(strcmp(g_argv[0], "ls")==0)
  103     {
W>104       g_argv[index++]="--color=auto";
  105     }
  106     //还可以让编译器支"ll"
  107     if(strcmp(g_argv[0],"ll")==0)
  108     {
W>109       g_argv[0] = "ls";
W>110       g_argv[index++] = "-l";
W>111       g_argv[index++] = "--color=auto";
  112     }
  113 
  114     //让ls命令有颜色
W>115     while(g_argv[index++] = strtok(NULL, " "));
  116     
  117     检查一下g_argv对不对
  118     //for(index = 0; g_argv[index];index++)
  119     //{
  120     //  //虽然存入的是地址,但是%s,会将它看成字符串打印出来
  121     //  printf("g_argv[%d] = %s\n", index, g_argv[index]);
  122     //}
  123 
  124 
  125     // export MYNUM=111222333
  126     if(strcmp(g_argv[0], "export")==0 && g_argv[1]!=NULL)
  127     {
  128       strcpy(my_num, g_argv[1]);
  129       int ret = putenv(my_num);
  130       if (ret == 0) printf("%s export sucess\n", my_num);  //查看一下导入的是什么
  131       continue;
  132     }
  133 
  134 
  135 
  136     //让父进程执行cd 命令
  137     if(strcmp(g_argv[0], "cd")==0)
  138     {
  139       if(g_argv[1]!=NULL) chdir(g_argv[1]); // cd .., cd path
  140       continue;
  141     }
  142     //4.让子进程执行命令,执行完后给父进程返回值
  143     pid_t id = fork();
  144     if(id==0)
  145     {
  146       //子进程,执行命令 
  147       printf("子进程开始执行任务\n");
  148       if(sep!=NULL)
  149       {
  150         int fd = -1;
  151         //有重定向
  152         switch(redir_status)
  153         {
  154           case INPUT_REDIR:
  155             fd = open(sep, O_RDONLY);
  156             dup2(fd, 0);
  157             break;
  158           case OUTPUT_REDIR:
  159             fd = open(sep, O_WRONLY | O_TRUNC | O_CREAT, 0666);
  160             dup2(fd, 1);
  161             break;
  162           case APPEND_REDIR:
  163             fd = open(sep, O_WRONLY | O_APPEND | O_CREAT, 0666);
  164             dup2(fd, 1);
  165             break;
  166           default:
  167             printf("erro????????\n");
  168             break;
  169         }
  170       }
  171 
  172       execvp(g_argv[0], g_argv);
  173       exit(1);
  174     }
  175 
  176     //father
  177     int status = 0;
  178     pid_t ret = waitpid(id, &status, 0);
  179     if(ret>0) printf("exit code:%d\n", WEXITSTATUS(status));
  180   }
  181   return 0;
  182 }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/260433.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CentOS7 安装 DockerCompose

目录 一、安装Docker 二、安装步骤 2.1 卸载 2.2 安装docker 2.3 启动docker 2.4 配置镜像加速器 一、安装Docker Docker 分为 CE 和 EE 两大版本。 CE 即社区版(免费&#xff0c;支持周期7个月)EE 即企业版强调安全&#xff0c;付费使用&#xff0c;支持周期 24 个月…

Linux之进程(五)(进程控制)

目录 一、进程创建 1、fork函数创建进程 2、fork函数的返回值 3、fork常规用法 4、fork调用失败的原因 二、进程终止 1、进程终止的方式 2、进程退出码 3、进程的退出方法 三、进程等待 1、进程等待的必要性 2、wait函数 3、waitpid函数 四、进程程序替换 1、概念…

Java如何将中文转化为拼音

Java中可以使用第三方库pinyin4j来实现中文转拼音。 首先&#xff0c;需要引入pinyin4j的jar包&#xff0c;可以在pinyin4j的官方网站&#xff08;http://pinyin4j.sourceforge.net/&#xff09;下载&#xff0c;也可以通过Maven引入。 Maven引入依赖&#xff1a; <depend…

aws配置以及下载 spaceNet6 数据集

一&#xff1a;注册亚马逊账号 注册的时候&#xff0c;唯一需要注意的是信用卡绑定&#xff0c;这个可以去淘宝买&#xff0c;搜索aws匿名卡。 注册完记得点击登录&#xff0c;记录一下自己的账户ID哦&#xff01; 二&#xff1a;登录自己的aws账号 2.1 首先创建一个用户 首…

数字化指南:数据可视化助力网店腾飞

数据可视化对于网店经营具有重要意义。它不仅仅是一种呈现数据的方式&#xff0c;更是提升网店运营效率和业绩的有力工具。下面我就以可视化从业者的角度&#xff0c;简单聊聊数据可视化能够给网店经营带来的帮助。 在网店经营中&#xff0c;数据可视化能够带来多方面的帮助。…

【PHP入门】2.2 流程控制

-流程控制- 流程控制&#xff1a;代码执行的方向 2.2.1控制分类 顺序结构&#xff1a;代码从上往下&#xff0c;顺序执行。&#xff08;代码执行的最基本结构&#xff09; 分支结构&#xff1a;给定一个条件&#xff0c;同时有多种可执行代码&#xff08;块&#xff09;&am…

基于MLP完成CIFAR-10数据集和UCI wine数据集的分类

基于MLP完成CIFAR-10数据集和UCI wine数据集的分类&#xff0c;使用到了sklearn和tensorflow&#xff0c;并对图片分类进行了数据可视化展示 数据集介绍 UCI wine数据集&#xff1a; http://archive.ics.uci.edu/dataset/109/wine 这些数据是对意大利同一地区种植的葡萄酒进…

Ubuntu 常用命令之 echo 命令用法介绍

echo 是一个在 Ubuntu 系统下常用的命令&#xff0c;主要用于在终端输出字符串或者变量。 echo 的基本语法 echo [option] [string]echo 命令的参数包括 -n&#xff1a;不输出结尾的换行符。-e&#xff1a;启用反斜杠转义字符。-E&#xff1a;禁用反斜杠转义&#xff08;这是…

【论文解读】Efficient SAO Coding Algorithm for x265 Encoder

时间&#xff1a;2015年 级别&#xff1a;IEEE 机构&#xff1a;上海交通大学 摘要 x265是一款开源的HEVC编码器&#xff0c;采用了多种优化技术&#xff0c;具有较快的编码速度和优良的编码性能。作为HEVC的一项关键技术&#xff0c;x265还采用了样本自适应偏移(sample adap…

c++ qt 模态框和阻拦器 优先级 问题 修复 已解决

在c项目中。有 加载动画 和 模态框提醒的功能, 导致发生一个问题&#xff0c;有提示框的时候&#xff0c;动画也停止&#xff0c;必须点击 按钮 所有代码才能有效。 解决办法 谨慎使用 deleteLater,因为和模态框拦截有冲突, 使用 隐藏 或者 删除指针。 deleteLater 使用逻辑是 …

自动气象监测站助力生活生产

随着科技的发展&#xff0c;我们的生活和生产方式正在发生着日新月异的变化。其中&#xff0c;WX-CQ12 自动气象监测站作为一项气象监测设备&#xff0c;正在发挥着越来越重要的作用。它不仅为我们提供了更加准确、实时的天气信息&#xff0c;还为农业、交通、旅游等领域提供了…

全新「机械手」算法:辅助花式抓杯子,GTX 1650实现150fps推断

新方法结合扩散模型和强化学习&#xff0c;将抓取问题分解为「如何抓」以及「何时抓」&#xff0c;平价显卡即可实现实时交互。 手是人类与世界交互的重要部分&#xff0c;手的缺失&#xff08;如上肢残障&#xff09;会大大影响人类的正常生活。 北京大学董豪团队通过将扩散模…

stm32学习总结:4、Proteus8+STM32CubeMX+MDK仿真串口收发

stm32学习总结&#xff1a;4、Proteus8STM32CubeMXMDK仿真串口收发 文章目录 stm32学习总结&#xff1a;4、Proteus8STM32CubeMXMDK仿真串口收发一、前言二、资料收集三、STM32CubeMX配置串口1、配置开启USART12、设置usart中断优先级3、配置外设独立生成.c和.h 四、MDK串口收发…

VS ASP.Net Core项目还原Packages包到本地(解决服务器没有网无法重新生成的问题)

问题背景 ASP.Net Core MVC项目&#xff0c;无法重新生成。 现场服务器没有网,放上去的代码无法通过nuget还原包到服务器&#xff0c;导致无法编译无法运行。 解决办法 将Packages还原到本机&#xff08;有网&#xff09;&#xff0c;然后再将代码放到服务器运行。 在有网的…

内网渗透测试基础——分析域内网段划分情况及拓扑结构

内网渗透测试基础——分析域内网段划分情况及拓扑结构 ​ 掌握了内网的相关信息后&#xff0c; 渗透测试人员可以分析目标网络的结构和安全防御策略&#xff0c;获取网段信息、各部门的IP地址段&#xff0c;并尝试绘制内网的拓扑结构图。当然&#xff0c;渗透测试人员无法了解…

深入了解Java中的锁机制

目录 1. synchronized关键字 1.1 基本概念 1.2 内置锁 1.3 限制 2. ReentrantLock 2.1 概述 2.2 公平性与非公平性 2.3 条件变量 3. 读写锁&#xff08;ReadWriteLock&#xff09; 3.1 概念 3.2 适用场景 4. StampedLock 4.1 概述 4.2 乐观读与悲观读 4.3 适用场…

仓储1代电子标签接口文档

标签注册 仓储1代注册 侧面按钮连按三次&#xff0c; 注册成功&#xff1a;红灯变绿灯 查询电子标签信息接口 接口地址&#xff1a;192.168.1.200/wms/associate/getTagsMsg 请求类型&#xff1a;multipart/form-data 请求方式&#xff1a;get 接口备注&#xff1a;暂无描…

如何设计用户评论表

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 上一篇提到树形结构是非…

C++_动态二维数组的两种方法

介绍 本文主要介绍使用 动态二维数组的两种方法 (PS:仅作创建 动态二维数组参考,详细使用方法根据需求自行改变) 第一种&#xff1a;连续存储结构的 二维动态数组(需固定 列 大小&#xff0c;可通过下标访问) 缺点: 1.需要在设计二维数组前写死 列 的大小 2.空间利用率不高 优点…

netty源码:(29)ChannelInboundHandlerAdapter

它实现的方法都有一个ChannelHandlerContext参数&#xff0c;它的方法都是直接调用ChannelHandlerContext参数对应的方法&#xff0c;该方法会调用下一个handler对应的方法。 可以继承这个类&#xff0c;重写感兴趣的方法,比如channelRead. 这个类有个子类&#xff1a;SimpleC…