nginx的作者为追求极致的高效,自己实现了很多颇具特色的nginx风格的数据结构以及公共函数。比如,nginx提供了带长度的字符串,根据编译器选项优化过的字符串拷贝函数ngx_copy等。所以,在我们写nginx模块时,应该尽量调用nginx提供的api,尽管有些api只是对glibc的宏定义。本节,我们介绍string、list、buffer、chain等一系列最基本的数据结构及相关api的使用技巧以及注意事项。

ngx_str_t(100%)

在nginx源码目录的src/core下面的ngx_string.h|c里面,包含了字符串的封装以及字符串相关操作的api。nginx提供了一个带长度的字符串结构ngx_str_t,它的原型如下:

typedef struct {size_t      len;u_char     *data;
} ngx_str_t;

从结构体当中,data指向字符串数据的第一个字符,字符串的结束用长度来表示,而不是由’\0’来表示结束。所以,在写nginx代码时,处理字符串的方法跟我们平时使用有很大的不一样,但要时刻记住,字符串不以’\0’结束,尽量使用nginx提供的字符串操作的api来操作字符串。 那么,nginx这样做有什么好处呢?首先,通过长度来表示字符串长度,减少计算字符串长度的次数。其次,nginx可以重复引用一段字符串内存,data可以指向任意内存,长度表示结束,而不用去copy一份自己的字符串(因为如果要以’\0’结束,而不能更改原字符串,所以势必要copy一段字符串)。我们在ngx_http_request_t结构体的成员中,可以找到很多字符串引用一段内存的例子,比如request_line、uri、args等等,这些字符串的data部分,都是指向在接收数据时创建buffer所指向的内存中,uri,args就没有必要copy一份出来。这样的话,减少了很多不必要的内存分配与拷贝。 正是基于此特性,在nginx中,必须谨慎的去修改一个字符串。在修改字符串时需要认真的去考虑:是否可以修改该字符串;字符串修改后,是否会对其它的引用造成影响。在后面介绍ngx_unescape_uri函数的时候,就会看到这一点。但是,使用nginx的字符串会产生一些问题,glibc提供的很多系统api函数大多是通过’\0’来表示字符串的结束,所以我们在调用系统api时,就不能直接传入str->data了。此时,通常的做法是创建一段str->len + 1大小的内存,然后copy字符串,最后一个字节置为’\0’。比较hack的做法是,将字符串最后一个字符的后一个字符backup一个,然后设置为’\0’,在做完调用后,再由backup改回来,但前提条件是,你得确定这个字符是可以修改的,而且是有内存分配,不会越界,但一般不建议这么做。 接下来,看看nginx提供的操作字符串相关的api。

#define ngx_string(str)     { sizeof(str) - 1, (u_char *) str }

ngx_string(str)是一个宏,它通过一个以’\0’结尾的普通字符串str构造一个nginx的字符串,鉴于其中采用sizeof操作符计算字符串长度,因此参数必须是一个常量字符串。

#define ngx_null_string     { 0, NULL }

定义变量时,使用ngx_null_string初始化字符串为空字符串,符串的长度为0,data为NULL。

#define ngx_str_set(str, text)                                               \(str)->len = sizeof(text) - 1; (str)->data = (u_char *) text

ngx_str_set用于设置字符串str为text,由于使用sizeof计算长度,故text必须为常量字符串。

#define ngx_str_null(str)   (str)->len = 0; (str)->data = NULL

ngx_str_null用于设置字符串str为空串,长度为0,data为NULL。

上面这四个函数,使用时一定要小心,ngx_string与ngx_null_string是“{,}”格式的,故只能用于赋值时初始化,如:

ngx_str_t str = ngx_string("hello world");
ngx_str_t str1 = ngx_null_string();

如果向下面这样使用,就会有问题,这里涉及到c语言中对结构体变量赋值操作的语法规则,在此不做介绍。

ngx_str_t str, str1;
str = ngx_string("hello world");    // 编译出错
str1 = ngx_null_string;                // 编译出错

这种情况,可以调用ngx_str_set与ngx_str_null这两个函数来做:

ngx_str_t str, str1;
ngx_str_set(&str, "hello world");
ngx_str_null(&str1);

按照C99标准,您也可以这么做:

ngx_str_t str, str1;
str  = (ngx_str_t) ngx_string("hello world");
str1 = (ngx_str_t) ngx_null_string;

另外要注意的是,ngx_string与ngx_str_set在调用时,传进去的字符串一定是常量字符串,否则会得到意想不到的错误(因为ngx_str_set内部使用了sizeof(),如果传入的是u_char*,那么计算的是这个指针的长度,而不是字符串的长度)。如:

ngx_str_t str;
u_char *a = "hello world";
ngx_str_set(&str, a);    // 问题产生

此外,值得注意的是,由于ngx_str_set与ngx_str_null实际上是两行语句,故在if/for/while等语句中单独使用需要用花括号括起来,例如:

