From ee8dfb1a1950ac0905410fa716b1e896c3a75471 Mon Sep 17 00:00:00 2001 From: "hewei.it" Date: Sat, 22 May 2021 20:01:44 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E6=96=B0=E5=BB=BA=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=88=86=E6=94=AF=EF=BC=8C=E7=94=A8=E5=81=9A=E6=BA=90=E7=A0=81?= =?UTF-8?q?=E4=B8=AD=E6=96=87=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- base/array.h | 5 +++++ base/hatomic.h | 6 ++++++ base/hbase.c | 32 +++++++++++++++++++++++++++---- base/hbase.h | 32 +++++++++++++++++++++++++++++++ base/hbuf.h | 50 ++++++++++++++++++++++++++++++++++++++++++------ base/hdef.h | 39 +++++++++++++++++++++++++++++++++++++ base/hplatform.h | 17 ++++++++++++++++ hexport.h | 35 +++++++++++++++++++++++++++++++++ hv.h | 8 ++++++++ 9 files changed, 214 insertions(+), 10 deletions(-) diff --git a/base/array.h b/base/array.h index c046eca3a..defc5c7fd 100644 --- a/base/array.h +++ b/base/array.h @@ -1,6 +1,11 @@ #ifndef HV_ARRAY_H_ #define HV_ARRAY_H_ +/* + * @功能:不定长数组的宏实现 + * + */ + /* * array * at: random access by pos diff --git a/base/hatomic.h b/base/hatomic.h index 4f986fcfb..2beede2e9 100644 --- a/base/hatomic.h +++ b/base/hatomic.h @@ -1,6 +1,11 @@ #ifndef HV_ATOMIC_H_ #define HV_ATOMIC_H_ +/* + * @功能:原子操作的跨平台封装 + * + */ + #ifdef __cplusplus // c++11 @@ -93,6 +98,7 @@ static inline void atomic_flag_clear(atomic_flag* p) { } #endif +// 垫底逻辑 #ifndef ATOMIC_ADD #define ATOMIC_ADD(p, n) (*(p) += (n)) #endif diff --git a/base/hbase.c b/base/hbase.c index 63a65fa88..c77d7f67f 100644 --- a/base/hbase.c +++ b/base/hbase.c @@ -18,6 +18,7 @@ long hv_free_cnt() { } void* safe_malloc(size_t size) { + // 原子增加内存分配计数 hatomic_inc(&s_alloc_cnt); void* ptr = malloc(size); if (!ptr) { @@ -28,13 +29,16 @@ void* safe_malloc(size_t size) { } void* safe_realloc(void* oldptr, size_t newsize, size_t oldsize) { + // 原子增加内存分配计数 hatomic_inc(&s_alloc_cnt); + // 原子增加内存释放计数 hatomic_inc(&s_free_cnt); void* ptr = realloc(oldptr, newsize); if (!ptr) { fprintf(stderr, "realloc failed!\n"); exit(-1); } + // 将新增的内存置0(系统库里的realloc未置空) if (newsize > oldsize) { memset((char*)ptr + oldsize, 0, newsize - oldsize); } @@ -74,6 +78,7 @@ char* strupper(char* str) { char* p = str; while (*p != '\0') { if (*p >= 'a' && *p <= 'z') { + // 大小写只有bits[5]不同,利用这一特性,使用位操作符比加减操作更高效 *p &= ~0x20; } ++p; @@ -85,6 +90,7 @@ char* strlower(char* str) { char* p = str; while (*p != '\0') { if (*p >= 'A' && *p <= 'Z') { + // 大小写只有bits[5]不同,利用这一特性,使用位操作符比加减操作更高效 *p |= 0x20; } ++p; @@ -96,9 +102,10 @@ char* strreverse(char* str) { if (str == NULL) return NULL; char* b = str; char* e = str; - while(*e) {++e;} - --e; + while(*e) {++e;} // 此时e指向‘\0’结束符 + --e; // 此时e指向最后一个字符 char tmp; + // 头尾指针法 while (e > b) { tmp = *e; *e = *b; @@ -138,6 +145,7 @@ bool strstartswith(const char* str, const char* start) { ++str; ++start; } + // 如果start走到了'\0'结束符,说明str是以start开头 return *start == '\0'; } @@ -145,9 +153,11 @@ bool strendswith(const char* str, const char* end) { assert(str != NULL && end != NULL); int len1 = 0; int len2 = 0; - while (*str) {++str; ++len1;} - while (*end) {++end; ++len2;} + while (*str) {++str; ++len1;} // 统计str的长度,str此时走到了'\0'结束符 + while (*end) {++end; ++len2;} // 统计end的长度,end此时走到了'\0'结束符 + // 如果str的长度比end的长度还小,当然不可能是以end字符串结尾,直接返回false if (len1 < len2) return false; + // 从最后一个字符开始比较是否相等 while (len2-- > 0) { --str; --end; @@ -160,6 +170,7 @@ bool strendswith(const char* str, const char* end) { bool strcontains(const char* str, const char* sub) { assert(str != NULL && sub != NULL); + // 直接使用了标准库函数strstr return strstr(str, sub) != NULL; } @@ -168,8 +179,10 @@ char* strrchr_dir(const char* filepath) { while (*p) ++p; while (--p >= filepath) { #ifdef OS_WIN + // windows下通常以正斜杠‘\’表示路径分隔符 if (*p == '/' || *p == '\\') #else + // unix下以反斜杠‘/’表示路径分隔符 if (*p == '/') #endif return p; @@ -178,16 +191,19 @@ char* strrchr_dir(const char* filepath) { } const char* hv_basename(const char* filepath) { + // 找到最后一个路径分割符,返回pos+1 const char* pos = strrchr_dir(filepath); return pos ? pos+1 : filepath; } const char* hv_suffixname(const char* filename) { + // 找到最后一个点符号,返回pos+1 const char* pos = strrchr_dot(filename); return pos ? pos+1 : ""; } int hv_mkdir_p(const char* dir) { + // 如果路径可访问,说明路径已经存在,直接返回 if (access(dir, 0) == 0) { return EEXIST; } @@ -195,6 +211,7 @@ int hv_mkdir_p(const char* dir) { safe_strncpy(tmp, dir, sizeof(tmp)); char* p = tmp; char delim = '/'; + // 通过路径分隔符,一级一级的创建子目录 while (*p) { #ifdef OS_WIN if (*p == '/' || *p == '\\') { @@ -208,6 +225,7 @@ int hv_mkdir_p(const char* dir) { } ++p; } + // 创建最后一级目录 if (hv_mkdir(tmp) != 0) { return EPERM; } @@ -215,9 +233,11 @@ int hv_mkdir_p(const char* dir) { } int hv_rmdir_p(const char* dir) { + // 如果路径不可访问,说明路径不存在,直接返回 if (access(dir, 0) != 0) { return ENOENT; } + // 删除最后一级目录 if (rmdir(dir) != 0) { return EPERM; } @@ -225,6 +245,7 @@ int hv_rmdir_p(const char* dir) { safe_strncpy(tmp, dir, sizeof(tmp)); char* p = tmp; while (*p) ++p; + // 通过路径分隔符,一级一级的删除父目录 while (--p >= tmp) { #ifdef OS_WIN if (*p == '/' || *p == '\\') { @@ -244,6 +265,8 @@ bool getboolean(const char* str) { if (str == NULL) return false; int len = strlen(str); if (len == 0) return false; + // 通过长度判断后再和对应字符串做比较, + // 而不需要和每个代表true的字符串都对比一遍。 switch (len) { case 1: return *str == '1' || *str == 'y' || *str == 'Y'; case 2: return stricmp(str, "on") == 0; @@ -254,6 +277,7 @@ bool getboolean(const char* str) { } } +// 获取可执行文件路径在各个操作系统上的实现不同,这里提供统一的封装接口 char* get_executable_path(char* buf, int size) { #ifdef OS_WIN GetModuleFileName(NULL, buf, size); diff --git a/base/hbase.h b/base/hbase.h index 5ce783214..63d49a675 100644 --- a/base/hbase.h +++ b/base/hbase.h @@ -1,12 +1,18 @@ #ifndef HV_BASE_H_ #define HV_BASE_H_ +/* + * @功能:此头文件实现了一些常用的函数 + * + */ + #include "hexport.h" #include "hplatform.h" // for bool #include "hdef.h" // for printd BEGIN_EXTERN_C +// 安全的内存分配与释放函数 //--------------------safe alloc/free--------------------------- HV_EXPORT void* safe_malloc(size_t size); HV_EXPORT void* safe_realloc(void* oldptr, size_t newsize, size_t oldsize); @@ -14,14 +20,17 @@ HV_EXPORT void* safe_calloc(size_t nmemb, size_t size); HV_EXPORT void* safe_zalloc(size_t size); HV_EXPORT void safe_free(void* ptr); +// 分配内存宏 #define HV_ALLOC(ptr, size)\ do {\ *(void**)&(ptr) = safe_zalloc(size);\ printd("alloc(%p, size=%llu)\tat [%s:%d:%s]\n", ptr, (unsigned long long)size, __FILE__, __LINE__, __FUNCTION__);\ } while(0) +// 通过sizeof计算内存占用大小,并分配内存,通常用于给结构体分配内存 #define HV_ALLOC_SIZEOF(ptr) HV_ALLOC(ptr, sizeof(*(ptr))) +// 释放内存宏 #define HV_FREE(ptr)\ do {\ if (ptr) {\ @@ -31,6 +40,7 @@ HV_EXPORT void safe_free(void* ptr); }\ } while(0) +// 统计内存分配/释放的次数,以此检查是否有内存未释放 HV_EXPORT long hv_alloc_cnt(); HV_EXPORT long hv_free_cnt(); static inline void hv_memcheck() { @@ -38,15 +48,25 @@ static inline void hv_memcheck() { } #define HV_MEMCHECK atexit(hv_memcheck); +// 一些字符串操作函数 //--------------------safe string------------------------------- +// 字符串转大写 HV_EXPORT char* strupper(char* str); +// 字符串转小写 HV_EXPORT char* strlower(char* str); +// 字符串翻转 HV_EXPORT char* strreverse(char* str); +// 判断字符串是否以某个字符串开头 HV_EXPORT bool strstartswith(const char* str, const char* start); +// 判断字符串是否以某个字符串结尾 HV_EXPORT bool strendswith(const char* str, const char* end); +// 判断字符串中是否包含某个子串 HV_EXPORT bool strcontains(const char* str, const char* sub); +// 标准库里的strncpy、strncat通常需要传入sizeof(dest_buf)-1, +// 如果不小心传成sizeof(dest_buf),可能造成内存越界访问以及不是以'\0'结尾, +// 所以这里重新实现了下,传入sizeof(dest_buf)也能安全工作 // strncpy n = sizeof(dest_buf)-1 // safe_strncpy n = sizeof(dest_buf) HV_EXPORT char* safe_strncpy(char* dest, const char* src, size_t n); @@ -55,6 +75,7 @@ HV_EXPORT char* safe_strncpy(char* dest, const char* src, size_t n); // safe_strncpy n = sizeof(dest_buf) HV_EXPORT char* safe_strncat(char* dest, const char* src, size_t n); +// 某些系统库里没有定义strlcpy和strlcat,这里兼容处理下 #if !HAVE_STRLCPY #define strlcpy safe_strncpy #endif @@ -63,23 +84,34 @@ HV_EXPORT char* safe_strncat(char* dest, const char* src, size_t n); #define strlcat safe_strncat #endif +// 查找最后一个点符号,通常用于分离文件名和后缀 #define strrchr_dot(str) strrchr(str, '.') +// 查找最后一个路径符号,通常用于分离路径和文件名 HV_EXPORT char* strrchr_dir(const char* filepath); // basename +// 获取文件名,使用上面的strrchr_dir实现 HV_EXPORT const char* hv_basename(const char* filepath); +// 获取后缀名,使用上面的strrchr_dot实现 HV_EXPORT const char* hv_suffixname(const char* filename); // mkdir -p +// 递归创建文件夹 HV_EXPORT int hv_mkdir_p(const char* dir); // rmdir -p +// 递归删除文件夹 HV_EXPORT int hv_rmdir_p(const char* dir); // 1 y on yes true enable +// 根据字符串返回对应表示的布尔类型值 HV_EXPORT bool getboolean(const char* str); +// 获取可执行文件完整路径 HV_EXPORT char* get_executable_path(char* buf, int size); +// 获取可执行文件所在目录 HV_EXPORT char* get_executable_dir(char* buf, int size); +// 获取可执行文件名 HV_EXPORT char* get_executable_file(char* buf, int size); +// 获取运行目录 HV_EXPORT char* get_run_dir(char* buf, int size); END_EXTERN_C diff --git a/base/hbuf.h b/base/hbuf.h index 9d144b4fd..2da4a1ffe 100644 --- a/base/hbuf.h +++ b/base/hbuf.h @@ -1,6 +1,11 @@ #ifndef HV_BUF_H_ #define HV_BUF_H_ +/* + * @功能:此头文件提供了一些常用的buffer + * + */ + #include "hdef.h" // for MAX #include "hbase.h" // for HV_ALLOC, HV_FREE @@ -21,6 +26,8 @@ typedef struct hbuf_s { #endif } hbuf_t; +// offset_buf_t 多了一个offset偏移量成员变量, +// 通常用于一次未操作完,记录下一次操作的起点 typedef struct offset_buf_s { char* base; size_t len; @@ -44,6 +51,7 @@ class HBuf : public hbuf_t { HBuf() : hbuf_t() { cleanup_ = false; } + // 浅拷贝构造函数 HBuf(void* data, size_t len) : hbuf_t(data, len) { cleanup_ = false; } @@ -79,6 +87,7 @@ class HBuf : public hbuf_t { cleanup_ = true; } + // 深拷贝 void copy(void* data, size_t len) { resize(len); memcpy(base, data, len); @@ -89,9 +98,10 @@ class HBuf : public hbuf_t { } private: - bool cleanup_; + bool cleanup_; // cleanup_变量用来记录buf是浅拷贝还是深拷贝,如果是深拷贝,析构时需释放掉分配内存 }; +// 可变长buffer类型,支持push_front/push_back/pop_front/pop_back操作 // VL: Variable-Length class HVLBuf : public HBuf { public: @@ -100,66 +110,85 @@ class HVLBuf : public HBuf { HVLBuf(size_t cap) : HBuf(cap) {_offset = _size = 0;} virtual ~HVLBuf() {} + // 返回当前起点 char* data() { return base + _offset; } + // 返回有效长度 size_t size() { return _size; } void push_front(void* ptr, size_t len) { + // 如果插入长度超过了剩余空间,则重新分配足够空间 if (len > this->len - _size) { size_t newsize = MAX(this->len, len)*2; base = (char*)safe_realloc(base, newsize, this->len); this->len = newsize; } + // 如果前面空间不足,则需要先整体后移 if (_offset < len) { // move => end memmove(base+this->len-_size, data(), _size); _offset = this->len-_size; } + // 插入到当前起点的前面 memcpy(data()-len, ptr, len); + // 记录新的起点位置 _offset -= len; + // 有效长度增加 _size += len; } void push_back(void* ptr, size_t len) { + // 如果插入长度超过了剩余空间,则重新分配足够空间 if (len > this->len - _size) { size_t newsize = MAX(this->len, len)*2; base = (char*)safe_realloc(base, newsize, this->len); this->len = newsize; } + // 如果后面空间不足,则需要先整体前移 else if (len > this->len - _offset - _size) { // move => start memmove(base, data(), _size); _offset = 0; } + // 插入到后面 memcpy(data()+_size, ptr, len); + // 起点位置不变,有效长度增加 _size += len; } void pop_front(void* ptr, size_t len) { if (len <= _size) { + // 将数据从开始位置拷贝出来 if (ptr) { memcpy(ptr, data(), len); } + // 起点位置后移 _offset += len; + // 如果起点位置已经到了结尾,则重置为0 if (_offset >= this->len) _offset = 0; + // 有效长度减少 _size -= len; } } void pop_back(void* ptr, size_t len) { if (len <= _size) { + // 将数据从尾部拷贝出来 if (ptr) { memcpy(ptr, data()+_size-len, len); } + // 起点位置不变,有效长度减少 _size -= len; } } void clear() { + // 清除操作:将起点位置和有效长度重置为0 _offset = _size = 0; } + // 一些别名函数 void prepend(void* ptr, size_t len) { push_front(ptr, len); } @@ -177,10 +206,11 @@ class HVLBuf : public HBuf { } private: - size_t _offset; - size_t _size; + size_t _offset; // _offet用来记录当前起点的偏移量 + size_t _size; // _size用来记录有效长度 }; +// 环形buffer:有序从环形buffer中分配与释放内存,避免频繁调用系统调用 class HRingBuf : public HBuf { public: HRingBuf() : HBuf() {_head = _tail = _size = 0;} @@ -189,13 +219,16 @@ class HRingBuf : public HBuf { char* alloc(size_t len) { char* ret = NULL; + // 如果头指针在尾指针前面或者已用长度等于0 if (_head < _tail || _size == 0) { // [_tail, this->len) && [0, _head) + // 如果尾指针后面剩余空间足够,则从尾指针后开始分配空间 if (this->len - _tail >= len) { ret = base + _tail; _tail += len; if (_tail == this->len) _tail = 0; } + // 如果头指针前面剩余空间足够,则从0开始分配空间 else if (_head >= len) { ret = base; _tail = len; @@ -203,21 +236,26 @@ class HRingBuf : public HBuf { } else { // [_tail, _head) + // 如果尾指针到头指针间的空间足够,则从尾指针后开始分配空间 if (_head - _tail >= len) { ret = base + _tail; _tail += len; } } + // 分配到了空间,已用长度增加 _size += ret ? len : 0; return ret; } void free(size_t len) { + // 已用长度减少 _size -= len; + // 如果释放的长度小于头指针后面的长度,头指针后移, if (len <= this->len - _head) { _head += len; if (_head == this->len) _head = 0; } + // 否则说明头指针已抵尾部,这块释放的内存是从0开始分配的,头指针置为len即可 else { _head = len; } @@ -228,9 +266,9 @@ class HRingBuf : public HBuf { size_t size() {return _size;} private: - size_t _head; - size_t _tail; - size_t _size; + size_t _head; // 头指针,用来记录读位置 + size_t _tail; // 尾指针,用来记录写位置 + size_t _size; // 用来记录已用长度 }; #endif diff --git a/base/hdef.h b/base/hdef.h index 9675897e2..96cbdad54 100644 --- a/base/hdef.h +++ b/base/hdef.h @@ -1,32 +1,45 @@ #ifndef HV_DEF_H_ #define HV_DEF_H_ +/* + * @功能:此头文件定义了一些常用宏 + * + */ + #include "hplatform.h" +// 取绝对值 #ifndef ABS #define ABS(n) ((n) > 0 ? (n) : -(n)) #endif +// 取负绝对值 #ifndef NABS #define NABS(n) ((n) < 0 ? (n) : -(n)) #endif +// 数组大小 #ifndef ARRAY_SIZE #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) #endif +// 设置bit位 #ifndef BITSET #define BITSET(p, n) (*(p) |= (1u << (n))) #endif +// 清除bit位 #ifndef BITCLR #define BITCLR(p, n) (*(p) &= ~(1u << (n))) #endif +// 获取bit位 #ifndef BITGET #define BITGET(i, n) ((i) & (1u << (n))) #endif +// 换行符 +// 因为历史原因,mac使用\r,linux使用\n,windows使用\r\n #ifndef CR #define CR '\r' #endif @@ -39,13 +52,16 @@ #define CRLF "\r\n" #endif +// float == 0 的写法 #define FLOAT_PRECISION 1e-6 #define FLOAT_EQUAL_ZERO(f) (ABS(f) < FLOAT_PRECISION) +// 定义一个表示永远的值 #ifndef INFINITE #define INFINITE (uint32_t)-1 #endif +// ASCII码 /* ASCII: [0, 0x20) control-charaters @@ -62,46 +78,57 @@ [0x61, 0x7A] => a~z */ +// 判断是否为字母 #ifndef IS_ALPHA #define IS_ALPHA(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) #endif +// 判断是否为数字 #ifndef IS_NUM #define IS_NUM(c) ((c) >= '0' && (c) <= '9') #endif +// 判断是否为字母或数字 #ifndef IS_ALPHANUM #define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) #endif +// 判断是否为控制符 #ifndef IS_CNTRL #define IS_CNTRL(c) ((c) >= 0 && (c) < 0x20) #endif +// 判断是否为可打印符 #ifndef IS_GRAPH #define IS_GRAPH(c) ((c) >= 0x20 && (c) < 0x7F) #endif +// 判断是否为16进制 #ifndef IS_HEX #define IS_HEX(c) (IS_NUM(c) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F')) #endif +// 判断是否为小写字母 #ifndef IS_LOWER #define IS_LOWER(c) (((c) >= 'a' && (c) <= 'z')) #endif +// 判断是否为大写字母 #ifndef IS_UPPER #define IS_UPPER(c) (((c) >= 'A' && (c) <= 'Z')) #endif +// 转小写 #ifndef LOWER #define LOWER(c) ((c) | 0x20) #endif +// 转大写 #ifndef UPPER #define UPPER(c) ((c) & ~0x20) #endif +// 定义一些整型显示转换宏 // LD, LU, LLD, LLU for explicit conversion of integer #ifndef LD #define LD(v) ((long)(v)) @@ -228,33 +255,45 @@ #define SAFE_RELEASE(p) do {if (p) {(p)->release(); (p) = NULL;}} while(0) #endif +/* + * @NOTE: 宏定义中的三个特殊符号:#,##,#@ + * #define TOSTR(x) #x // 给x加上双引号 + * #define TOCHAR(x) #@x // 给x加上单引号 + * #define STRCAT(x,y) x##y // 连接x和y成一个字符串 + * + */ #define STRINGIFY(x) STRINGIFY_HELPER(x) #define STRINGIFY_HELPER(x) #x #define STRINGCAT(x, y) STRINGCAT_HELPER(x, y) #define STRINGCAT_HELPER(x, y) x##y +// 获取结构体成员偏移量 #ifndef offsetof #define offsetof(type, member) \ ((size_t)(&((type*)0)->member)) #endif +// 获取结构体成员尾部的偏移量 #ifndef offsetofend #define offsetofend(type, member) \ (offsetof(type, member) + sizeof(((type*)0)->member)) #endif +// 根据结构体成员指针获取结构体指针 #ifndef container_of #define container_of(ptr, type, member) \ ((type*)((char*)(ptr) - offsetof(type, member))) #endif +// DEBUG打印宏 #ifdef PRINT_DEBUG #define printd(...) printf(__VA_ARGS__) #else #define printd(...) #endif +// ERROR打印宏 #ifdef PRINT_ERROR #define printe(...) fprintf(stderr, __VA_ARGS__) #else diff --git a/base/hplatform.h b/base/hplatform.h index a6944b05b..1d6bf595f 100644 --- a/base/hplatform.h +++ b/base/hplatform.h @@ -1,9 +1,15 @@ #ifndef HV_PLATFORM_H_ #define HV_PLATFORM_H_ +/* + * @功能:此头文件主要定义了平台相关宏 + * + */ + #include "hconfig.h" // OS +// 操作系统宏 #if defined(WIN64) || defined(_WIN64) #define OS_WIN64 #define OS_WIN32 @@ -45,6 +51,7 @@ #endif // ARCH +// CPU体系结构宏 #if defined(__i386) || defined(__i386__) || defined(_M_IX86) #define ARCH_X86 #define ARCH_X86_32 @@ -61,6 +68,7 @@ #endif // COMPILER +// 编译器宏 #if defined (_MSC_VER) #define COMPILER_MSVC @@ -106,6 +114,7 @@ #undef HAVE_PTHREAD_H #define HAVE_PTHREAD_H 0 +// 禁掉一些MSVC编译时警告 #pragma warning (disable: 4018) // signed/unsigned comparison #pragma warning (disable: 4100) // unused param #pragma warning (disable: 4244) // conversion loss of data @@ -132,6 +141,7 @@ #endif // headers +// #include一些常用头文件 #ifdef OS_WIN #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN @@ -195,6 +205,7 @@ #endif // ENDIAN +// 大小端宏 #ifndef BIG_ENDIAN #define BIG_ENDIAN 4321 #endif @@ -206,6 +217,7 @@ #endif // BYTE_ORDER +// 字节序宏 #ifndef BYTE_ORDER #if defined(ARCH_X86) || defined(ARCH_X86_64) || defined(__ARMEL__) #define BYTE_ORDER LITTLE_ENDIAN @@ -215,6 +227,7 @@ #endif // ANSI C +// #include ANSI C 标准头文件 #include #include #include @@ -229,6 +242,8 @@ #include #include +// bool类型兼容处理 +// c99前C语言是没有bool类型的 #ifndef __cplusplus #if HAVE_STDBOOL_H #include @@ -247,6 +262,7 @@ #endif #endif +// int类型兼容处理 #if HAVE_STDINT_H #include #elif defined(_MSC_VER) && _MSC_VER < 1700 @@ -266,6 +282,7 @@ typedef double float64_t; typedef int (*method_t)(void* userdata); typedef void (*procedure_t)(void* userdata); +// 根据生成的hconfig.h,#include一些非ANSI C 头文件 #if HAVE_SYS_TYPES_H #include #endif diff --git a/hexport.h b/hexport.h index 5c69918db..30b215897 100644 --- a/hexport.h +++ b/hexport.h @@ -1,7 +1,15 @@ #ifndef HV_EXPORT_H_ #define HV_EXPORT_H_ +/* + * @功能: 此头文件主要是定义动态库导出宏HV_EXPORT + * + * @备注: 如果是以静态库方式使用,需将HV_STATICLIB加入到预编译宏 + * + */ + // HV_EXPORT +// 接口导出宏 #if defined(HV_STATICLIB) || defined(HV_SOURCE) #define HV_EXPORT #elif defined(_MSC_VER) @@ -17,6 +25,7 @@ #endif // HV_DEPRECATED +// 接口过时声明宏 #if defined(HV_NO_DEPRECATED) #define HV_DEPRECATED #elif defined(__GNUC__) || defined(__clang__) @@ -28,6 +37,7 @@ #endif // HV_UNUSED +// 参数未使用宏 #if defined(__GNUC__) #define HV_UNUSED __attribute__((visibility("unused"))) #else @@ -35,6 +45,13 @@ #endif // @param[IN | OUT | INOUT] +/* + * 参数描述宏: + * IN => 输入参数 + * OUT => 输出参数 + * INOUT=> 既作输入参数,又作输出参数 + * + */ #ifndef IN #define IN #endif @@ -48,6 +65,13 @@ #endif // @field[OPTIONAL | REQUIRED | REPEATED] +/* + * 字段描述宏: + * OPTIONAL => 可选字段 + * REQUIRED => 必需字段 + * REPEATED => 可重复字段 + * + */ #ifndef OPTIONAL #define OPTIONAL #endif @@ -62,6 +86,13 @@ #ifdef __cplusplus +/* + * @NOTE:extern "C"的作用 + * 由于C++支持函数重载,而C语言不支持,因此函数被C++编译后在符号库中的名字是与C语言不同的; + * C++编译后的函数需要加上参数的类型才能唯一标定重载后的函数, + * 加上extern "C",是为了向编译器指明这段代码按照C语言的方式进行编译。 + * + */ #ifndef EXTERN_C #define EXTERN_C extern "C" #endif @@ -74,6 +105,7 @@ #define END_EXTERN_C } // extern "C" #endif +// 命名空间声明宏 #ifndef BEGIN_NAMESPACE #define BEGIN_NAMESPACE(ns) namespace ns { #endif @@ -86,14 +118,17 @@ #define USING_NAMESPACE(ns) using namespace ns; #endif +// 缺省值 #ifndef DEFAULT #define DEFAULT(x) = x #endif +// 枚举类型声明 #ifndef ENUM #define ENUM(e) enum e #endif +// 结构体类型声明 #ifndef STRUCT #define STRUCT(s) struct s #endif diff --git a/hv.h b/hv.h index 51fe0e27e..19acb08f2 100644 --- a/hv.h +++ b/hv.h @@ -1,16 +1,23 @@ #ifndef HV_H_ #define HV_H_ +/* + * @功能: base模块下的头文件比较零散,所以提个了一个汇总的头文件,方便大家#include + * + */ + /** * @copyright 2018 HeWei, all rights reserved. */ // platform +// 平台相关头文件 #include "hconfig.h" #include "hexport.h" #include "hplatform.h" // c +// c语言头文件 #include "hdef.h" // #include "hatomic.h"// #include "herr.h" // @@ -30,6 +37,7 @@ #include "hbuf.h" // cpp +// c++语言头文件 #ifdef __cplusplus #include "hmap.h" // #include "hstring.h" // From 00f53dc498aec800f0416f0e590644214f65d6fc Mon Sep 17 00:00:00 2001 From: "hewei.it" Date: Sun, 23 May 2021 22:00:56 +0800 Subject: [PATCH 2/4] add comments --- base/heap.h | 5 +++++ base/hendian.h | 5 +++++ base/herr.h | 5 +++++ base/hlog.h | 31 +++++++++++++++++++++++++++++++ base/hmath.h | 7 +++++++ base/hmutex.h | 12 ++++++++++++ base/hproc.h | 6 ++++++ base/hsocket.h | 9 +++++++++ base/hssl.h | 5 +++++ 9 files changed, 85 insertions(+) diff --git a/base/heap.h b/base/heap.h index a0c5b5685..6b333fec4 100644 --- a/base/heap.h +++ b/base/heap.h @@ -1,6 +1,11 @@ #ifndef HV_HEAP_H_ #define HV_HEAP_H_ +/* + * @功能:此头文件实现了大小堆 + * + */ + #include // for assert #include // for NULL diff --git a/base/hendian.h b/base/hendian.h index 8181f346c..2677a4f6f 100644 --- a/base/hendian.h +++ b/base/hendian.h @@ -1,6 +1,11 @@ #ifndef HV_ENDIAN_H_ #define HV_ENDIAN_H_ +/* + * @功能:此头文件封装了跨平台的大小端工具函数 + * + */ + #include "hplatform.h" #if defined(OS_MAC) #include diff --git a/base/herr.h b/base/herr.h index 3b3b146da..1468d56e0 100644 --- a/base/herr.h +++ b/base/herr.h @@ -1,6 +1,11 @@ #ifndef HV_ERR_H_ #define HV_ERR_H_ +/* + * @功能:此头文件定义了一些错误码 + * + */ + #include #include "hexport.h" diff --git a/base/hlog.h b/base/hlog.h index 0669ce8c5..ace0c91aa 100644 --- a/base/hlog.h +++ b/base/hlog.h @@ -1,6 +1,11 @@ #ifndef HV_LOG_H_ #define HV_LOG_H_ +/* + * @功能:写日志 + * + */ + /* * hlog is thread-safe */ @@ -15,6 +20,7 @@ #define DIR_SEPARATOR_STR "/" #endif +// __FILE__是完整路径宏,对于日志来说太长,这里我们提取出文件名__FILENAME__ #ifndef __FILENAME__ // #define __FILENAME__ (strrchr(__FILE__, DIR_SEPARATOR) ? strrchr(__FILE__, DIR_SEPARATOR) + 1 : __FILE__) #define __FILENAME__ (strrchr(DIR_SEPARATOR_STR __FILE__, DIR_SEPARATOR) + 1) @@ -26,6 +32,7 @@ extern "C" { #endif +// 定义一些终端颜色宏,方便区分日志级别 #define CLR_CLR "\033[0m" /* 恢复颜色 */ #define CLR_BLACK "\033[30m" /* 黑色字 */ #define CLR_RED "\033[31m" /* 红色字 */ @@ -53,6 +60,7 @@ extern "C" { XXX(LOG_LEVEL_ERROR, "ERROR", CLR_RED) \ XXX(LOG_LEVEL_FATAL, "FATAL", CLR_RED_WHT) +// 定义日志级别 typedef enum { LOG_LEVEL_VERBOSE = 0, #define XXX(id, str, clr) id, @@ -71,37 +79,58 @@ typedef enum { // network_logger() see event/nlog.h typedef void (*logger_handler)(int loglevel, const char* buf, int len); +// 实现了几种常见的日志处理器,标准输出、标准错误、写文件 HV_EXPORT void stdout_logger(int loglevel, const char* buf, int len); HV_EXPORT void stderr_logger(int loglevel, const char* buf, int len); HV_EXPORT void file_logger(int loglevel, const char* buf, int len); +// 在event模块中我们还实现了一个网络日志服务, +// 连上的客户端就会收到日志,是不是有点像android的logcat呢 // network_logger implement see event/nlog.h // HV_EXPORT void network_logger(int loglevel, const char* buf, int len); typedef struct logger_s logger_t; +// 创建日志器 HV_EXPORT logger_t* logger_create(); +// 销毁日志器 HV_EXPORT void logger_destroy(logger_t* logger); +// 设置日志处理器 HV_EXPORT void logger_set_handler(logger_t* logger, logger_handler fn); +// 设置日志级别 HV_EXPORT void logger_set_level(logger_t* logger, int level); // level = [VERBOSE,DEBUG,INFO,WARN,ERROR,FATAL,SILENT] HV_EXPORT void logger_set_level_by_str(logger_t* logger, const char* level); +// 设置一条日志最大的长度 HV_EXPORT void logger_set_max_bufsize(logger_t* logger, unsigned int bufsize); +// 启用日志颜色打印 HV_EXPORT void logger_enable_color(logger_t* logger, int on); +// 打印日志 HV_EXPORT int logger_print(logger_t* logger, int level, const char* fmt, ...); +// 下面是文件日志器的一些设置 // below for file logger +// 设置日志文件 HV_EXPORT void logger_set_file(logger_t* logger, const char* filepath); +// 设置日志文件最大尺寸 HV_EXPORT void logger_set_max_filesize(logger_t* logger, unsigned long long filesize); // 16, 16M, 16MB HV_EXPORT void logger_set_max_filesize_by_str(logger_t* logger, const char* filesize); +// 设置日志保留天数 HV_EXPORT void logger_set_remain_days(logger_t* logger, int days); +// 启用fsync +// @NOTE: fsync用来强制刷新缓存到磁盘,日志的实时可见性更好,但会影响性能, +// 在日志很多的场合,建议关闭此选项,可使用定时器定时调用logger_fsync去刷新到磁盘。 HV_EXPORT void logger_enable_fsync(logger_t* logger, int on); +// 强制调用fsync刷新缓存到磁盘 HV_EXPORT void logger_fsync(logger_t* logger); +// 获取当前的日志文件路径 HV_EXPORT const char* logger_get_cur_file(logger_t* logger); +// 提供一个默认的日志器 // hlog: default logger instance HV_EXPORT logger_t* hv_default_logger(); +// hlog_xxx即使用默认的日志器 // macro hlog* #define hlog hv_default_logger() #define hlog_set_file(filepath) logger_set_file(hlog, filepath) @@ -121,6 +150,7 @@ HV_EXPORT logger_t* hv_default_logger(); #define hloge(fmt, ...) logger_print(hlog, LOG_LEVEL_ERROR, fmt " [%s:%d:%s]\n", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__) #define hlogf(fmt, ...) logger_print(hlog, LOG_LEVEL_FATAL, fmt " [%s:%d:%s]\n", ## __VA_ARGS__, __FILENAME__, __LINE__, __FUNCTION__) +// 在android平台,使用__android_log_print // below for android #if defined(ANDROID) || defined(__ANDROID__) #include @@ -137,6 +167,7 @@ HV_EXPORT logger_t* hv_default_logger(); #define hlogf(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__) #endif +// 更通用的日志宏 // macro alias #if !defined(LOGD) && !defined(LOGI) && !defined(LOGW) && !defined(LOGE) && !defined(LOGF) #define LOGD hlogd diff --git a/base/hmath.h b/base/hmath.h index 993a7b994..10548938a 100644 --- a/base/hmath.h +++ b/base/hmath.h @@ -1,8 +1,14 @@ #ifndef HV_MATH_H_ #define HV_MATH_H_ +/* + * @功能:此头文件补充了一些数学工具函数 + * + */ + #include +// 向下取2的指数倍, floor2e(3) = 2 static inline unsigned long floor2e(unsigned long num) { unsigned long n = num; int e = 0; @@ -12,6 +18,7 @@ static inline unsigned long floor2e(unsigned long num) { return ret; } +// 向上取2的指数倍, floor2e(3) = 4 static inline unsigned long ceil2e(unsigned long num) { // 2**0 = 1 if (num == 0 || num == 1) return 1; diff --git a/base/hmutex.h b/base/hmutex.h index c1791b266..a72426fc5 100644 --- a/base/hmutex.h +++ b/base/hmutex.h @@ -1,6 +1,18 @@ #ifndef HV_MUTEX_H_ #define HV_MUTEX_H_ +/* + * @功能:此头文件封装了跨平台的线程同步手段,包括 + * 互斥量 hmutex_t + * 可重入的互斥量 hrecursive_mutex_t + * 自旋锁 hspinlock_t + * 读写锁 hrwlock_t + * 定时锁 htimed_mutex_t + * 条件变量 hcondvar_t + * 信号量 hsem_t + * + */ + #include "hexport.h" #include "hplatform.h" #include "htime.h" diff --git a/base/hproc.h b/base/hproc.h index b7835626c..063ed7b63 100644 --- a/base/hproc.h +++ b/base/hproc.h @@ -1,6 +1,12 @@ #ifndef HV_PROC_H_ #define HV_PROC_H_ +/* + * @功能:衍生进程 + * @备注:unix使用的多进程、windows下使用的多线程 + * + */ + #include "hplatform.h" typedef struct proc_ctx_s { diff --git a/base/hsocket.h b/base/hsocket.h index 814a2c37f..d0562f027 100644 --- a/base/hsocket.h +++ b/base/hsocket.h @@ -1,6 +1,11 @@ #ifndef HV_SOCKET_H_ #define HV_SOCKET_H_ +/* + * @功能:此头文件封装了跨平台的套接字操作 + * + */ + #include "hexport.h" #include "hplatform.h" @@ -26,6 +31,7 @@ static inline int socket_errno() { } HV_EXPORT const char* socket_strerror(int err); +// 屏蔽一些windows和unix下套接字的差异 #ifdef OS_WIN typedef int socklen_t; static inline int blocking(int sockfd) { @@ -52,6 +58,7 @@ typedef int SOCKET; #endif //-----------------------------sockaddr_u---------------------------------------------- +// 定义了一个包含了IPv4、IPv6、Unix Domain Socket的联合类型 typedef union { struct sockaddr sa; struct sockaddr_in sin; @@ -61,10 +68,12 @@ typedef union { #endif } sockaddr_u; +// 域名解析 // @param host: domain or ip // @retval 0:succeed HV_EXPORT int Resolver(const char* host, sockaddr_u* addr); +// 提供了一系列操作sockaddr_u的工具函数 HV_EXPORT const char* sockaddr_ip(sockaddr_u* addr, char *ip, int len); HV_EXPORT uint16_t sockaddr_port(sockaddr_u* addr); HV_EXPORT int sockaddr_set_ip(sockaddr_u* addr, const char* host); diff --git a/base/hssl.h b/base/hssl.h index 479d64177..a0dede9b8 100644 --- a/base/hssl.h +++ b/base/hssl.h @@ -1,6 +1,11 @@ #ifndef HV_SSL_H_ #define HV_SSL_H_ +/* + * @功能:提供了统一的SSL/TLS封装接口 + * + */ + #include "hexport.h" typedef void* hssl_ctx_t; ///> SSL_CTX From 4275df85742382daf447b2e26eb66188f52889d7 Mon Sep 17 00:00:00 2001 From: "hewei.it" Date: Tue, 25 May 2021 14:03:21 +0800 Subject: [PATCH 3/4] add comments --- examples/consul/main.cpp | 8 ++++++++ examples/curl.cpp | 10 ++++++++++ examples/hloop_test.c | 20 ++++++++++++++++++++ examples/hmain_test.cpp | 14 ++++++++++++++ examples/htimer_test.c | 5 +++++ examples/nc.c | 5 +++++ 6 files changed, 62 insertions(+) diff --git a/examples/consul/main.cpp b/examples/consul/main.cpp index 867f988ef..9712bb3dd 100644 --- a/examples/consul/main.cpp +++ b/examples/consul/main.cpp @@ -4,6 +4,11 @@ #include "consul.h" +/* + * @介绍:consul服务注册与发现示例程序 + * + */ + int main(int argc, char* argv[]) { if (argc < 3) { printf("Usage: consul_cli subcmd ServiceName [ServiceAddress ServicePort] [NodeIP NodePort]\n"); @@ -41,17 +46,20 @@ int main(int argc, char* argv[]) { consul_health_t health; if (strcmp(subcmd, "register") == 0) { + // 注册服务 int ret = register_service(&node, &service, &health); printf("register_service retval=%d\n", ret); goto discover; } else if (strcmp(subcmd, "deregister") == 0) { + // 注销服务 int ret = deregister_service(&node, &service); printf("deregister_service retval=%d\n", ret); goto discover; } else if (strcmp(subcmd, "discover") == 0) { discover: + // 发现服务 std::vector services; discover_services(&node, ServiceName, services); for (auto& service : services) { diff --git a/examples/curl.cpp b/examples/curl.cpp index 4684d68ec..2a088f064 100644 --- a/examples/curl.cpp +++ b/examples/curl.cpp @@ -7,6 +7,12 @@ * bin/curl -v 127.0.0.1:8080/echo -d 'hello,world!' */ +/* + * @介绍:curl是一个著名的URL命令行工具,支持ftp、telnet、smtp、http等多种协议, + * 最常用的还是用作http请求,这个示例代码使用libhv的http客户端实现了类似功能。 + * + */ + #include "http_client.h" #ifdef _MSC_VER @@ -114,6 +120,7 @@ int main(int argc, char* argv[]) { parse_cmdline(argc, argv); int ret = 0; + // 解析命令行参数,填充到请求对象 HttpRequest req; if (grpc) { http_version = 2; @@ -217,8 +224,10 @@ int main(int argc, char* argv[]) { } } HttpResponse res; + // 创建http客户端 http_client_t* hc = http_client_new(); send: + // 发送请求,接收响应 ret = http_client_send(hc, &req, &res); if (verbose) { printf("%s\n", req.Dump(true,true).c_str()); @@ -243,6 +252,7 @@ int main(int argc, char* argv[]) { #endif goto send; } + // 销毁http客户端 http_client_del(hc); return ret; } diff --git a/examples/hloop_test.c b/examples/hloop_test.c index 75b575490..8765ea9eb 100644 --- a/examples/hloop_test.c +++ b/examples/hloop_test.c @@ -6,20 +6,29 @@ * */ +/* + * @介绍:此程序是为了测试事件循环的各种事件,包括空闲事件、IO事件、定时器事件、自定义事件。 + * + */ + #include "hloop.h" #include "hbase.h" #include "hlog.h" #include "nlog.h" +// 自定义日志处理器 void mylogger(int loglevel, const char* buf, int len) { + // ERROR以上级别日志打印到标准错误 if (loglevel >= LOG_LEVEL_ERROR) { stderr_logger(loglevel, buf, len); } + // INFO以上级别日志写入日志文件 if (loglevel >= LOG_LEVEL_INFO) { file_logger(loglevel, buf, len); } + // 所有日志都发送到网络日志器 network_logger(loglevel, buf, len); } @@ -61,27 +70,34 @@ void on_custom_events(hevent_t* ev) { } int main() { + // 在程序退出时做内存检查 // memcheck atexit HV_MEMCHECK; + // 新建事件循环 hloop_t* loop = hloop_new(0); + // 测试空闲事件和事件优先级 // test idle and priority for (int i = HEVENT_LOWEST_PRIORITY; i <= HEVENT_HIGHEST_PRIORITY; ++i) { hidle_t* idle = hidle_add(loop, on_idle, 10); hevent_set_priority(idle, i); } + // 测试超时定时器 // test timer timeout for (int i = 1; i <= 10; ++i) { htimer_t* timer = htimer_add(loop, on_timer, i*1000, 3); hevent_set_userdata(timer, (void*)(intptr_t)i); } + // 测试时间定时器,类似cron效果 + // 获取下一分钟的分钟数,如30,则每小时的第30分钟会触发回调 // test timer period int minute = time(NULL)%3600/60; htimer_add_period(loop, cron_hourly, minute+1, -1, -1, -1, -1, INFINITE); + // 测试网络日志器,类似android logcat效果,可直接使用telent/nc连接端口10514接收日志 // test network_logger htimer_add(loop, timer_write_log, 1000, INFINITE); logger_set_handler(hlog, mylogger); @@ -91,11 +107,13 @@ int main() { #endif nlog_listen(loop, DEFAULT_LOG_PORT); + // 测试非阻塞标准输入 // test nonblock stdin printf("input 'quit' to quit loop\n"); char buf[64]; hread(loop, 0, buf, sizeof(buf), on_stdin); + // 测试自定义事件 // test custom_events for (int i = 0; i < 10; ++i) { hevent_t ev; @@ -106,7 +124,9 @@ int main() { hloop_post_event(loop, &ev); } + // 运行事件循环 hloop_run(loop); + // 释放事件循环 hloop_free(&loop); return 0; } diff --git a/examples/hmain_test.cpp b/examples/hmain_test.cpp index 1771607b5..0a029d7ac 100644 --- a/examples/hmain_test.cpp +++ b/examples/hmain_test.cpp @@ -15,6 +15,20 @@ * */ +/* + * @介绍:该示例程序展示了一个完整命令行程序所需的功能,包括 + * 命令行解析 + * 打印帮助信息 + * 打印版本信息 + * 信号处理 + * 解析配置文件 + * 写日志 + * 后台运行 + * 创建pid文件 + * 多进程/多线程扩展 + * + */ + typedef struct conf_ctx_s { IniParser* parser; int loglevel; diff --git a/examples/htimer_test.c b/examples/htimer_test.c index 99301f0b9..018e1c68d 100644 --- a/examples/htimer_test.c +++ b/examples/htimer_test.c @@ -1,6 +1,11 @@ #include "hloop.h" #include "hbase.h" +/* + * @介绍:此程序是为了测试定时器增加、删除、重置接口。 + * + */ + void on_timer(htimer_t* timer) { printf("time=%llus on_timer\n", LLU(hloop_now(hevent_loop(timer)))); } diff --git a/examples/nc.c b/examples/nc.c index 717fdc166..fa056c9c9 100644 --- a/examples/nc.c +++ b/examples/nc.c @@ -12,6 +12,11 @@ * > [Enter] */ +/* + * @介绍:nc是unix下著名的网络连接客户端,此示例程序使用libhv实现了nc类似的功能。 + * + */ + #include "hloop.h" #include "hbase.h" #include "hsocket.h" From 4524dbd77b4b74f61d9e75b9ae8dbebf941716de Mon Sep 17 00:00:00 2001 From: "hewei.it" Date: Wed, 26 May 2021 16:07:10 +0800 Subject: [PATCH 4/4] add comments for httpd --- examples/httpd/handler.h | 21 ++++++++++++++++++ examples/httpd/httpd.cpp | 46 ++++++++++++++++++++++++++++++++++++++++ examples/httpd/router.h | 9 ++++++++ 3 files changed, 76 insertions(+) diff --git a/examples/httpd/handler.h b/examples/httpd/handler.h index 3db5f9e57..b178e8099 100644 --- a/examples/httpd/handler.h +++ b/examples/httpd/handler.h @@ -15,14 +15,20 @@ class Handler { // if (req->content_type != APPLICATION_JSON) { // return response_status(resp, HTTP_STATUS_BAD_REQUEST); // } + + // 解析body字符串到对应结构体(json、form、kv) req->ParseBody(); + // 响应格式默认为application/json resp->content_type = APPLICATION_JSON; #if 0 + // 前处理中我们可以做一些公共逻辑,如请求统计、请求拦截、API鉴权等, + // 下面是一段简单的Token头校验代码 // authentication sample code if (strcmp(req->path.c_str(), "/login") != 0) { string token = req->GetHeader("token"); if (token.empty()) { response_status(resp, 10011, "Miss token"); + // 返回错误码将直接发送响应,不再走后续的处理 return HTTP_STATUS_UNAUTHORIZED; } else if (strcmp(token.c_str(), "abcdefg") != 0) { @@ -32,10 +38,12 @@ class Handler { return 0; } #endif + // 返回0表示继续走后面的处理流程 return 0; } static int postprocessor(HttpRequest* req, HttpResponse* resp) { + // 后处理中我们可以做一些后处理公共逻辑,如响应状态码统计、响应数据加密等 // printf("%s\n", resp->Dump(true, true).c_str()); return 0; } @@ -46,6 +54,7 @@ class Handler { if (!strTime.empty()) { int ms = atoi(strTime.c_str()); if (ms > 0) { + // 该操作会阻塞当前IO线程,仅用做测试接口,实际应用中请勿在回调中做耗时操作 hv_delay(ms); } } @@ -60,6 +69,7 @@ class Handler { if (!strTime.empty()) { int ms = atoi(strTime.c_str()); if (ms > 0) { + // 在回调线程中可直接使用setTimeout/setInterval定时器接口 hv::setTimeout(ms, [writer](hv::TimerID timerID){ writer->Begin(); HttpResponse* resp = writer->response.get(); @@ -74,6 +84,7 @@ class Handler { static int query(HttpRequest* req, HttpResponse* resp) { // scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment] // ?query => HttpRequest::query_params + // URL里携带的请求参数已被解析到query_params数据结构里,可通过GetParam("key")获取 for (auto& param : req->query_params) { resp->Set(param.first.c_str(), param.second); } @@ -85,6 +96,7 @@ class Handler { if (req->content_type != APPLICATION_URLENCODED) { return response_status(resp, HTTP_STATUS_BAD_REQUEST); } + // 设置响应格式为application/x-www-form-urlencoded resp->content_type = APPLICATION_URLENCODED; resp->kv = req->kv; resp->kv["int"] = hv::to_string(123); @@ -97,6 +109,7 @@ class Handler { if (req->content_type != APPLICATION_JSON) { return response_status(resp, HTTP_STATUS_BAD_REQUEST); } + // 设置响应格式为application/json resp->content_type = APPLICATION_JSON; resp->json = req->json; resp->json["int"] = 123; @@ -109,16 +122,19 @@ class Handler { if (req->content_type != MULTIPART_FORM_DATA) { return response_status(resp, HTTP_STATUS_BAD_REQUEST); } + // 设置响应格式为multipart/form-data resp->content_type = MULTIPART_FORM_DATA; resp->form = req->form; resp->form["int"] = 123; resp->form["float"] = 3.14; resp->form["string"] = "hello"; + // 使用formdata格式传输文件 // resp->form["file"] = FormData(NULL, "test.jpg"); // resp->UploadFormFile("file", "test.jpg"); return 200; } + // 通过 Get/Set 接口可以 获取/设置 key:value 到对应数据结构体 json/form/kv static int test(HttpRequest* req, HttpResponse* resp) { // bool b = req->Get("bool"); // int64_t n = req->Get("int"); @@ -141,6 +157,7 @@ class Handler { if (req->content_type != APPLICATION_GRPC) { return response_status(resp, HTTP_STATUS_BAD_REQUEST); } + // 需引入protobuf库序列化/反序列化body // parse protobuf // ParseFromString(req->body); // resp->content_type = APPLICATION_GRPC; @@ -153,6 +170,7 @@ class Handler { static int restful(HttpRequest* req, HttpResponse* resp) { // RESTful /:field/ => HttpRequest::query_params // path=/group/:group_name/user/:user_id + // restful风格URL里的参数已被解析到query_params数据结构里,可通过GetParam("key")获取 // string group_name = req->GetParam("group_name"); // string user_id = req->GetParam("user_id"); for (auto& param : req->query_params) { @@ -162,6 +180,7 @@ class Handler { return 200; } + // 登录示例:校验用户名,密码,返回一个token static int login(HttpRequest* req, HttpResponse* resp) { string username = req->GetString("username"); string password = req->GetString("password"); @@ -184,6 +203,7 @@ class Handler { } } + // 上传文件示例 static int upload(HttpRequest* req, HttpResponse* resp) { // return resp->SaveFormFile("file", "html/uploads/test.jpg"); if (req->content_type != MULTIPART_FORM_DATA) { @@ -205,6 +225,7 @@ class Handler { } private: + // 统一的响应格式 static int response_status(HttpResponse* resp, int code = 200, const char* message = NULL) { resp->Set("code", code); if (message == NULL) message = http_status_str((enum http_status)code); diff --git a/examples/httpd/httpd.cpp b/examples/httpd/httpd.cpp index e56d1991a..19d79dce4 100644 --- a/examples/httpd/httpd.cpp +++ b/examples/httpd/httpd.cpp @@ -1,3 +1,9 @@ +/* + * @介绍:httpd即HTTP服务端后台程序,该示例程序展示了如何使用libhv构建一个功能完备的HTTP服务端。 + * 打算使用libhv做HTTP服务端的同学建议通读此程序,你将受益匪浅。 + * + */ + #include "hv.h" #include "hmain.h" #include "iniparser.h" @@ -46,7 +52,9 @@ void print_help() { printf("Options:\n%s\n", detail_options); } +// 解析配置文件 int parse_confile(const char* confile) { + // 加载配置文件 IniParser ini; int ret = ini.LoadFromFile(confile); if (ret != 0) { @@ -54,27 +62,36 @@ int parse_confile(const char* confile) { exit(-40); } + // 设置日志文件 // logfile string str = ini.GetValue("logfile"); if (!str.empty()) { strncpy(g_main_ctx.logfile, str.c_str(), sizeof(g_main_ctx.logfile)); } hlog_set_file(g_main_ctx.logfile); + + // 设置日志等级 // loglevel str = ini.GetValue("loglevel"); if (!str.empty()) { hlog_set_level_by_str(str.c_str()); } + + // 设置日志文件大小 // log_filesize str = ini.GetValue("log_filesize"); if (!str.empty()) { hlog_set_max_filesize_by_str(str.c_str()); } + + // 设置日志保留天数 // log_remain_days str = ini.GetValue("log_remain_days"); if (!str.empty()) { hlog_set_remain_days(atoi(str.c_str())); } + + // 是否启用fsync强制刷新日志缓存到磁盘 // log_fsync str = ini.GetValue("log_fsync"); if (!str.empty()) { @@ -83,6 +100,7 @@ int parse_confile(const char* confile) { hlogi("%s version: %s", g_main_ctx.program_name, hv_compile_version()); hlog_fsync(); + // 设置工作进程数 // worker_processes int worker_processes = 0; str = ini.GetValue("worker_processes"); @@ -96,10 +114,13 @@ int parse_confile(const char* confile) { } } g_http_server.worker_processes = LIMIT(0, worker_processes, MAXNUM_WORKER_PROCESSES); + + // 设置工作进程数 // worker_threads int worker_threads = ini.Get("worker_threads"); g_http_server.worker_threads = LIMIT(0, worker_threads, 16); + // 设置http监听端口 // http_port int port = 0; const char* szPort = get_arg("p"); @@ -113,6 +134,8 @@ int parse_confile(const char* confile) { port = ini.Get("http_port"); } g_http_server.port = port; + + // 设置https监听端口 // https_port if (HV_WITH_SSL) { g_http_server.https_port = ini.Get("https_port"); @@ -122,31 +145,42 @@ int parse_confile(const char* confile) { exit(-10); } + // 设置url前缀 // base_url str = ini.GetValue("base_url"); if (str.size() != 0) { g_http_service.base_url = str; } + + // 设置html文档根目录 // document_root str = ini.GetValue("document_root"); if (str.size() != 0) { g_http_service.document_root = str; } + + // 设置首页 // home_page str = ini.GetValue("home_page"); if (str.size() != 0) { g_http_service.home_page = str; } + + // 设置错误页面 // error_page str = ini.GetValue("error_page"); if (str.size() != 0) { g_http_service.error_page = str; } + + // 设置indexof目录 // index_of str = ini.GetValue("index_of"); if (str.size() != 0) { g_http_service.index_of = str; } + + // 设置SSL证书 // ssl if (g_http_server.https_port > 0) { std::string crt_file = ini.GetValue("ssl_certificate"); @@ -170,6 +204,7 @@ int parse_confile(const char* confile) { return 0; } +// reload信号处理: 重新加载配置文件 static void on_reload(void* userdata) { hlogi("reload confile [%s]", g_main_ctx.confile); parse_confile(g_main_ctx.confile); @@ -178,6 +213,7 @@ static void on_reload(void* userdata) { int main(int argc, char** argv) { // g_main_ctx main_ctx_init(argc, argv); + // 解析命令行 //int ret = parse_opt(argc, argv, options); int ret = parse_opt_long(argc, argv, long_options, ARRAY_SIZE(long_options)); if (ret != 0) { @@ -205,31 +241,37 @@ int main(int argc, char** argv) { printf("================================================\n"); */ + // -h 打印帮助信息 // help if (get_arg("h")) { print_help(); exit(0); } + // -v 打印版本信息 // version if (get_arg("v")) { print_version(); exit(0); } + // -c 设置配置文件 // parse_confile const char* confile = get_arg("c"); if (confile) { strncpy(g_main_ctx.confile, confile, sizeof(g_main_ctx.confile)); } + // 解析配置文件 parse_confile(g_main_ctx.confile); + // -t 测试配置文件 // test if (get_arg("t")) { printf("Test confile [%s] OK!\n", g_main_ctx.confile); exit(0); } + // -s 信号处理 // signal signal_init(on_reload); const char* signal = get_arg("s"); @@ -238,6 +280,7 @@ int main(int argc, char** argv) { } #ifdef OS_UNIX + // -d 后台运行 // daemon if (get_arg("d")) { // nochdir, noclose @@ -249,12 +292,15 @@ int main(int argc, char** argv) { } #endif + // 创建pid文件 // pidfile create_pidfile(); // http_server + // 注册路由 Router::Register(g_http_service); g_http_server.service = &g_http_service; + // 运行http服务 ret = http_server_run(&g_http_server); return ret; } diff --git a/examples/httpd/router.h b/examples/httpd/router.h index b0d842567..0242d8803 100644 --- a/examples/httpd/router.h +++ b/examples/httpd/router.h @@ -11,38 +11,45 @@ class Router { public: static void Register(HttpService& router) { + // 前处理 => 处理 => 后处理 // preprocessor => Handler => postprocessor router.preprocessor = Handler::preprocessor; router.postprocessor = Handler::postprocessor; // curl -v http://ip:port/ping router.GET("/ping", [](HttpRequest* req, HttpResponse* resp) { + // 发送字符串 return resp->String("pong"); }); // curl -v http://ip:port/data router.GET("/data", [](HttpRequest* req, HttpResponse* resp) { static char data[] = "0123456789"; + // 发送二进制数据 return resp->Data(data, 10); }); // curl -v http://ip:port/html/index.html router.GET("/html/index.html", [](HttpRequest* req, HttpResponse* resp) { + // 发送文件内容 return resp->File("html/index.html"); }); // curl -v http://ip:port/paths router.GET("/paths", [&router](HttpRequest* req, HttpResponse* resp) { + // 发送json return resp->Json(router.Paths()); }); // curl -v http://ip:port/echo -d "hello,world!" router.POST("/echo", [](HttpRequest* req, HttpResponse* resp) { + // 回显请求 resp->content_type = req->content_type; resp->body = req->body; return 200; }); + // 通配符匹配 // wildcard * // curl -v http://ip:port/wildcard/any router.GET("/wildcard*", [](HttpRequest* req, HttpResponse* resp) { @@ -50,6 +57,7 @@ class Router { return resp->String(str); }); + // 异步响应 // curl -v http://ip:port/async router.GET("/async", [](const HttpRequestPtr& req, const HttpResponseWriterPtr& writer) { writer->response->headers["X-Request-tid"] = hv::to_string(hv_gettid()); @@ -67,6 +75,7 @@ class Router { router.GET("/www.*", [](const HttpRequestPtr& req, const HttpResponseWriterPtr& writer) { HttpRequestPtr req2(new HttpRequest); req2->url = req->path.substr(1); + // 异步HTTP客户端请求 + 异步响应 http_client_send_async(req2, [writer](const HttpResponsePtr& resp2){ writer->Begin(); if (resp2 == NULL) {