Mongoose笔记——main

 

mongoose执行文件的入口肯定在main.c中(如果将Mongoose嵌入到你的应用程序中,就由你来决定入口了!)。

在典型的main函数入口中,我们可以看到下面的流程:

上面即是main函数中的主流程。需要注意的是调用mg_start()之后返回一个mg_context结构体的实例,这个实例将会在整个连接请求中用到,而且如果你在启动mongoose中设置了参数选项,在下面的process_command_line_arguments()函数中还会对ctx进行修改。从这里我们也知道了,mongoose程序的核心入口时mg_start(),最后终结于mg_stop()。

 

init_server_name()函数

具体实现:

snprintf(server_name, sizeof(server_name), "Mongoose web server v. %s",mg_version());


分析:

涉及函数和变量:

mg_version() 

#define MONGOOSE_VERSION "3.6"

static char server_name[40];        // Set by init_server_name()
mg_version函数的具体实现如下:

const char *mg_version(void)

 {          

       return MONGOOSE_VERSION; 

 }

start_mongoose()函数:

具体实现:

static void start_mongoose(int argc, char *argv[]) {
  char *options[MAX_OPTIONS];
  int i;

  // Edit passwords file if -A option is specified
  if (argc > 1 && !strcmp(argv[1], "-A")) {  //指定-A选项 编辑密码文件
    if (argc != 6) {
      show_usage_and_exit();
    }
    exit(mg_modify_passwords_file(argv[2], argv[3], argv[4], argv[5]) ?
         EXIT_SUCCESS : EXIT_FAILURE);
  }

  // Show usage if -h or --help options are specified
  if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {  // -h 或者-help 指定用法
    show_usage_and_exit();
  }

  /* Update config based on command line arguments */
  process_command_line_arguments(argv, options); //更新配置文件 基于命令行

  /*设置信号: Ctrl-C 退出*/
  signal(SIGTERM, signal_handler);
  signal(SIGINT, signal_handler);

  /* Start Mongoose */
  ctx = mg_start(&mongoose_callback, NULL, (const char **) options);
  for (i = 0; options[i] != NULL; i++) {
    free(options[i]);
  }

  if (ctx == NULL) {
    die("%s", "Failed to start Mongoose."); //打印错误字符串
  }
}

分析:

其中 void show_usage_and_exit 函数:打印退出字符串

static void show_usage_and_exit(void) {
  const char **names;
  int i;

  fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka, built %s\n",
          mg_version(), __DATE__);
  fprintf(stderr, "Usage:\n");
  fprintf(stderr, "  mongoose -A <htpasswd_file> <realm> <user> <passwd>\n");
  fprintf(stderr, "  mongoose <config_file>\n");
  fprintf(stderr, "  mongoose [-option value ...]\n");
  fprintf(stderr, "\nOPTIONS:\n");

  names = mg_get_valid_option_names();
  for (i = 0; names[i] != NULL; i += 3) {
    fprintf(stderr, "  -%s %s (default: \"%s\")\n",
            names[i], names[i + 1], names[i + 2] == NULL ? "" : names[i + 2]);
  }
  fprintf(stderr, "\nSee  http://code.google.com/p/mongoose/wiki/MongooseManual"
          " for more details.\n");
  fprintf(stderr, "Example:\n  mongoose -s cert.pem -p 80,443s -d no\n");
  exit(EXIT_FAILURE);

其中 process_command_line_arguments函数:对ctx进行修改

static void process_command_line_arguments(char *argv[], char **options) {
  char line[MAX_CONF_FILE_LINE_SIZE], opt[sizeof(line)], val[sizeof(line)], *p;
  FILE *fp = NULL;
  size_t i, cmd_line_opts_start = 1, line_no = 0;

  options[0] = NULL;

  // Should we use a config file ?
  if (argv[1] != NULL && argv[1][0] != '-') {
    snprintf(config_file, sizeof(config_file), "%s", argv[1]);
    cmd_line_opts_start = 2;
  } else if ((p = strrchr(argv[0], DIRSEP)) == NULL) {
    // No command line flags specified. Look where binary lives
    snprintf(config_file, sizeof(config_file), "%s", CONFIG_FILE);
  } else {
    snprintf(config_file, sizeof(config_file), "%.*s%c%s",
             (int) (p - argv[0]), argv[0], DIRSEP, CONFIG_FILE);
  }

  fp = fopen(config_file, "r");

  // If config file was set in command line and open failed, die
  if (cmd_line_opts_start == 2 && fp == NULL) {
    die("Cannot open config file %s: %s", config_file, strerror(errno));
  }

  // Load config file settings first
  if (fp != NULL) {
    fprintf(stderr, "Loading config file %s\n", config_file);

    // Loop over the lines in config file
    while (fgets(line, sizeof(line), fp) != NULL) {
      line_no++;

      // Ignore empty lines and comments
      for (i = 0; isspace(* (unsigned char *) &line[i]); ) i++;
      if (line[i] == '#' || line[i] == '\0') {
        continue;
      }

      if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
        printf("%s: line %d is invalid, ignoring it:\n %s",
               config_file, (int) line_no, line);
      } else {
        set_option(options, opt, val);
      }
    }

    (void) fclose(fp);
  }

  // Handle command line flags. They override config file and default settings.
  for (i = cmd_line_opts_start; argv[i] != NULL; i += 2) {
    if (argv[i][0] != '-' || argv[i + 1] == NULL) {
      show_usage_and_exit();
    }
    set_option(options, &argv[i][1], argv[i + 1]);
  }

}