ngx_str_t str;
if (cond)ngx_str_set(&str, "true");     // 问题产生
elsengx_str_set(&str, "false");    // 问题产生
void ngx_strlow(u_char *dst, u_char *src, size_t n);

将src的前n个字符转换成小写存放在dst字符串当中,调用者需要保证dst指向的空间大于等于n,且指向的空间必须可写。操作不会对原字符串产生变动。如要更改原字符串,可以:

ngx_strlow(str->data, str->data, str->len);
ngx_strncmp(s1, s2, n)

区分大小写的字符串比较,只比较前n个字符。

ngx_strcmp(s1, s2)

区分大小写的不带长度的字符串比较。

ngx_int_t ngx_strcasecmp(u_char *s1, u_char *s2);

不区分大小写的不带长度的字符串比较。

ngx_int_t ngx_strncasecmp(u_char *s1, u_char *s2, size_t n);

不区分大小写的带长度的字符串比较,只比较前n个字符。

u_char * ngx_cdecl ngx_sprintf(u_char *buf, const char *fmt, ...);
u_char * ngx_cdecl ngx_snprintf(u_char *buf, size_t max, const char *fmt, ...);
u_char * ngx_cdecl ngx_slprintf(u_char *buf, u_char *last, const char *fmt, ...);

上面这三个函数用于字符串格式化,ngx_snprintf的第二个参数max指明buf的空间大小,ngx_slprintf则通过last来指明buf空间的大小。推荐使用第二个或第三个函数来格式化字符串,ngx_sprintf函数还是比较危险的,容易产生缓冲区溢出漏洞。在这一系列函数中,nginx在兼容glibc中格式化字符串的形式之外,还添加了一些方便格式化nginx类型的一些转义字符,比如%V用于格式化ngx_str_t结构。在nginx源文件的ngx_string.c中有说明:

/** supported formats:*    %[0][width][x][X]O        off_t*    %[0][width]T              time_t*    %[0][width][u][x|X]z      ssize_t/size_t*    %[0][width][u][x|X]d      int/u_int*    %[0][width][u][x|X]l      long*    %[0][width|m][u][x|X]i    ngx_int_t/ngx_uint_t*    %[0][width][u][x|X]D      int32_t/uint32_t*    %[0][width][u][x|X]L      int64_t/uint64_t*    %[0][width|m][u][x|X]A    ngx_atomic_int_t/ngx_atomic_uint_t*    %[0][width][.width]f      double, max valid number fits to %18.15f*    %P                        ngx_pid_t*    %M                        ngx_msec_t*    %r                        rlim_t*    %p                        void **    %V                        ngx_str_t **    %v                        ngx_variable_value_t **    %s                        null-terminated string*    %*s                       length and string*    %Z                        '\0'*    %N                        '\n'*    %c                        char*    %%                        %**  reserved:*    %t                        ptrdiff_t*    %S                        null-terminated wchar string*    %C                        wchar*/

这里特别要提醒的是,我们最常用于格式化ngx_str_t结构,其对应的转义符是%V,传给函数的一定要是指针类型,否则程序就会coredump掉。这也是我们最容易犯的错。比如:

ngx_str_t str = ngx_string("hello world");
char buffer[1024];
ngx_snprintf(buffer, 1024, "%V", &str);    // 注意,str取地址
void ngx_encode_base64(ngx_str_t *dst, ngx_str_t *src);
ngx_int_t ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src);

这两个函数用于对str进行base64编码与解码,调用前,需要保证dst中有足够的空间来存放结果,如果不知道具体大小,可先调用ngx_base64_encoded_length与ngx_base64_decoded_length来预估最大占用空间。

uintptr_t ngx_escape_uri(u_char *dst, u_char *src, size_t size,ngx_uint_t type);

对src进行编码,根据type来按不同的方式进行编码,如果dst为NULL,则返回需要转义的字符的数量,由此可得到需要的空间大小。type的类型可以是:

#define NGX_ESCAPE_URI         0
#define NGX_ESCAPE_ARGS        1
#define NGX_ESCAPE_HTML        2
#define NGX_ESCAPE_REFRESH     3
#define NGX_ESCAPE_MEMCACHED   4
#define NGX_ESCAPE_MAIL_AUTH   5
void ngx_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type);

对src进行反编码,type可以是0、NGX_UNESCAPE_URI、NGX_UNESCAPE_REDIRECT这三个值。如果是0,则表示src中的所有字符都要进行转码。如果是NGX_UNESCAPE_URI与NGX_UNESCAPE_REDIRECT,则遇到’?’后就结束了,后面的字符就不管了。而NGX_UNESCAPE_URI与NGX_UNESCAPE_REDIRECT之间的区别是NGX_UNESCAPE_URI对于遇到的需要转码的字符,都会转码,而NGX_UNESCAPE_REDIRECT则只会对非可见字符进行转码。

uintptr_t ngx_escape_html(u_char *dst, u_char *src, size_t size);

对html标签进行编码。

