http监听socket的初始化
来源:互联网 发布:csgo武器数据 编辑:程序博客网 时间:2024/06/06 02:31
//监听的配置信息,在ngx_http_core_main_conf_t结构的ports数组中将会保存所有的端口监听配置信息typedef struct { ngx_int_t family; in_port_t port; //端口号 ngx_array_t addrs; /* array of ngx_http_conf_addr_t */ //数组,存放所有当前端口的地址结构} ngx_http_conf_port_t;在nginx的http部分的核心模块ngx_http_core_module的srv_conf配置结构ngx_http_core_main_conf_t中,有一个ports数组,其就是用来存储上述结构体的,nginx将会建立这个数组然后来创建监听socket。
//地址结构typedef struct { ngx_http_listen_opt_t opt; //监听的配置结构//一些相关的hash变量,例如servername与ngx_http_core_srv_conf_t ngx_hash_t hash; ngx_hash_wildcard_t *wc_head; ngx_hash_wildcard_t *wc_tail;#if (NGX_PCRE) ngx_uint_t nregex; ngx_http_server_name_t *regex;#endif /* the default server configuration for this address:port */ ngx_http_core_srv_conf_t *default_server; //这个地址的默认server ngx_array_t servers; /* array of ngx_http_core_srv_conf_t */ //该地址的所有对应的server} ngx_http_conf_addr_t;在ngx_http_conf_port_t结构的最后一个域addrs是一个数组,其保存的就是上述结构,
//监听的配置结构typedef struct { union { struct sockaddr sockaddr; //通用套接字地质结构 struct sockaddr_in sockaddr_in; //网际套接字地质结构#if (NGX_HAVE_INET6) struct sockaddr_in6 sockaddr_in6;#endif#if (NGX_HAVE_UNIX_DOMAIN) struct sockaddr_un sockaddr_un;#endif u_char sockaddr_data[NGX_SOCKADDRLEN]; } u; socklen_t socklen; //地址结构长度 unsigned set:1; unsigned default_server:1; unsigned bind:1; //绑定标志 unsigned wildcard:1;#if (NGX_HTTP_SSL) unsigned ssl:1;#endif#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) unsigned ipv6only:2;#endif unsigned so_keepalive:2; int backlog; int rcvbuf; int sndbuf;#if (NGX_HAVE_SETFIB) int setfib;#endif#if (NGX_HAVE_KEEPALIVE_TUNABLE) int tcp_keepidle; int tcp_keepintvl; int tcp_keepcnt;#endif#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) char *accept_filter;#endif#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) ngx_uint_t deferred_accept;#endif u_char addr[NGX_SOCKADDR_STRLEN + 1];} ngx_http_listen_opt_t;
这个对应的是地址结构ngx_http_conf_addr_t的opt域。
好了,看完这些重要的配置结构,我们可以开始着手看如何初始化http监听了,我们知道在server块命令中,有两个重要的命令,server_name与listen,server_name命令用来实现虚拟主机的功能,用来设置每个server块的虚拟主机名。listen命令则用来设置监听socket的信息。
我们先来看server_name的回调函数ngx_http_core_server:
value = cf->args->elts; //获取解析出来的参数 for (i = 1; i < cf->args->nelts; i++) { ch = value[i].data[0]; if ((ch == '*' && (value[i].len < 3 || value[i].data[1] != '.')) || (ch == '.' && value[i].len < 2)) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "server name \"%V\" is invalid", &value[i]); return NGX_CONF_ERROR; } if (ngx_strchr(value[i].data, '/')) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "server name \"%V\" has suspicious symbols", &value[i]); } sn = ngx_array_push(&cscf->server_names); //相当于是在srv_conf 的server_names数组中分配一个元素的空间,用来保存当前的server_name if (sn == NULL) { return NGX_CONF_ERROR; } sn->server = cscf;//将当前的server_name的所属srv_conf设置 if (ngx_strcasecmp(value[i].data, (u_char *) "$hostname") == 0) { sn->name = cf->cycle->hostname; } else { sn->name = value[i]; //赋值 }上面是截取的主要代码,说白了就是根据解析出来的参数创建一个ngx_http_server_name_t结构,然后将其压入到当前server块命令创建的ngx_http_core_srv_conf_t结构的server_names数组当中。
嗯,接下来看listen命令的回调函数ngx_http_core_listen(这个就比较重要了):
ngx_http_core_srv_conf_t *cscf = conf; //获取当前的srv_conf结构,这个ngx_http_core_srv_conf_t为当前server自己创建的ngx_http_core_srv_conf_t ngx_str_t *value, size; ngx_url_t u; //url结构 ngx_uint_t n; ngx_http_listen_opt_t lsopt; //用来存储监听套接字的配置信息 cscf->listen = 1; //表示当前的server配置已经进行了listen配置,如果不listen配置的话,那么会安排默认的端口监听 value = cf->args->elts; //获取解析出来的参数 ngx_memzero(&u, sizeof(ngx_url_t)); u.url = value[1]; //获取传进来的地址(ip+port) u.listen = 1; u.default_port = 80; //默认端口 if (ngx_parse_url(cf->pool, &u) != NGX_OK) { //相当于是对u进行初始化,比如说ip地址,端口号等 if (u.err) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s in \"%V\" of the \"listen\" directive", u.err, &u.url); } return NGX_CONF_ERROR; }上面的代码首先是获取当前server块命令创建的ngx_http_core_srv_conf_t结构,然后用listen后面的参数(一般情况下是ip+端口号的形式)来初始化url结构的url参数,另外还创建了一个监听配置信息ngx_http_listen_opt_t结构。然后还调用了ngx_parse_url函数,通过ip地址,端口号等将url结构最终转化为socket地址结构等。
ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t)); ngx_memcpy(&lsopt.u.sockaddr, u.sockaddr, u.socklen); //为lsopt的地址结构赋值//初始化监听套接字的配置 lsopt.socklen = u.socklen; //socket地址结构的长度 lsopt.backlog = NGX_LISTEN_BACKLOG; lsopt.rcvbuf = -1; //接收缓冲 lsopt.sndbuf = -1;#if (NGX_HAVE_SETFIB) lsopt.setfib = -1;#endif lsopt.wildcard = u.wildcard;//将二进制的地址结构转换为文本的形式 (void) ngx_sock_ntop(&lsopt.u.sockaddr, lsopt.addr, NGX_SOCKADDR_STRLEN, 1);接下来就是通过转换出来的地址结构来初始化ngx_http_listen_opt_t结构,接下来的代码还有一些对其的初始化,但是没什么意思,就不贴出来了,最后比较重要的一段代码是:
if (ngx_http_add_listen(cf, cscf, &lsopt) == NGX_OK) { //加入监听套接字,将会创建ngx_http_conf_port_t结构,并将其放入到ngx_http_core_main_conf_t的ports数组当中 return NGX_CONF_OK; }通过调用ngx_http_add_listen函数,将会创建ngx_http_conf_port_t结构,并且还会把它保存到ngx_http_core_main_conf_t结构的ports数组当中,这样nginx就能通过这个数组来创建监听了。
好了接下来看ngx_http_add_listen函数吧。
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); //获取ngx_http_core_module的main_conf 配置结构//初始化ngx_http_core_module的ports数组 if (cmcf->ports == NULL) { //如果其ports的数组还没有初始化 cmcf->ports = ngx_array_create(cf->temp_pool, 2, sizeof(ngx_http_conf_port_t)); if (cmcf->ports == NULL) { return NGX_ERROR; } }首先是获取ngx_http_core_main_conf_t结构,这里需要注意的是,前面的文章我们已经说过了,虽然在server命令快中也会创建自己的ngx_http_conf_ctx_t结构,但是它们所指向的main_conf 都是统一的,因为这里的ngx_http_core_main_conf_t结构其实也是唯一的。接下来还要判断当前期的ports数组是否为空,如果为空的话那么还要初始化这个数组。
//获取网际地址结构 sa = &lsopt->u.sockaddr;//判断地址结构的类型 switch (sa->sa_family) {#if (NGX_HAVE_INET6) case AF_INET6: sin6 = &lsopt->u.sockaddr_in6; p = sin6->sin6_port; break;#endif#if (NGX_HAVE_UNIX_DOMAIN) case AF_UNIX: p = 0; break;#endif default: /* AF_INET */ sin = &lsopt->u.sockaddr_in; //获取IPV4地址结构 p = sin->sin_port; //获取端口号 break; }接下来的代码是从lsopt中获取通用套接字地址结构,然后判断类型,并获取最终的IPV4地址结构以及端口号。
port = cmcf->ports->elts;//遍历ports数组,看要添加的端口号信息是否存在,如果已经存在的话,调用ngx_http_add_addresses函数将相应的地址信息加入到port上就可以的 for (i = 0; i < cmcf->ports->nelts; i++) { if (p != port[i].port || sa->sa_family != port[i].family) { continue; } /* a port is already in the port list *///如果端口的信息已经存在于ports数组当中了,那么只需要添加一个地址结构就可以了 return ngx_http_add_addresses(cf, cscf, &port[i], lsopt); }接下来是遍历ngx_http_core_main_conf_t的ports数组中的所有ngx_http_conf_port_t结构,看是否已经有相同的端口的信息了,如果有的话,那么只需要调用ngx_http_add_addresses函数来在当前ngx_http_conf_port_t结构中添加一个地址信息就可以了。如果没有相同的端口的信息的话,那么就需要以下的代码在ports数组中添加一个ngx_http_conf_port_t结构。
//表示要添加的端口信息并没有存在,那么需要在ports数组中加入一个元素,并进行初始化,并还要添加地址信息 port = ngx_array_push(cmcf->ports); if (port == NULL) { return NGX_ERROR; }//相当于是为刚刚压入数组的元素赋值 port->family = sa->sa_family; port->port = p; //端口号 port->addrs.elts = NULL; //将addres数组置空//添加地址信息 return ngx_http_add_address(cf, cscf, port, lsopt); //为该端口添加地址结构上述代码就是用来添加一个ngx_http_conf_port_t结构,然后最后依然要调用ngx_http_add_address为其添加地址结构。嗯,接下来我们先来看ngx_http_add_addresses函数:
* 在port的addr数组中已经存在该地址时,直接将ngx_http_core_srv_conf_t * 结构添加到到addr对应的servers数组中。 */ for (i = 0; i < port->addrs.nelts; i++) { //比较地址是否相同 if (ngx_memcmp(p, addr[i].opt.u.sockaddr_data + off, len) != 0) { continue; } /* the address is already in the address list *///将当前的ngx_http_core_srv_conf_t直接压入到addr的servers数组中就行了 if (ngx_http_add_server(cf, cscf, &addr[i]) != NGX_OK) { return NGX_ERROR; }首先是循环ports的addrs数组,找到是否有地址相同的,如果有的话,那么就好办了,只用调用ngx_http_add_server函数,将当前的srv_conf结构压入到addr的servers数组就可以了。当然如果最后找不到相同的地址的话,其实还是要调用ngx_http_add_address函数,在port的addrs数组添加一项。
接下来来看ngx_http_add_address函数吧:
/* * add the server address, the server names and the server core module * configurations to the port list *///添加地址信息,该函数用于创建一个地址结构ngx_http_conf_addr_t,并将其加入到port的addrs数组当中,并初始化地址结构static ngx_int_tngx_http_add_address(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, ngx_http_conf_port_t *port, ngx_http_listen_opt_t *lsopt){ ngx_http_conf_addr_t *addr; //创建一个地址配置结构//如果当前的port配置结构的addrs数组为空,那么初始化它 if (port->addrs.elts == NULL) { if (ngx_array_init(&port->addrs, cf->temp_pool, 4, sizeof(ngx_http_conf_addr_t)) != NGX_OK) { return NGX_ERROR; } } addr = ngx_array_push(&port->addrs); //压入ngx_http_conf_port_t结构的addrs数组当中 if (addr == NULL) { return NGX_ERROR; }//为压入的地址结构赋值 addr->opt = *lsopt; // addr->hash.buckets = NULL; addr->hash.size = 0; addr->wc_head = NULL; addr->wc_tail = NULL;#if (NGX_PCRE) addr->nregex = 0; addr->regex = NULL;#endif addr->default_server = cscf; //设置默认的server addr->servers.elts = NULL; return ngx_http_add_server(cf, cscf, addr); //该函数用于将当前的ngx_http_core_srv_conf_t结构压入到地质结构addr的servers数组中,表示当前的地址结构增加一个server}
该函数,一看代码应该就知道是什么意思了吧,说白了就是创建一个ngx_http_conf_addr_t地址结构,然后将其压入到当前port的addrs数组当中,然后当然还要对当前的地址结构进行一些初始化,最后调用ngx_http_add_server函数将当前的ngx_http_core_srv_conf_t压入到地址结构的servers数组当中就可以了。
嗯,上面写的很多了,我们来回顾一下监听初始化的解析配置文件的过程吧。
(1)调用listen命令的回调函数ngx_http_core_listen,根据解析配置文件获取的ip+port来创建监听套接字的配置结构ngx_http_listen_opt_t
(2)调用ngx_http_add_listen函数,判断当前ngx_http_core_main_conf_t结构中ports数组是否有相应端口的信息,如果已经有了的话,那么只需要调用ngx_http_add_addresses函数,在对应的ngx_http_conf_port_t结构中加入相应的地址信息就行了。如果没有的话那么就得创建一个ngx_http_conf_port_t结构,并且把其保存到ngx_http_core_main_conf_t的ports数组当中,然后再调用ngx_http_add_address函数为其添加地址信息。
(3)对于ngx_http_add_addresses以及ngx_http_add_address函数,其实说白了就是在ngx_http_conf_port_t结构的addrs数组中添加一个ngx_http_conf_addr_t结构,即地址结构,并且还会调用ngx_http_add_server函数为其配置虚拟主机的信息,nginx也会根据这个地址结构来创建监听socket。
最后,就是在ngx_http_core_main_conf_t结构中会有一个ports数组保存所有的ngx_http_conf_port_t结构,然后再ngx_http_conf_port_t结构中又会有一个addrs数组,保存端口对应的所有地址结构ngx_http_conf_addr_t(嗯,毕竟是服务器嘛,有几个网卡都是正常的)。接着对于一个ngx_http_conf_addr_t结构,又会有一个servers数组来保存其对应的所有虚拟主机的配置信息。
好了,监听部分的配置文件解析就差不多了,接下来可以开始具体如何创建监听结构,即ngx_listening_t结构了,在http命令的回调函数ngx_http_block最后会调用ngx_http_optimize_servers函数来完成这个过程。接下来我们就来看看这个函数吧。
//该函数相当于是遍历ngx_http_core_main_conf_t结构中的ports数组,然后创建监听,并还要将它们保存到cycle变量的listening数组当中去static ngx_int_tngx_http_optimize_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf, ngx_array_t *ports){ ngx_uint_t p, a; ngx_http_conf_port_t *port; //监听配置结构 ngx_http_conf_addr_t *addr; //地址配置结构 if (ports == NULL) { return NGX_OK; } port = ports->elts;//遍历ngx_http_core_main_conf_t结构中的ports数组中所有ngx_http_conf_port_t结构 for (p = 0; p < ports->nelts; p++) { /** * 将addrs排序,带通配符的地址排在后面 */ ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts, sizeof(ngx_http_conf_addr_t), ngx_http_cmp_conf_addrs); /* * check whether all name-based servers have the same * configuration as a default server for given address:port */ addr = port[p].addrs.elts; for (a = 0; a < port[p].addrs.nelts; a++) { if (addr[a].servers.nelts > 1#if (NGX_PCRE) || addr[a].default_server->captures#endif ) { /** * 初始addr(ngx_http_conf_addr_t)中的hash、wc_head和wc_tail哈希表。 * 这些哈希表以server name(虚拟主机名)为key,server块的ngx_http_core_srv_conf_t为 * value,用于在处理请求时,根据请求的host请求行快速找到处理该请求的server配置结构。 */ if (ngx_http_server_names(cf, cmcf, &addr[a]) != NGX_OK) { return NGX_ERROR; } } }//初始化监听 if (ngx_http_init_listening(cf, &port[p]) != NGX_OK) { return NGX_ERROR; } } return NGX_OK;}函数还是相对比较简单的,基本上注释也都说的比较清楚了,函数还会调用ngx_http_init_listening函数来具体的根据每一个ngx_http_conf_port_t结构来创建监听,接下来我们看看ngx_http_init_listening函数吧。
//遍历ngx_http_conf_port_t结构addrs数组中的所有ngx_http_conf_addr_t结构,并根据它来创建监听 while (i < last) { if (bind_wildcard && !addr[i].opt.bind) { i++; continue; }//添加监听,即在cycle变量中添加一个ngx_listening_t结构 ls = ngx_http_add_listening(cf, &addr[i]); if (ls == NULL) { return NGX_ERROR; } hport = ngx_pcalloc(cf->pool, sizeof(ngx_http_port_t)); if (hport == NULL) { return NGX_ERROR; } ls->servers = hport; if (i == last - 1) { hport->naddrs = last; } else { hport->naddrs = 1; i = 0; } switch (ls->sockaddr->sa_family) {#if (NGX_HAVE_INET6) case AF_INET6: if (ngx_http_add_addrs6(cf, hport, addr) != NGX_OK) { return NGX_ERROR; } break;#endif default: /* AF_INET */ if (ngx_http_add_addrs(cf, hport, addr) != NGX_OK) { return NGX_ERROR; } break; } addr++; last--; }上述代码的主要内容就是通过遍历ngx_http_conf_port_t结构addrs数组中的所有ngx_http_conf_addr_t结构,并根据它来创建监听(调用ngx_http_add_listening函数),而且还要初始化ngx_listenting_t的servers域,用它来指明每一个监听socket的虚拟主机的信息。我们再来看看ngx_http_add_listening函数:
//该函数用于根据地址结构创建ngx_listening_tstatic ngx_listening_t *ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr){ ngx_listening_t *ls; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf;//创建一个监听,该监听结构会直接保存在cycle变量的listening数组当中去 ls = ngx_create_listening(cf, &addr->opt.u.sockaddr, addr->opt.socklen); if (ls == NULL) { return NULL; } ls->addr_ntop = 1; ls->handler = ngx_http_init_connection; //listen监听的处理函数,这里表示用这个函数来预处理刚刚accept的连接分配的connection cscf = addr->default_server; ls->pool_size = cscf->connection_pool_size; ls->post_accept_timeout = cscf->client_header_timeout; clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index]; ls->logp = clcf->error_log; ls->log.data = &ls->addr_text; ls->log.handler = ngx_accept_log_error;上述代码是截取的最主要的代码,说白了就是调用ngx_create_listening函数,在cycle的listening数组中添加一个ngx_listenting_t结构,然后还要初始化其的一些基本信息。并且还要将其的hander设置为ngx_http_init_connection函数,这个我们在前面讲事件循环的时候说到过,当监听端口获取一个连接,并为其分配connection之后,会调用这个handler来处理这个connection。
好了,这样子的话就创建了监听结构,并将其保存到了cycle中去了,然后http的监听socket初始化就完事了。至于最后是怎么打开socket的前面的文章已经说过了。
嗯,接下来的文章可以具体来分析http部分了。
- http监听socket的初始化
- 监听socket初始化
- 一个基于Socket的http请求监听程序实现
- nginx源码分析(5)——监听socket初始化
- nginx 监听socket的继承
- 16.3.1 socket()的初始化
- socket http的理解
- http、socket的区别
- Socket监听
- 监听socket
- socket监听
- Socket的监听的三种方式
- 监听本地回路socket的工具RawCap
- select监听多个socket的例子
- 监听socket(bind()的backlog参数)
- 7-socket的实践到内核--socket的监听
- 7-socket的实践到内核--socket的监听 .
- C# Socket系列一 简单的创建socket的监听
- UIbutton 按钮做3D旋转动画 CATransform3DCAAnimation
- Android系统--事件读取及分发
- Linux下VMware workstation使用技巧三则
- linux debug fs(1)
- linux debug fs (2)----Linux驱动调试的Debugfs的使用简介
- http监听socket的初始化
- SVM运用
- Eclipse CDT生成、调用C动态库
- 检测流文件结束符
- VirtualBox中Windows 7虚拟机不能上网怎么办?
- xcode APP 打包以及提交apple审核详细流程(新版本更新提交审核)
- mysql常用命令
- C8051F34X IO 引脚初始化步骤
- C++第11周项目2(7)参考——都要学C