/*
Nginx中定义了基本的数据结构ngx_connection_t来表示连接,这个连接表示是客户端主动发起的、Nginx服务器被动接受的TCP连接,我们可以简单称
其为被动连接。同时,在有些请求的处理过程中,Nginx会试图主动向其他上游服务器建立连接,并以此连接与上游服务器通信,因此,这样的
连接与ngx_connection_t又是不同的,Nginx定义了}ngx_peer_connection_t结构体来表示主动连接,当然,ngx_peer_connection_t主动连接是
以ngx_connection-t结构体为基础实现的。本节将说明这两种连接中各字段的意义,同时需要注意的是,这两种连接都不可以随意创建,必须从
连接池中获取,
*/
/*
在使用连接池时,Nginx也封装了两个方法,见表。
如果我们开发的模块直接使用了连接池,那么就可以用这两个方法来获取、释放ngx_connection_t结构体。
表9-1 连接池的使用方法
┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓
┃ 连接池操作方法名 ┃ 参数含义 ┃ 执行意义 ┃
┣━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━┫
┃npc_connection_t *ngx_get_connection ┃ s是这条连接的套接字句柄, ┃ 从连接池中获取一个ngx_connection_t ┃
┃(ngx_socket_t s, ngx_log_t *log) ┃log则是记录日志的对象 ┃结构体,同时获取相应的读/写事件 ┃
┣━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━┫
┃void ngx_free_connection ┃ c是需要回收的连接 ┃ 将这个连接回收到连接池中 ┃
┃(ngx_connection_t) ┃ ┃ ┃
┗━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━┛
*/
/*一个ngx_connection_s对应一个ngx_event_s read和一个ngx_event_s write,其中事件的fd是从ngx_connection_s->fd获取,他们
在ngx_worker_process_init->ngx_event_process_init中关联起来 */
//ngx_event_t事件和ngx_connection_t连接是处理TCP连接的基础数据结构, 通过ngx_get_connection从连接池中获取一个ngx_connection_s结构,
//被动连接(客户端连接nginx)对应的数据结构是ngx_connection_s,主动连接(nginx连接后端服务器)对应的数据结构是ngx_peer_connection_s
struct ngx_connection_s { //cycle->read_events和cycle->write_events这两个数组存放的是ngx_event_s,他们是对应的,见ngx_event_process_init
/*
连接未使用时,data成员用于充当连接池中空闲连接链表中的next指针(ngx_event_process_init)。当连接被使用时,data的意义由使用它的Nginx模块而定,
如在HTTP框架中,data指向ngx_http_request_t请求
在服务器端accept客户端连接成功(ngx_event_accept)后,会通过ngx_get_connection从连接池获取一个ngx_connection_t结构,也就是每个客户端连接对于一个ngx_connection_t结构,
并且为其分配一个ngx_http_connection_t结构,ngx_connection_t->data = ngx_http_connection_t,见ngx_http_init_connection
*/
/*
在子请求处理过程中,上层父请求r的data指向第一个r下层的子请求,例如第二层的r->connection->data指向其第三层的第一个
创建的子请求r,c->data = sr见ngx_http_subrequest,在subrequest往客户端发送数据的时候,只有data指向的节点可以先发送出去
listen过程中,指向原始请求ngx_http_connection_t(ngx_http_init_connection ngx_http_ssl_handshake),接收到客户端数据后指向ngx_http_request_t(ngx_http_wait_request_handler)
http2协议的过程中,在ngx_http_v2_connection_t(ngx_http_v2_init)
*/
void *data; /* 如果是subrequest,则data最终指向最下层子请求r,见ngx_http_subrequest */
//如果是文件异步i/o中的ngx_event_aio_t,则它来自ngx_event_aio_t->ngx_event_t(只有读),如果是网络事件中的event,则为ngx_connection_s中的event(包括读和写)
ngx_event_t *read;//连接对应的读事件 赋值在ngx_event_process_init,空间是从ngx_cycle_t->read_event池子中获取的
ngx_event_t *write; //连接对应的写事件 赋值在ngx_event_process_init 一般在ngx_handle_write_event中添加些事件,空间是从ngx_cycle_t->read_event池子中获取的
ngx_socket_t fd;//套接字句柄
/* 如果启用了ssl,则发送和接收数据在ngx_ssl_recv ngx_ssl_write ngx_ssl_recv_chain ngx_ssl_send_chain */
//服务端通过ngx_http_wait_request_handler读取数据
ngx_recv_pt recv; //直接接收网络字符流的方法 见ngx_event_accept或者ngx_http_upstream_connect 赋值为ngx_os_io 在接收到客户端连接或者向上游服务器发起连接后赋值
ngx_send_pt send; //直接发送网络字符流的方法 见ngx_event_accept或者ngx_http_upstream_connect 赋值为ngx_os_io 在接收到客户端连接或者向上游服务器发起连接后赋值
/* 如果启用了ssl,则发送和接收数据在ngx_ssl_recv ngx_ssl_write ngx_ssl_recv_chain ngx_ssl_send_chain */
//以ngx_chain_t链表为参数来接收网络字符流的方法 ngx_recv_chain
ngx_recv_chain_pt recv_chain; //赋值见ngx_event_accept ngx_event_pipe_read_upstream中执行
//以ngx_chain_t链表为参数来发送网络字符流的方法 ngx_send_chain
//当http2头部帧发送的时候,会在ngx_http_v2_header_filter把ngx_http_v2_send_chain.send_chain=ngx_http_v2_send_chain
ngx_send_chain_pt send_chain; //赋值见ngx_event_accept ngx_http_write_filter和ngx_chain_writer中执行
//这个连接对应的ngx_listening_t监听对象,通过listen配置项配置,此连接由listening监听端口的事件建立,赋值在ngx_event_process_init
//接收到客户端连接后会冲连接池分配一个ngx_connection_s结构,其listening成员指向服务器接受该连接的listen信息结构,见ngx_event_accept
ngx_listening_t *listening; //实际上是从cycle->listening.elts中的一个ngx_listening_t
off_t sent;//这个连接上已经发送出去的字节数 //ngx_linux_sendfile_chain和ngx_writev_chain没发送多少字节就加多少字节
ngx_log_t *log;//可以记录日志的ngx_log_t对象 其实就是ngx_listening_t中获取的log //赋值见ngx_event_accept
/*
内存池。一般在accept -个新连接时,会创建一个内存池,而在这个连接结束时会销毁内存池。注意,这里所说的连接是指成功建立的
TCP连接,所有的ngx_connection_t结构体都是预分配的。这个内存池的大小将由listening监听对象中的pool_size成员决定
*/
ngx_pool_t *pool; //在accept返回成功后创建poll,见ngx_event_accept, 连接上游服务区的时候在ngx_http_upstream_connect创建
struct sockaddr *sockaddr; //连接客户端的sockaddr结构体 客户端的,本端的为下面的local_sockaddr 赋值见ngx_event_accept
socklen_t socklen; //sockaddr结构体的长度 //赋值见ngx_event_accept
ngx_str_t addr_text; //连接客户端字符串形式的IP地址
ngx_str_t proxy_protocol_addr;
#if (NGX_SSL)
ngx_ssl_connection_t *ssl; //赋值见ngx_ssl_create_connection
#endif
//本机的监听端口对应的sockaddr结构体,也就是listening监听对象中的sockaddr成员
struct sockaddr *local_sockaddr; //赋值见ngx_event_accept
socklen_t local_socklen;
/*
用于接收、缓存客户端发来的字符流,每个事件消费模块可自由决定从连接池中分配多大的空间给buffer这个接收缓存字段。
例如,在HTTP模块中,它的大小决定于client_header_buffer_size配置项
*/
ngx_buf_t *buffer; //ngx_http_request_t->header_in指向该缓冲去
/*
该字段用来将当前连接以双向链表元素的形式添加到ngx_cycle_t核心结构体的reusable_connections_queue双向链表中,表示可以重用的连接
*/
ngx_queue_t queue;
/*
连接使用次数。ngx_connection t结构体每次建立一条来自客户端的连接,或者用于主动向后端服务器发起连接时(ngx_peer_connection_t也使用它),
number都会加l
*/
ngx_atomic_uint_t number; //这个应该是记录当前连接是整个连接中的第几个连接,见ngx_event_accept ngx_event_connect_peer
ngx_uint_t requests; //处理的请求次数
/*
缓存中的业务类型。任何事件消费模块都可以自定义需要的标志位。这个buffered字段有8位,最多可以同时表示8个不同的业务。第三方模
块在自定义buffered标志位时注意不要与可能使用的模块定义的标志位冲突。目前openssl模块定义了一个标志位:
#define NGX_SSL_BUFFERED Ox01
HTTP官方模块定义了以下标志位:
#define NGX HTTP_LOWLEVEL_BUFFERED 0xf0
#define NGX_HTTP_WRITE_BUFFERED 0x10
#define NGX_HTTP_GZIP_BUFFERED 0x20
#define NGX_HTTP_SSI_BUFFERED 0x01
#define NGX_HTTP_SUB_BUFFERED 0x02
#define NGX_HTTP_COPY_BUFFERED 0x04
#define NGX_HTTP_IMAGE_BUFFERED Ox08
同时,对于HTTP模块而言,buffered的低4位要慎用,在实际发送响应的ngx_http_write_filter_module过滤模块中,低4位标志位为1则惫味着
Nginx会一直认为有HTTP模块还需要处理这个请求,必须等待HTTP模块将低4位全置为0才会正常结束请求。检查低4位的宏如下:
#define NGX_LOWLEVEL_BUFFERED OxOf
*/
unsigned buffered:8; //不为0,表示有数据没有发送完毕,ngx_http_request_t->out中还有未发送的报文
/*
本连接记录日志时的级别,它占用了3位,取值范围是0-7,但实际上目前只定义了5个值,由ngx_connection_log_error_e枚举表示,如下:
typedef enum{
NGXi ERROR—AIERT=0,
NGX' ERROR ERR,
NGX ERROR_INFO,
NGX, ERROR IGNORE ECONNRESET,
NGX ERROR—IGNORE EIb:fVAL
}
ngx_connection_log_error_e ;
*/
unsigned log_error:3; /* ngx_connection_log_error_e */
//标志位,为1时表示不期待字符流结束,目前无意义
unsigned unexpected_eof:1;
//每次处理完一个客户端请求后,都会ngx_add_timer(rev, c->listening->post_accept_timeout);
/*读客户端连接的数据,在ngx_http_init_connection(ngx_connection_t *c)中的ngx_add_timer(rev, c->listening->post_accept_timeout)把读事件添加到定时器中,如果超时则置1
每次ngx_unix_recv把内核数据读取完毕后,在重新启动add epoll,等待新的数据到来,同时会启动定时器ngx_add_timer(rev, c->listening->post_accept_timeout);
如果在post_accept_timeout这么长事件内没有数据到来则超时,开始处理关闭TCP流程*/
//当ngx_event_t->timedout置1的时候,该置也同时会置1,参考ngx_http_process_request_line ngx_http_process_request_headers
//在ngx_http_free_request中如果超时则会设置SO_LINGER来减少time_wait状态
unsigned timedout:1; //标志位,为1时表示连接已经超时,也就是过了post_accept_timeout多少秒还没有收到客户端的请求
unsigned error:1; //标志位,为1时表示连接处理过程中出现错误
/*
标志位,为1时表示连接已经销毁。这里的连接指是的TCP连接,而不是ngx_connection t结构体。当destroyed为1时,ngx_connection_t结
构体仍然存在,但其对应的套接字、内存池等已经不可用
*/
unsigned destroyed:1; //ngx_http_close_connection中置1
unsigned idle:1; //为1时表示连接处于空闲状态,如keepalive请求中丽次请求之间的状态
unsigned reusable:1; //为1时表示连接可重用,它与上面的queue字段是对应使用的
unsigned close:1; //为1时表示连接关闭
/*
和后端的ngx_connection_t在ngx_event_connect_peer这里置为1,但在ngx_http_upstream_connect中c->sendfile &= r->connection->sendfile;,
和客户端浏览器的ngx_connextion_t的sendfile需要在ngx_http_update_location_config中判断,因此最终是由是否在configure的时候是否有加
sendfile选项来决定是置1还是置0
*/
//赋值见ngx_http_update_location_config
unsigned sendfile:1; //标志位,为1时表示正在将文件中的数据发往连接的另一端
/*
标志位,如果为1,则表示只有在连接套接字对应的发送缓冲区必须满足最低设置的大小阅值时,事件驱动模块才会分发该事件。这与上文
介绍过的ngx_handle_write_event方法中的lowat参数是对应的
*/
unsigned sndlowat:1; //ngx_send_lowat
/*
标志位,表示如何使用TCP的nodelay特性。它的取值范围是下面这个枚举类型ngx_connection_tcp_nodelay_e。
typedef enum{
NGX_TCP_NODELAY_UNSET=O,
NGX_TCP_NODELAY_SET,
NGX_TCP_NODELAY_DISABLED
) ngx_connection_tcp_nodelay_e;
*/
unsigned tcp_nodelay:2; /* ngx_connection_tcp_nodelay_e */ //域套接字默认是disable的,
/*
标志位,表示如何使用TCP的nopush特性。它的取值范围是下面这个枚举类型ngx_connection_tcp_nopush_e:
typedef enum{
NGX_TCP_NOPUSH_UNSET=0,
NGX_TCP_NOPUSH_SET,
NGX_TCP_NOPUSH_DISABLED
) ngx_connection_tcp_nopush_e
*/ //域套接字默认是disable的,
unsigned tcp_nopush:2; /* ngx_connection_tcp_nopush_e */
unsigned need_last_buf:1;
#if (NGX_HAVE_IOCP)
unsigned accept_context_updated:1;
#endif
/*
#if (NGX HAVE AIO- SENDFILE)
//标志位,为1时表示使用异步I/O的方式将磁盘上文件发送给网络连接的另一端
unsigned aio一sendfile:l;
//使用异步I/O方式发送的文件,busy_sendfile缓冲区保存待发送文件的信息
ngx_buf_t *busy_sendf ile;
#endif
*/
#if (NGX_HAVE_AIO_SENDFILE)
unsigned busy_count:2;
#endif
#if (NGX_THREADS)
ngx_thread_task_t *sendfile_task;
#endif
};