当然,我这里只介绍了一些常用的api的使用,大家可以先熟悉一下,在实际使用过程中,遇到不明白的,最快最直接的方法就是去看源码,看api的实现或看nginx自身调用api的地方是怎么做的,代码就是最好的文档。

Nginx基本数据结构之ngx_str_t相关推荐

  1. Nginx学习笔记(三) Nginx基本数据结构

    话说学习一种编程语言,例如C语言,我们首先学的也是数据结构,这是以后开发程序的关键.为了更好更方便的开发Nginx,Nginx自己实现了很多适合nginx的数据结构. Nginx中的数组 ngx_ar ...

  2. Nginx基本数据结构之ngx_hash_keys_arrays_t

    大家看到在构建一个ngx_hash_wildcard_t的时候,需要对通配符的哪些key进行预处理.这个处理起来比较麻烦.而当有一组key,这些里面既有无通配符的key,也有包含通配符的key的时候. ...

  3. Nginx基本数据结构之ngx_hash_t

    ngx_hash_t是nginx自己的hash表的实现.定义和实现位于src/core/ngx_hash.h|c中.ngx_hash_t的实现也与数据结构教课书上所描述的hash表的实现是大同小异.对 ...

  4. nginx基本数据结构ngx_module_t,ngx_module_s

    ngx_module_t是nginx的模块化架构最基本的数据结构.  其结构定义如下,其中的注释为功能说明: struct ngx_module_s { ngx_uint_t            c ...

  5. nginx基本数据结构及接口

    简单的数据类型 在core/ngx_confing.h中定义了基本的数据类型的映射,大部分都映射到c语言自身的数据类型: typedef intptr_t        ngx_int_t; type ...

  6. Nginx基本数据结构之ngx_buf_t

    这个ngx_buf_t就是这个ngx_chain_t链表的每个节点的实际数据.该结构实际上是一种抽象的数据结构,它代表某种具体的数据.这个数据可能是指向内存中的某个缓冲区,也可能指向一个文件的某一部分 ...

  7. Nginx基本数据结构之ngx_pool_t

    ngx_pool_t是一个非常重要的数据结构,在很多重要的场合都有使用,很多重要的数据结构也都在使用它.那么它究竟是一个什么东西呢?简单的说,它提供了一种机制,帮助管理一系列的资源(如内存,文件等), ...

  8. Nginx基本数据结构之ngx_queue_t

    ngx_queue_t是nginx中的双向链表,在nginx源码目录src/core下面的ngx_queue.h|c里面.它的原型如下: typedef struct ngx_queue_s ngx_ ...

  9. Nginx基本数据结构之ngx_list_t

    ngx_list_t顾名思义,看起来好像是一个list的数据结构.这样的说法,算对也不算对.因为它符合list类型数据结构的一些特点,比如可以添加元素,实现自增长,不会像数组类型的数据结构,受到初始设 ...

最新文章

  1. 某网友惊现如此言论:程序员没有技术壁垒,不值得这么高工资!过高工资引起加班和行业内卷,应该用降薪来换取不加班!网友:你好天真!...
  2. 在eclipse里jsp编译后的java和class文件的位置
  3. boost::multiprecision模块complex128相关的测试程序
  4. 陈睿学长在CUIT建校70周年校庆上的演讲
  5. 多元线性回归分析matlab实验报告,利用MATLAB进行多元线性回归.ppt
  6. 阿里 P8 Java高级架构师,都需要掌握哪些技术栈?
  7. 头回见!95后女大学生买iPhone11出租:租借者想尝鲜或显摆
  8. 区块链架构与应用(区块链入门篇)
  9. python iloc iat_python数据预处理_DataFrame数据筛选loc,iloc,ix,at,iat
  10. 调制解调器和路由器的区别:
  11. 初中python编程初步教学设计_初中Python程序设计顺序结构教学设计方案.docx
  12. 目前企业的云计算转型,主要可以划分为哪四个阶段?
  13. mysql不锁表加索引
  14. Nesterov加速算法
  15. angular2+ 常用链接
  16. 虚幻4和Unity3D应该学哪个? 1
  17. crh寄存器_STM32直接操作寄存器
  18. 〔转载〕从蓝色巨人到四海一家 IBM品牌塑造及转型
  19. @程序员,不加班就滚吧 | 程序员有话说
  20. 一个基于C#实现的简易QQ农场

热门文章

  1. Python求解多元非线性方程组
  2. 系统升级不停服务器,服务器操作系统一直升级吗
  3. eureka 集群失败的原因_对于注册中心,ZooKeeper、Eureka哪个更合适?
  4. dijikstra 旅行商问题_第27期:基于旅行商问题(TSP)的配送网络优化—R实现
  5. ng6 常见错误汇总(持续更新)
  6. C# ToString()和Convert.ToString()的区别【转】
  7. Splash resource_timeout 属性
  8. mysql排序自段为字符串类型问题解决
  9. mysql的介绍和安装
  10. ViewPager 无限循环遇到的坑 viewpager.setOffscreenPageLimit(2);