可以指定配置文件 ./mongoose test.txt  如果不指定 ,默认在bin下面寻找 mongoose.conf

可以进行如下配置:

error_log_file error.log  
# cgi_extensions php  
# cgi_environment <value>  
# put_delete_passwords_file <value>  
# cgi_interpreter c:\root\php-cgi.exe  
# protect_uri <value>  
# authentication_domain mydomain.com  
# ssi_extensions .shtml,.shtm  
# access_log_file <value>  
# ssl_chain_file <value>  
# enable_directory_listing yes  
  
  
# global_passwords_file <value>  
#index_files index.html,index.htm,index.cgi  
# enable_keep_alive no  
# access_control_list <value>  
# max_request_size 16384  
  
  
# extra_mime_types <value>  
#端口  
#listening_ports 8088 
#指定网站根目录,指到本人的Apache服务器里(根据自己的需要配置)  
# document_root D:\http  
# ssl_certificate <value>  
# num_threads 10  
# run_as_user <value> 

mg_start()函数:在一个单独的线程里开始Mongoose服务器实例. 返回实例句柄(Mongoose上下文). 这个上下文
在后面同服务器的交互中需要,比如设置选项,停止服务等等

在mg_start()主要是做一些初始化的工作,最后才会正式进入工作服务于client。这里的初始化工作就好比一个人的出生需要十月怀胎,为诞生积蓄能量,要从受精卵长成一个完整的人。在准备工作完成之后,mg_start()会启动一个主线程master_thread,它用于监听所有的client连接请求。

 

具体实现:

