3.7.2 将内存中的字符串作为包体发送

调用ngx_http_output_filter方法即可向客户端发送HTTP响应包体,下面查看一下此方法的原型,如下所示。


ngx_int_t ngx_http_output_filter(ngx_http_request_tr,ngx_chain_tin);


ngx_http_output_filter的返回值在mytest例子中不需要处理,通过在ngx_http_mytest_handler方法中返回的方式传递给ngx_http_finalize_request即可。ngx_chain_t结构已经在3.2.6节中介绍过,它仅用于容纳ngx_buf_t缓冲区,所以需要先了解一下如何使用ngx_buf_t分配内存。下面介绍Nginx的内存池是如何分配内存的。

为了减少内存碎片的数量,并通过统一管理来减少代码中出现内存泄漏的可能性,Nginx设计了ngx_pool_t内存池数据结构。本章我们不会深入分析内存池的实现,只关注内存池的用法。在ngx_http_mytest_handler处理方法传来的ngx_http_request_t对象中就有这个请求的内存池管理对象,我们对内存池的操作都可以基于它来进行,这样,在这个请求结束的时候,内存池分配的内存也都会被释放。


struct ngx_http_request_s{

……

ngx_pool_t*pool;

……

};


实际上,在r中可以获得许多内存池对象,这些内存池的大小、意义及生存期各不相同。第3部分会涉及许多内存池,本章使用r->pool内存池即可。有了ngx_pool_t对象后,可以从内存池中分配内存。例如,下面这个基本的申请分配内存的方法:


voidngx_palloc(ngx_pool_tpool,size_t size);


其中,ngx_palloc函数将会从pool内存池中分配到size字节的内存,并返回这段内存的起始地址。如果返回NULL空指针,则表示分配失败。还有一个封装了ngx_palloc的函数ngx_pcalloc,它多做了一件事,就是把ngx_palloc申请到的内存块全部置为0,虽然,多数情况下更适合用ngx_pcalloc来分配内存。

假如要分配一个ngx_buf_t结构,可以这样做:


ngx_buf_t*b=ngx_pcalloc(r->pool,sizeof(ngx_buf_t));


这样,ngx_buf_t中的成员指向的内存仍然可以继续分配,例如:


b->start=(u_char*)ngx_pcalloc(r->pool,128);

b->pos=b->start;

b->last=b->start;

b->end=b->last+128;

b->temporary=1;


实际上,Nginx还封装了一个生成ngx_buf_t的简便方法,它完全等价于上面的6行语句,如下所示。


ngx_buf_t*b=ngx_create_temp_buf(r->pool,128);


分配完内存后,可以向这段内存写入数据。当写完数据后,要让b->last指针指向数据的末尾,如果b->last与b->pos相等,那么HTTP框架是不会发送一个字节的包体的。

最后,把上面的ngx_buf_t*b用ngx_chain_t传给ngx_http_output_filter方法就可以发送HTTP响应的包体内容了。例如:


ngx_chain_t out;

out.buf=b;

out.next=NULL;

return ngx_http_output_filter(r,&out);


注意 在向用户发送响应包体时,必须牢记Nginx是全异步的服务器,也就是说,不可以在进程的栈里分配内存并将其作为包体发送。当ngx_http_output_filter方法返回时,可能由于TCP连接上的缓冲区还不可写,所以导致ngx_buf_t缓冲区指向的内存还没有发送,可这时方法返回已把控制权交给Nginx了,又会导致栈里的内存被释放,最后就会造成内存越界错误。因此,在发送响应包体时,尽量将ngx_buf_t中的pos指针指向从内存池里分配的内存。