struct mg_context *mg_start(mg_callback_t user_callback, void *user_data,
                            const char **options) {
  struct mg_context *ctx;
  const char *name, *value, *default_value;
  int i;


  // Allocate context and initialize reasonable general case defaults.
  // TODO(lsm): do proper error handling here.
  if ((ctx = (struct mg_context *) calloc(1, sizeof(*ctx))) == NULL) {
    return NULL;
  }
  ctx->user_callback = user_callback;
  ctx->user_data = user_data;

  while (options && (name = *options++) != NULL) {
    if ((i = get_option_index(name)) == -1) {
      cry(fc(ctx), "Invalid option: %s", name);
      free_context(ctx);
      return NULL;
    } else if ((value = *options++) == NULL) {
      cry(fc(ctx), "%s: option value cannot be NULL", name);
      free_context(ctx);
      return NULL;
    }
    if (ctx->config[i] != NULL) {
      cry(fc(ctx), "warning: %s: duplicate option", name);
      free(ctx->config[i]);
    }
    ctx->config[i] = mg_strdup(value);
    DEBUG_TRACE(("[%s] -> [%s]", name, value));
  }

  // Set default value if needed
  for (i = 0; config_options[i * ENTRIES_PER_CONFIG_OPTION] != NULL; i++) {
    default_value = config_options[i * ENTRIES_PER_CONFIG_OPTION + 2];
    if (ctx->config[i] == NULL && default_value != NULL) {
      ctx->config[i] = mg_strdup(default_value);
      DEBUG_TRACE(("Setting default: [%s] -> [%s]",
                   config_options[i * ENTRIES_PER_CONFIG_OPTION + 1],
                   default_value));
    }
  }

  // NOTE(lsm): order is important here. SSL certificates must
  // be initialized before listening ports. UID must be set last.
  if (!set_gpass_option(ctx) ||
#if !defined(NO_SSL)
      !set_ssl_option(ctx) ||
#endif
      !set_ports_option(ctx) ||
#if !defined(_WIN32)
      !set_uid_option(ctx) ||
#endif
      !set_acl_option(ctx)) {
    free_context(ctx);
    return NULL;
  }


  // Ignore SIGPIPE signal, so if browser cancels the request, it
  // won't kill the whole process.
  (void) signal(SIGPIPE, SIG_IGN);
  // Also ignoring SIGCHLD to let the OS to reap zombies properly.
  (void) signal(SIGCHLD, SIG_IGN);


  (void) pthread_mutex_init(&ctx->mutex, NULL);
  (void) pthread_cond_init(&ctx->cond, NULL);
  (void) pthread_cond_init(&ctx->sq_empty, NULL);
  (void) pthread_cond_init(&ctx->sq_full, NULL);

  // Start master (listening) thread
  mg_start_thread(master_thread, ctx);

  // Start worker threads
  for (i = 0; i < atoi(ctx->config[NUM_THREADS]); i++) {
    if (mg_start_thread(worker_thread, ctx) != 0) {
      cry(fc(ctx), "Cannot start worker thread: %d", ERRNO);
    } else {
      ctx->num_threads++;
    }
  }

  return ctx;


 

mg_stop()函数:在关闭服务的最后 调用,释放所有相关资源. 这个将会阻塞,直到所有的Mongoose线程停止.

stop_flag表示是否应该停止的标记,它有三个可能的值0、1、2。 stop_flag=0表示 不应该停止,这是初始值;stop_flag=1表示停止,在mg_stop()函数中的一开始设置stop_flag=1,这会触发mg_fini(),且在mg_stop()中会一直等待mg_fini执行完成;stop_flag=2用于通知mg_stop(),mg_fini已经执行完成,stop_flag=2在mg_fini函数中的末尾设置。

具体实现:

void mg_stop(struct mg_context *ctx) {
  ctx->stop_flag = 1;

  // Wait until mg_fini() stops
  while (ctx->stop_flag != 2) {
    (void) mg_sleep(10);
  }
  free_context(ctx);
}

其他函数:

const char *mg_get_option(struct mg_context *context, const char *option_name)
返回特定选项的当前值

int mg_set_option(struct mg_context *context, const char *option_name, const char *option_value)
设置一个特定的选项. 选项值被复制. 选项的值 必须是一个有效的 以nul字节结束的ASCII或者UTF-8字符串. 可以在任意时候改变选项值. 如果选项是未知的,返回-1. 0表示设置失败哦, 1表示成功.

void mg_set_uri_callback(struct mg_context *context, const char *uri_regx, mg_callback_t func, void *user_data)
注册一个URI处理函数. 在uri_regx使用”*”(星号,匹配0到多个字符), 可以处理多个URI
user_data 指针 被作为第 3个参数 传递给 处理函数 .如果为NULL, 那么就删除这个handler

void mg_set_error_callback(struct mg_context *context, int error_code, mg_callback_t func, void *user_data)
注册一个错误处理函数. 比如 “404 Not Found”错误处理. 传递0作为 错误代码,将绑定到所有错误. 错误代码值 在 请求信息作为状态代码 被传递

void mg_set_log_callback(struct mg_context *context, mg_callback_t func)
指定一个函数来接收 所有的服务器的日志信息

void mg_set_ssl_password_callback(struct mg_context *ctx, mg_spcb_t func)
只在SSL许可请求密码时才需要.

void mg_modify_passwords_file(struct mg_context *ctx, const char *file_name, const char *user_name, const char *password)
添加,编辑或者删除 密码文件中的条目. 如果密码不为NULL, 条目将添加(已经存在就是修改)
如果password是NULL, 条目将删除. 返回1表示成功, 0表示错误

处理用户请求
int mg_write(struct mg_connection *, const void *buf, int len)
发送数据给客户. 返回值为 发送的字节个数. 如果发送的字节数< 请求的,或者为-1,表明发生了网络错误, 通常是 远程的客户端关闭了链接.

int mg_printf(struct mg_connection *, const char *fmt, …)
发送数据给客户端, 用printf()的semantics. 注意mg_printf 使用内部的缓冲区大小为
MAX_REQUEST_SIZE(16kb默认), 所以不要往里面打印大于 这个数目的数据. 否则,多出的数据会被截断. 函数的返回值是 发送的字节个数. Error sematics同mg_write()函数

const char *mg_get_header(const struct mg_connection *, const char *header_name)
给出特定的HTTP头部的值

char *mg_get_var(const struct mg_connection *, const char*var_name)
得到特定的表单变量的值. 注意: 这个函数(不像其他函数)会申请内存. 返回的值,必须自己主动用mg_free()释放. 如果这个变量既不再URI查询串,也不在POST数据中出现, 就返回NULL

char *mg_free(void *ptr)
释放 由 mg_get_var()申请的内存

void mg_authorize(struct mg_connection *)
设置authorize标志. 应该只在用mg_protect_uri()注册的回调函数中调用

通用辅助函数
const char *mg_version(void)
返回当前Mongoose version的版本

void mg_show_usage_string(FILE *)
显示 Mongoose 使用方法
命令行版本的使用

启动
mongoose option config_file




备注:参考吴秦的日志,感谢吴秦同学~~

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值