热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

c/c++开发分享structc开源框架介绍

引言-一切才刚刚开始structc是C结构基础库.简单可复用.structc- https:github.comwangzhionestructc之前也描述过几次structc,文

引言 – 一切才刚刚开始

  structc 是 c 结构基础库. 简单可复用. 

   – %ignore_a_1%s://github.com/wangzhione/structc

  之前也描述过几次 structc, 文字多代码风格少. 最近加班不多, 准备详细解说哈其思考初衷.

0.0 整体结构

structc  ├── extern  ├── license  ├── makefile  ├── readme.md  ├── structc  └── structc.sln

structc.sln       : winds  项目管理文件 visual studio 

structc           : 项目整体源码和素材文件目录

readme.md  : 项目介绍 markdown

makefile          : linux 编译文件 make 

license        : mit 开源协议

extern             : 项目引入的外部库目录

extern  ├── jemalloc  ├── jemalloc-vc141-release-static.lib  ├── libuv.lib  ├── pthread.h  ├── pthread_lib.lib  ├── sched.h  ├── semaphore.h  ├── strings.h  ├── uv  └── uv.h

以上就是我们看到 structc 项目整体结构.

0.1 外部库

  当前很谨慎的引入两个半外部库.  最大程度会静态库编译链接运行. 荣我慢慢细说.

1. – https://github.com/jemalloc/jemalloc

  jemalloc 是 c 构建底层高性能 malloc 库. 也被称为系统编程末期最后免费午餐. 整个 structc

malloc 全权交给 je_malloc 抗压.  其中 winds 编译静态库部分, 项目本身也有细说 – 

https://github.com/jemalloc/jemalloc/tree/dev/msvc

how to build jemalloc for windows  =================================    1. install cygwin with at least the following packages:     * autoconf     * autogen     * gawk     * grep     * sed    2. install visual studio 2015 or 2017 with visual c++    3. add cygwinbin to the path environment variable    4. open "x64 native tools command prompt for vs 2017"     (note: x86/x64 doesn't matter at this point)    5. generate header files:     sh -c "cc=cl ./autogen.sh"    6. now the project can be opened and built in visual studio:     msvcjemalloc_vc2017.sln

( 注: vs 使用最新版本. 网址打不开那就fq. 后面其也一样, 时刻保证最新 2018/10/10 ~ )   

对于 linux 编译安装参照下面脚本 

# 开发环境安装  sudo apt install gcc gdb autogen autoconf    # jemalloc 安装  cd  wget https://github.com/jemalloc/jemalloc/releases/download/5.1.0/jemalloc-5.1.0.tar.bz2  tar -jxvf jemalloc-5.1.0.tar.bz2  cd jemalloc-5.1.0    sh autogen.sh  make -j4  sudo make install  sudo ldconfig    cd  rm -rf jemalloc-5.1.0 jemalloc-5.1.0.tar.bz2

当 jemalloc 构建好了. 设计 alloc 层引入到 structc 框架中, 用户取代系统 malloc…

– https://github.com/wangzhione/structc/blob/master/structc/system/alloc.h

#ifndef _h_alloc  #define _h_alloc    #include   #include     // :) 高效内存分配, 莫名伤感 ~  // _msc_ver -> winds cl  // __gnuc__ -> linux gcc  //  #ifdef _msc_ver  //  // cpu 检测 x64 or x86  // isx64 defined 表示 x64 否则 x86  //  #   if defined(_m_arm64) || defined(_m_x64)  #       define isx64  #   endif  //  // _m_ppc 为 powerpc 平台定义, 现在已不支持  // so winds 可以认为都是小端平台  //  #   if defined(_m_ppc)  #       define isbenian  #   endif    #elif  __gnuc__    #   if defined(__x86_64__)  #       define isx64  #   endif  //  // 大小端检测 : isbenian defined 表示大端  //  #   if defined(__big_endian__) || defined(__big_endian_bitfield)  #       define isbenian  #   endif    #else  #   error build ( ̄︶ ̄) s  #endif    // off_alloc - 关闭全局 free / malloc 配置  #ifndef off_alloc    #   undef  free  #   define free    free_    #   undef  strdup  #   define strdup  strdup_    #   undef  malloc  #   define malloc  malloc_  #   undef  calloc  #   define calloc  calloc_  #   undef  realloc  #   define realloc realloc_    #endif//off_alloc    //  // free_ - free 包装函数  // ptr      : 内存首地址  // return   : void  //  extern void free_(void * ptr);    //  // malloc_ - malloc 包装, 封装一些特殊业务  // size     : 分配的内存字节  // return   : 返回可使用的内存地址.  //  extern void * malloc_(size_t size);    //  // strdup_ - strdup 包装函数  // s        : '' 结尾 c 字符串  // return   : 拷贝后新的 c 字符串  //  extern char * strdup_(const char * s);    //  // calloc_ - calloc 包装, 封装一些特殊业务  // num      : 数量  // size     : 大小  // return   : 返回可用内存地址, 并且置0  //  extern void * calloc_(size_t num, size_t size);    //  // realloc_ - realoc 包装函数, 封装一些特殊业务  // ptr      : 内存首地址, null 等同于 malloc  // size     : 重新分配的内存大小  // return   : 返回重新分配好的新地址内容  //  extern void * realloc_(void * ptr, size_t size);    #endif//_h_stdexit

– https://github.com/wangzhione/structc/blob/master/structc/system/alloc.c

#include     #define off_alloc  #include "alloc.h"    #define jemalloc_no_demangle  #include     //  // free_ - free 包装函数  // ptr      : 内存首地址  // return   : void  //  inline void free_(void * ptr) {      je_free(ptr);  }    // 简单内存不足检测处理  static inline void * mcheck(void * ptr, size_t size) {      if (null == ptr) {          fprintf(stderr, "out of memory trying to allocate %zun", size);          fflush(stderr);          abort();      }      return ptr;  }    //  // malloc_ - malloc 包装, 封装一些特殊业务  // size     : 分配的内存字节  // return   : 返回可使用的内存地址.  //  inline void * malloc_(size_t size) {      void * ptr = je_malloc(size);      return mcheck(ptr, size);  }    //  // strdup_ - strdup 包装函数  // s        : '' 结尾 c 字符串  // return   : 拷贝后新的 c 字符串  //  inline char * strdup_(const char * s) {      if (s) {          size_t n = strlen(s) + 1;          char * ptr = malloc_(n);          return memcpy(ptr, s, n);      }      return null;  }    //  // calloc_ - calloc 包装, 封装一些特殊业务  // num      : 数量  // size     : 大小  // return   : 返回可用内存地址, 并且置0  //  inline void * calloc_(size_t num, size_t size) {      void * ptr = je_calloc(num, size);      return mcheck(ptr, size);  }    //  // realloc_ - realoc 包装函数, 封装一些特殊业务  // ptr      : 内存首地址, null 等同于 malloc  // size     : 重新分配的内存大小  // return   : 返回重新分配好的新地址内容  //  inline void * realloc_(void * ptr, size_t size) {      void * ntr = je_realloc(ptr, size);      return mcheck(ntr, size);  }

包装了一层. 从 alloc.h 中 off_alloc 宏可以看出, 具备支持插拔能力 ~

2.  – https://github.com/libuv/libuv  

  libuv 用 c 写的高性能单线程网络 io 库. 希望通过它来支撑网络层.  winds 编译静态库

参照 libuv 项目首页燥起来就行. 其中 gyp 安装了这个版本, 其它随波逐流 ~

   – https://github.com/adblockplus/gyp

linux 编译安装脚本 

# libuv 安装  cd  wget https://github.com/libuv/libuv/archive/v1.23.1.zip  unzip v1.23.1.zip  cd libuv-1.23.1    sh autogen.sh  ./configure  make -j4  sudo make install  sudo ldconfig    cd  #  # 注意 uv 头文件, 全部导入到系统 include 目录下面  #  rm -rf libuv-1.23.1 v1.23.1.zip

注意要将编译后 include 完整拷贝到安装目录 include下. 这样 uv 头文件全, 日后会用到.  

libuv 开箱即用, 不太需要什么基础封装. 

3. pthread – https://github.com/gerhobbelt/pthread-win32

  这是最后那半个, 为 winds 引入 posix thread 模型.  编译起来很简单(前提咱们 vs 玩的熟). 

扯点闲篇. linux 和 winds 相辅相成, 对立而统一. 一个是一切从头码, 一个开始就已经注册未来.

描述比较粗, 但大概这意思.  (两个都不 eary, 玩很久才敢入门见岳父岳母) . 这里包装了一层

– https://github.com/wangzhione/structc/blob/master/structc/system/thread.h

#ifndef _h_thread  #define _h_thread    #include "struct.h"  #include   #include     //  // pthread_end - 等待启动线程结束  // tid      : 线程id  // return   : void  //  inline void pthread_end(pthread_t tid) {      pthread_join(tid, null);  }    //  // pthread_run - 异步启动线程  // id       : &tid 线程id地址  // frun     : 运行的主体  // arg      : 运行参数  // return   : 返回线程构建结果, 0 is success  //  #define pthread_run(id, frun, arg)                                    pthread_run_(&(id), (node_f)(frun), (void *)(intptr_t)(arg))  inline int pthread_run_(pthread_t * id, node_f frun, void * arg) {      return pthread_create(id, null, (start_f)frun, arg);  }    //  // pthread_async - 异步启动分离线程  // frun     : 运行的主体  // arg      : 运行参数  // return   : 返回 0 is success  //   #define pthread_async(frun, arg)                                      pthread_async_((node_f)(frun), (void *)(intptr_t)(arg))  inline int pthread_async_(node_f frun, void * arg) {      int ret;      pthread_t tid;      pthread_attr_t attr;        pthread_attr_init(&attr);      pthread_attr_setdetachstate(&attr, pthread_create_detached);      ret = pthread_create(&tid, &attr, (start_f)frun, arg);      pthread_attr_destroy(&attr);        return ret;  }    #endif//_h_thread

利用现代编译器兼容性构建了 pthread 两种启动宏, 后续写 pthread create 相关代码会得心应手!  

到此我们大一统治线程模型就定下来了. 还顺带引出了一个很重要辅助头文件. 

– https://github.com/wangzhione/structc/blob/master/structc/struct/struct.h

#ifndef _h_struct  #define _h_struct    #include   #include "alloc.h"  #include   #include   #include   #include   #include   #include   #include   #include   #include   #include   #include     //  // enum flag int - 函数返回值全局状态码  // >= 0 标识 success 状态, <0 标识 error 状态  //  enum {      sbase       =  +0, // 正确基础类型        ebase       =  -1, // 错误基础类型      eparam      =  -2, // 输入参数错误      efd         =  -3, // 文件打开失败      eclose      =  -4, // 文件操作关闭      eaccess     =  -5, // 没有操作权限      ealloc      =  -6, // 内存操作错误      eparse      =  -7, // 协议解析错误      esmall      =  -8, // 过小基础错误      ebig        =  -9, // 过大基础错误      etimeout    = -10, // 操作超时错误  };    //  // dcode - debug 模式下的测试宏  // dcode({  //     puts("debug start...");  // });  //  #ifndef dcode  #   ifdef _debug  #       define dcode(code)  do code while(0)  #   else  #       define dcode(code)    #   endif //  ! _debug  #endif  //  ! dcode    //  // icmp_f - 比较行为的类型  //  : int add_cmp(const void * now, const void * node)  //  typedef int (* icmp_f)();    //  // vnew_f - 根据规则构建对象  //  : void * rtree_new(void * node)  //  typedef void * (* vnew_f)();    //  // node_f - 销毁当前对象节点  //  : void list_die(void * node);   //  typedef void (* node_f)(void * node);    //  // start_f - pthread create func  //  : int * run(int * arg)  //  typedef void * (* start_f)(void * arg);    //  // each_f - each 循环操作, arg 外部参数, node 是内部结点  //  : int dict_echo(struct dict * node, void * arg) { return 0; }  //  typedef int (* each_f)(void * node, void * arg);    //  // cerr - 打印错误信息  // exit - 打印错误信息, 并 exit  // if   - 条件判断异常退出的辅助宏  //  #define cerr(fmt, ...)                                                     fprintf(stderr, "[%s:%s:%d][%d:%s]" fmt "n",                                  __file__, __func__, __line__, errno, strerror(errno), ##__va_args__)    #define exit(fmt, ...)                                                     do {                                                                           cerr(fmt, ##__va_args__);                                                  exit(exit_failure);                                                    } while(0)    #define if(cond)                                                           if ((cond)) exit(#cond)    //  // return - 打印错误信息, 并 return 返回指定结果  // val      : return的东西, 当需要 return void; 时候填 ',' 就过 or nil  // fmt      : 双引号包裹的格式化字符串  // ...      : fmt中对应的参数  // return   : val  //   #define return(val, fmt, ...)                                             do {                                                                          cerr(fmt, ##__va_args__);                                                 return val;                                                           } while(0)    #define nil  #define retnil(fmt, ...)                                                  return(nil , fmt, ##__va_args__)    #define retnul(fmt, ...)                                                  return(null, fmt, ##__va_args__)    #endif//_h_struct

作者尝试写 structc 项目时第一个源文件 : )

0.2 ide 弱议

   winds 没得选, 最新最全的 visual studio best version 有才能统治一切. 这里主要说

的是 linux 上面我们的选择. 最开始我是 vi + make + gcc + gdb 开发和编译的. 

makefile – https://github.com/wangzhione/structc/blob/master/makefile 

# 编译的目录结构  # release : make   # debug   : make d=-d_debug  # clean   : make clean

make 是编译发布, make d=-d_debug 是编译 debug, make clean 项目清理. 手工操作. 

这样搞对我都还好, 什么都行. 

但不妨更精进一步 [vi + make + gcc + gdb] -> [code + f5 + f10 + f11] 是不是更妙.

微软作为桌面软件霸主, code(vscode 简称)不用我多说, 不得不. 那开搞

1. 安装软件

  ubuntu best version 

  vscode 

 安装好 vscode 后, 在其内部安装插件 microsoft c/c++ for visual studio code

2. f1 -> edit configurations -> c_cpp_properties.json

 设置如下内容和vs配置很相似

{      "configurations": [          {              "name": "linux",              "includepath": [                  "/usr/include/c++/7",                  "/usr/include/x86_64-linux-gnu/c++/7",                  "/usr/include/c++/7/backward",                  "/usr/lib/gcc/x86_64-linux-gnu/7/include",                  "/usr/local/include",                  "/usr/lib/gcc/x86_64-linux-gnu/7/include-fixed",                  "/usr/include/x86_64-linux-gnu",                  "/usr/include",                  "${workspaceroot}",                  "${workspaceroot}/structc/base",                  "${workspaceroot}/structc/struct",                  "${workspaceroot}/structc/system"              ],              "defines": [                  "_debug",                  "__gnuc__"              ],              "intellisensemode": "clang-x64",              "browse": {                  "path": [                      "/usr/include/c++/7",                      "/usr/include/x86_64-linux-gnu/c++/7",                      "/usr/include/c++/7/backward",                      "/usr/lib/gcc/x86_64-linux-gnu/7/include",                      "/usr/local/include",                      "/usr/lib/gcc/x86_64-linux-gnu/7/include-fixed",                      "/usr/include/x86_64-linux-gnu",                      "/usr/include",                      "${workspaceroot}"                  ],                  "limitsymbolstoincludedheaders": true,                  "databasefilename": ""              },              "compilerpath": "/usr/bin/clang",              "cstandard": "c11",              "cppstandard": "c++17"          }      ],      "version": 4  }

3. f5 -> launch.json

    按照规律改 program 项目生成 和 prelaunchtask 前置任务

{      // 使用 intellisense 了解相关属性。       // 悬停以查看现有属性的描述。      // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387      "version": "0.2.0",      "configurations": [          {              "name": "(gdb) launch",              "type": "cppdbg",              "request": "launch",              "program": "${workspacefolder}/out/main.exe",              "args": [],              "stopatentry": false,              "cwd": "${workspacefolder}",              "environment": [],              "externalconsole": true,              "prelaunchtask": "debug",              "mimode": "gdb",              "setupcommands": [                  {                      "description": "enable pretty-printing for gdb",                      "text": "-enable-pretty-printing",                      "ignorefailures": true                  }              ]          }      ]  }

4. f5 -> tasks.json

建立下面任务, 目前只使用了 debug

{      // see https://go.microsoft.com/fwlink/?linkid=733558      // for the documentation about the tasks.json format      "version": "2.0.0",      "tasks": [          {              "type"    : "shell",              "label"   : "debug",              "command" : "make d=-d_debug"          }      ]  }

此刻我们就可以 f5 搞起来 ~

structc 开源框架介绍

兄弟们是不是很亲切, 这么复杂定制化项目都可以可视化调试. 还有谁 ~ 当然 ide 有没有

都好说, 难说的是你是否耐的下心去感悟技术的脉络, 可不能学京东技术, 对开源缺失敬畏

之心, 技术不见得多厉害, 节操提前贷款没了 ~ 最终成为奥义之梗 : )

 

前言 –  不妨说点设计

  进入 structc/structc 看到以下项目结构

wzhi@wzc:~/structc/structc$ tree -l 1  .  ├── base  ├── conf  ├── main  ├── readme.md  ├── struct  ├── structc.vcxproj  ├── structc.vcxproj.filters  ├── structc.vcxproj.user  ├── system  └── test

base      : 基础接口封装目录

conf       : 配置文件目录

main      : 主函数目录

struct    : 数据结构接口目录

system  : 系统库包装目录

test        : 单元测试目录

1.0 main 主函数设计

wzhi@wzc:~/structc/structc/main$ tree  .  ├── main.c  ├── main_init.c  ├── main_run.c  └── main_test.c

重点关注下入口 mian 主函数设计 main.c 

#include "head.h"    //  // main - 程序的总入口, 从扯开始  // argc     : 输入参数个数  // argv     : 参数集  // return   : 返回程序退出的状态码  //  int main(int argc, char * argv[]) {      //      // 初始化 ... ...      // ... ...      extern_run(main_init);        //      // make d=-d_debug      // main_test 单元测试才会启动      //  #ifdef _debug      extern_run(main_test);  #endif        // ...       // ... 启动当前项目运行的主函数      //      extern_run(main_run, argc, argv);        return exit_success;  }

其中 extern_run 也很奇巧

//  // extern_run - 简单的声明, 并立即使用的宏  // ftest    : 需要执行的函数名称  // ...      : 可变参数, 保留  //  #define extern_run(ftest, ...)                                    do {                                                                  extern void ftest();                                              ftest (__va_args__);                                          } while(0)

越过声明直接使用的宏声明. structc 中 main 函数一共做了二件半事情.

main_init 初始化函数, main_run 业务运行函数, 还有半个 main_test 运行单元测试.

随后我们好好看看这个单元测试套路. 

1.1 test 单元测试套路设计

先看看 main_test.c 

#include "head.h"    //  // test - 用于单元测试函数, 执行并输出运行时间  // ftest    : 需要执行的测试函数名称  // ...      : 可变参数, 保留  //  #define test(ftest, ...)                                           do {                                                                   extern void ftest();                                               clock_t $s = clock();                                              ftest (##__va_args__);                                             double $e = (double)clock();                                       printf(str(ftest)" run time:%lfsn", ($e-$s)/clocks_per_sec);  } while(0)      //  // main_test - *_test is here run  // return   : void  //  void main_test(void) {      //      // 开始你的表演, 单元测试      //        extern_run(uv_tty_test);  }

以上只给予了业务测试的能力. 其中 uv_tty_test 函数就是单元测试目录下其中一个的单元测试函数体.

而我们每个业务测试函数, 顺带会创建一个同名的 .c 文件. 例如这里是 uv_tty_test.c 

#include   #include   #include   #include     //  // 测试 libuv tty 操作控制台  // 输出一段有颜色的文字  //  void uv_tty_test(void) {      uv_tty_t tty;      uv_buf_t buf[3];      unsigned i, len = sizeof buf / sizeof *buf;      uv_loop_t * loop = uv_default_loop();        // 目前只对 tty 控制台处理      if (uv_guess_handle(1) != uv_tty) {          fprintf(stderr, "uv_guess_handle(1) != uv_tty!n");          exit(exit_failure);      }        uv_tty_init(loop, &tty, 1, 0);      uv_tty_set_mode(&tty, uv_tty_mode_normal);        // 开始发送消息      buf[0].base = "33[46;37m";      buf[1].base = u8"(✿◡‿◡) 喵酱 ((●'-'●)) 比 ♥ 里~ n";      buf[2].base = "33[0m";      for (i = 0; i 

思路很直白. 这些就是单元测试的真相… . 比较清晰的展示(业务是复杂中减负) 

1.2 system 系统库设计

这里面设计东东不少, 只挑一些经典的供人看看. 代码即注释 ~

– https://github.com/wangzhione/structc/blob/master/structc/system/socket.h

#ifndef _h_socket  #define _h_socket    #include   #include   #include "struct.h"  #include   #include     #ifdef __gnuc__    #include   #include   #include   #include   #include   #include   #include   #include   #include     //  // this is used instead of -1, since the. by winsock  // on now linux eagain and ewouldblock may be the same value   // connect 链接中, linux 是 einprogress,winds 是 wsaewouldblock  //  typedef int socket_t;    #define invalid_socket          (~0)  #define socket_error            (-1)    // socket_init - 初始化 socket 库初始化方法  inline void socket_init(void) {      // 管道破裂, 忽略 sigpipe 信号      signal(sigpipe, sig_ign);  }    inline int socket_close(socket_t s) {      return close(s);  }    // socket_set_block     - 设置套接字是阻塞  // socket_set_nonblock  - 设置套接字是非阻塞  inline int socket_set_block(socket_t s) {      int mode = fcntl(s, f_getfl, 0);      return fcntl(s, f_setfl, mode & ~o_nonblock);  }    inline int socket_set_nonblock(socket_t s) {      int mode = fcntl(s, f_getfl, 0);      return fcntl(s, f_setfl, mode | o_nonblock);  }    // socket_recv      - 读取数据  // socket_send      - 写入数据  inline int socket_recv(socket_t s, void * buf, int sz) {      return (int)read(s, buf, sz);  }    inline int socket_send(socket_t s, const void * buf, int sz) {      return (int)write(s, buf, sz);  }    #endif    #ifdef _msc_ver    #include     #undef  errno  #define errno                   wsagetlasterror()  #undef  strerror  #define strerror                ((char * (*)(int))strerr)    #undef  eintr  #define eintr                   wsaeintr  #undef  eagain  #define eagain                  wsaewouldblock  #undef  einprogress  #define einprogress             wsaewouldblock    /*   * winsock 2 extension -- manifest constants for shutdown()   */  #define shut_rd                 sd_receive  #define shut_wr                 sd_send  #define shut_rdwr               sd_both    #define so_reuseport            so_reuseaddr    typedef socket socket_t;  typedef int socklen_t;    //  // gettimeofday - linux sys/time.h 中得到微秒时间实现  // tv       : 返回结果包含秒数和微秒数  // tz       : 包含的时区, winds 上这个变量没有作用  // return   : 默认返回 0  //  extern int gettimeofday(struct timeval * tv, void * tz);    //  // strerr - linux 上替代 strerror, winds 替代 formatmessage   // error    : linux 是 errno, winds 可以是 wsagetlasterror() ...   // return   : system os 拔下来的提示常量字符串  //  extern const char * strerr(int err);    // socket_init - 初始化 socket 库初始化方法  inline void socket_init(void) {      wsadata wsad;      wsastartup(winsock_version, &wsad);  }    // socket_close     - 关闭上面创建后的句柄  inline int socket_close(socket_t s) {      return closesocket(s);  }    // socket_set_block     - 设置套接字是阻塞  // socket_set_nonblock  - 设置套接字是非阻塞  inline int socket_set_block(socket_t s) {      u_long mode = 0;      return ioctlsocket(s, fionbio, &mode);  }    inline int socket_set_nonblock(socket_t s) {      u_long mode = 1;      return ioctlsocket(s, fionbio, &mode);  }    // socket_recv      - 读取数据  // socket_send      - 写入数据  inline int socket_recv(socket_t s, void * buf, int sz) {      return sz > 0 ? recv(s, buf, sz, 0) : 0;  }    inline int socket_send(socket_t s, const void * buf, int sz) {      return send(s, buf, sz, 0);  }    #endif    //  // 通用 sockaddr_in ipv4 地址  //  typedef struct sockaddr_in sockaddr_t[1];    // socket_dgram     - 创建 udp socket  // socket_stream    - 创建 tcp socket  inline socket_t socket_dgram(void) {      return socket(pf_inet, sock_dgram, ipproto_udp);  }    inline socket_t socket_stream(void) {      return socket(pf_inet, sock_stream, ipproto_tcp);  }    // socket_set_reuse - 开启端口和地址复用  // socket_set_keepalive - 开启心跳包检测, 默认2h 5次  inline int socket_set_enable(socket_t s, int optname) {      int ov = 1;      return setsockopt(s, sol_socket, optname, (void *)&ov, sizeof ov);  }    inline int socket_set_reuse(socket_t s) {      return socket_set_enable(s, so_reuseport);  }    inline int socket_set_keepalive(socket_t s) {      return socket_set_enable(s, so_keepalive);  }    // socket_set_rcvtimeo - 设置接收数据毫秒超时时间  // socket_set_sndtimeo - 设置发送数据毫秒超时时间  inline int socket_set_time(socket_t s, int ms, int optname) {      struct timeval ov = { 0,0 };      if (ms > 0) {          ov.tv_sec = ms / 1000;          ov.tv_usec = (ms % 1000) * 1000;      }      return setsockopt(s, sol_socket, optname, (void *)&ov, sizeof ov);  }    inline int socket_set_rcvtimeo(socket_t s, int ms) {      return socket_set_time(s, ms, so_rcvtimeo);  }    inline int socket_set_sndtimeo(socket_t s, int ms) {      return socket_set_time(s, ms, so_sndtimeo);  }    // socket_get_error - 得到当前socket error 值, 0 表示正确, 其它都是错误  inline int socket_get_error(socket_t s) {      int err;      socklen_t len = sizeof(err);      int r = getsockopt(s, sol_socket, so_error, (void *)&err, &len);      return r <0 ? errno : err;  }    // socket_recvfrom  - recvfrom 接受函数  // socket_sendto    - sendto 发送函数  inline int socket_recvfrom(socket_t s, void * buf, int len, int flags, sockaddr_t in) {      socklen_t inlen = sizeof (sockaddr_t);      return recvfrom(s, buf, len, flags, (struct sockaddr *)in, &inlen);  }    inline int socket_sendto(socket_t s, const void * buf, int len, int flags, const sockaddr_t to) {      return sendto(s, buf, len, flags, (const struct sockaddr *)to, sizeof(sockaddr_t));  }    //  // socket_recvn     - socket 接受 sz 个字节  // socket_sendn     - socket 发送 sz 个字节  //  extern int socket_recvn(socket_t s, void * buf, int sz);  extern int socket_sendn(socket_t s, const void * buf, int sz);    // socket_bind          - bind    绑定函数  // socket_listen        - listen  监听函数  // socket_accept        - accept  等接函数  // socket_connect       - connect 链接函数  inline int socket_bind(socket_t s, const sockaddr_t addr) {      return bind(s, (const struct sockaddr *)addr, sizeof(sockaddr_t));  }    inline int socket_listen(socket_t s) {      return listen(s, somaxconn);  }    inline socket_t socket_accept(socket_t s, sockaddr_t addr) {      socklen_t len = sizeof (sockaddr_t);      return accept(s, (struct sockaddr *)addr, &len);  }    inline int socket_connect(socket_t s, const sockaddr_t addr) {      return connect(s, (const struct sockaddr *)addr, sizeof(sockaddr_t));  }    //  // socket_binds     - 端口绑定返回绑定好的 socket fd, 返回 invalid_socket or pf_inet pf_inet6  // socket_listens   - 端口监听返回监听好的 socket fd.  //  extern socket_t socket_binds(const char * ip, uint16_t port, uint8_t protocol, int * family);  extern socket_t socket_listens(const char * ip, uint16_t port, int backlog);      //  // socket_addr -socket_recv 通过 ip, port 构造 ipv4 结构  //  extern int socket_addr(const char * ip, uint16_t port, sockaddr_t addr);    // socket_pton - 返回 ip 串  inline char * socket_pton(sockaddr_t addr, char ip[inet_addrstrlen]) {      return (char *)inet_ntop(af_inet, &addr->sin_addr, ip, inet_addrstrlen);  }    //  // socket_host - 通过 ip:port 串得到 socket addr 结构  // host     : ip:port 串  // addr     : 返回最终生成的地址  // return   : >= ebase 表示成功  //  extern int socket_host(const char * host, sockaddr_t addr);    //  // socket_tcp - 创建 tcp 详细套接字  // host     : ip:port 串    // return   : 返回监听后套接字  //  extern socket_t socket_tcp(const char * host);    //  // socket_udp - 创建 udp 详细套接字  // host     : ip:port 串    // return   : 返回绑定后套接字  //  extern socket_t socket_udp(const char * host);    //  // socket_connects - 返回链接后的阻塞套接字  // host     : ip:port 串    // return   : 返回链接后阻塞套接字  //  extern socket_t socket_connects(const char * host);    //  // socket_connectos - 返回链接后的非阻塞套接字  // host     : ip:port 串    // ms       : 链接过程中毫秒数  // return   : 返回链接后非阻塞套接字  //  extern socket_t socket_connectos(const char * host, int ms);    #endif//_h_socket

– https://github.com/wangzhione/structc/blob/master/structc/system/socket.c

#include "socket.h"    #ifdef _msc_ver    //  // gettimeofday - linux sys/time.h 中得到微秒时间实现  // tv       : 返回结果包含秒数和微秒数  // tz       : 包含的时区, winds 上这个变量没有作用  // return   : 默认返回 0  //  int   gettimeofday(struct timeval * tv, void * tz) {      struct tm m;      systemtime se;        getlocaltime(&se);      m.tm_year = se.wyear - 1900;      m.tm_mon = se.wmonth - 1;      m.tm_mday = se.wday;      m.tm_hour = se.whour;      m.tm_min = se.wminute;      m.tm_sec = se.wsecond;      m.tm_isdst = -1; // 不考虑夏令时        tv->tv_sec = (long)mktime(&m);      tv->tv_usec = se.wmilliseconds * 1000;        return 0;  }    #endif    //  // socket_recvn     - socket 接受 sz 个字节  // socket_sendn     - socket 发送 sz 个字节  //    int   socket_recvn(socket_t s, void * buf, int sz) {      int r, n = sz;      while (n > 0) {          r = recv(s, buf, n, 0);          if (r == 0) break;          if (r == socket_error) {              if (errno == eintr)                  continue;              return socket_error;          }          n -= r;          buf = (char *)buf + r;      }      return sz - n;  }    int   socket_sendn(socket_t s, const void * buf, int sz) {      int r, n = sz;      while (n > 0) {          r = send(s, buf, n, 0);          if (r == 0) break;          if (r == socket_error) {              if (errno == eintr)                  continue;              return socket_error;          }          n -= r;          buf = (char *)buf + r;      }      return sz - n;  }    //  // socket_addr - 通过 ip, port 构造 ipv4 结构  //  int   socket_addr(const char * ip, uint16_t port, sockaddr_t addr) {      addr->sin_family = af_inet;      addr->sin_port = htons(port);      addr->sin_addr.s_addr = inet_addr(ip);      if (addr->sin_addr.s_addr == inaddr_none) {          struct hostent * host = gethostbyname(ip);          if (!host || !host->h_addr)              return eparam;            // 尝试一种, 默认 ipv4          memcpy(&addr->sin_addr, host->h_addr, host->h_length);      }      memset(addr->sin_zero, 0, sizeof addr->sin_zero);        return sbase;  }    //  // socket_binds     - 端口绑定返回绑定好的 socket fd, 返回 invalid_socket or pf_inet pf_inet6  // socket_listens   - 端口监听返回监听好的 socket fd.  //  socket_t   socket_binds(const char * ip, uint16_t port, uint8_t protocol, int * family) {      socket_t fd;      char ports[sizeof "65535"];      struct addrinfo * addr = null, hint = { 0 };      if (null == ip || *ip == '')          ip = "0.0.0.0"; // default inaddr_any        sprintf(ports, "%hu", port);      hint.ai_family = af_unspec;      if (protocol == ipproto_tcp)          hint.ai_socktype = sock_stream;      else {          assert(protocol == ipproto_udp);          hint.ai_socktype = sock_dgram;      }      hint.ai_protocol = protocol;        if (getaddrinfo(ip, ports, &hint, &addr))          return invalid_socket;        fd = socket(addr->ai_family, addr->ai_socktype, 0);      if (fd == invalid_socket)          goto err_free;      if (socket_set_reuse(fd))          goto err_close;      if (bind(fd, addr->ai_addr, (int)addr->ai_addrlen))          goto err_close;        // success return ip family      if (family)          *family = addr->ai_family;      freeaddrinfo(addr);      return fd;    err_close:      socket_close(fd);  err_free:      freeaddrinfo(addr);      return invalid_socket;  }    socket_t   socket_listens(const char * ip, uint16_t port, int backlog) {      socket_t fd = socket_binds(ip, port, ipproto_tcp, null);      if (invalid_socket != fd && listen(fd, backlog)) {          socket_close(fd);          return invalid_socket;      }      return fd;  }    // host_parse - 解析 host 内容  static int host_parse(const char * host, char ip[bufsiz], uint16_t * pprt) {      int port = 0;      char * st = ip;      if (!host || !*host || *host == ':')          strcpy(ip, "0.0.0.0");      else {          char c;          // 简单检查字符串是否合法          size_t n = strlen(host);          if (n >= bufsiz)              return(eparam, "host err %s", host);            // 寻找分号          while ((c = *host++) != ':' && c)              *ip++ = c;          *ip = '';          if (c == ':') {              if (n > ip - st + sizeof "65535")                  return(eparam, "host port err %s", host);              port = atoi(host);              // 有些常识数字, 不一定是魔法 ... :)              if (port <= 1024 || port > 65535)                  return(eparam, "host port err %s, %d", host, port);          }      }        *pprt = port;      return sbase;  }    //  // socket_host - 通过 ip:port 串得到 socket addr 结构  // host     : ip:port 串  // addr     : 返回最终生成的地址  // return   : >= ebase 表示成功  //  int   socket_host(const char * host, sockaddr_t addr) {      uint16_t port; char ip[bufsiz];      if (host_parse(host, ip, &port) = sbase)          if (socket_connect(s, addr) >= sbase)              return s;        socket_close(s);      return(invalid_socket, "socket_connects %s", host);  }    //  // socket_connecto      - connect 超时链接, 返回非阻塞 socket  //  static int socket_connecto(socket_t s, const sockaddr_t addr, int ms) {      int n, r;      struct timeval to;      fd_set rset, wset, eset;        // 还是阻塞的connect      if (ms <0) return socket_connect(s, addr);        // 非阻塞登录, 先设置非阻塞模式      r = socket_set_nonblock(s);      if (r = sbase || errno != einprogress) {          socket_set_block(s);          return r;      }        // 超时 timeout, 直接返回结果 ebase = -1 错误      if (ms == 0) {          socket_set_block(s);          return ebase;      }        fd_zero(&rset); fd_set(s, &rset);      fd_zero(&wset); fd_set(s, &wset);      fd_zero(&eset); fd_set(s, &eset);      to.tv_sec = ms / 1000;      to.tv_usec = (ms % 1000) * 1000;      n = select((int)s + 1, &rset, &wset, &eset, &to);      // 超时直接滚      if (n <= 0) {          socket_set_block(s);          return ebase;      }        // 当连接成功时候,描述符会变成可写      if (n == 1 && fd_isset(s, &wset)){          socket_set_block(s);          return sbase;      }        // 当连接建立遇到错误时候, 描述符变为即可读又可写      if (fd_isset(s, &eset) || n == 2) {          socklen_t len = sizeof n;          // 只要最后没有 error 那就 链接成功          if (!getsockopt(s, sol_socket, so_error, (char *)&n, &len) && !n)              r = sbase;      }      socket_set_block(s);      return r;  }    //  // socket_connectos - 返回链接后的非阻塞套接字  // host     : ip:port 串    // ms       : 链接过程中毫秒数  // return   : 返回链接后非阻塞套接字  //  socket_t   socket_connectos(const char * host, int ms) {      sockaddr_t addr;      socket_t s = socket_stream();      if (invalid_socket == s) {          return(s, "socket_stream is error");      }        // 解析配置成功后尝试链接      if (socket_host(host, addr) >= sbase)          if (socket_connecto(s, addr, ms) >= sbase)              return s;        socket_close(s);      return(invalid_socket, "socket_connectos %s", host);  }

哪怕 winds, 设计思路也是仿照 linux socket 套路. 构建 socket 接口, 希望上面代码能说明什么是少林

拳法, 千锤百练.  后面 base struct system 代码量不少, 难一一说. 喜欢的可以后续抄袭一次. (我也是

抄袭别人而走入了编程的世界, 了解这分形的人生吧)

 

正文 –  风吹草动

  通过引言前言认识了 structc 是什么样项目, 项目构建, 代码风格等. 这里准备说一下设计 structc

项目初衷. 很久前写 c 代码, 发现数据结构确定后, 基本整个脉络就定下了. 所以想用 c 构建一个通用

简单的数据结构库. 所以有了这个项目. 

  扯一点, 了解 structc 项目后能够为怎样技能加点. 例如学完 struct 目录, 数据结构可以轻松结课. 

抄完 system 操作系统可以结课. base 清楚后, 框架中间件设计也算入门了. 了解整体布局后, 实战中的

脚手架设计也不过如此. 但缺点也有, 见效慢. c 太老了, 想通过 c 看清编程源头, 不下时间是不现实的,

幸运的是最终收获 -> 哈哈 -> 怎么脱发又严重了 …. 

比如 – https://github.com/wangzhione/structc/blob/master/structc/system/atom.h

#ifndef _h_atom  #define _h_atom    #include "atomic.h"    //  // atom_t 自旋锁类型  // [static] atom_t o = 0;  //   atom_lock(o);  //  - one man rpg  // atom_unlock(o);  //  typedef volatile long atom_t;    // atom_acquire - 维护优化后读写代码不在其前  #define atom_acquire()      atomic_fence(atomic_acquire)  // atom_release - 维护优化后读写代码不在其后  #define atom_release()      atomic_fence(atomic_release)  // atom_seq_cst - 维护优化后读写代码前后不动  #define atom_seq_cst()      atomic_fence(atomic_seq_cst)    #ifdef __gnuc__    #define atom_trylock(o)     (!__sync_lock_test_and_set(&(o), 1))    #define atom_lock(o)        while(__sync_lock_test_and_set(&(o), 1))    #define atom_unlock(o)      __sync_lock_release(&(o))    // 内存屏障, 维持代码顺序  #define atom_sync()         __sync_synchronize()    // v += a ; return v;  #define atom_add(v, a)      __sync_add_and_fetch(&(v), (a))  // type tmp = v ; v = a; return tmp;  #define atom_set(v, a)      __sync_lock_test_and_set(&(v), (a))  // v &= a; return v;  #define atom_and(v, a)      __sync_and_and_fetch(&(v), (a))  // return ++v;  #define atom_inc(v)         __sync_add_and_fetch(&(v), 1)  // return --v;  #define atom_dec(v)         __sync_sub_and_fetch(&(v), 1)  // bool b = v == c; b ? v=a : ; return b;  #define atom_cas(v, c, a)   __sync_bool_compare_and_swap(&(v), (c), (a))    #endif    #ifdef _msc_ver    #include   #include     /* interlocked intrinsic mapping for _nf/_acq/_rel */  #if defined(_m_arm) || defined(_m_arm64)  #define _acquire(x) atomic_concat(x, _acq)  #else /* defined(_m_arm) || defined(_m_arm64) */  #define _acquire(x) x  #endif /* defined(_m_arm) || defined(_m_arm64) */    #define atom_trylock(o)     (!_acquire(_interlockedbittestandset)(&(o), 0))    #define atom_lock(o)        while(_acquire(_interlockedbittestandset)(&(o), 0))    inline void store_release(atom_t * x) {      /* store _value atomically with release memory order */  #if defined(_m_arm) || defined(_m_arm64)      __dmb(0xb /* _arm_barrier_ish or _arm64_barrier_ish*/);      __iso_volatile_store32((volatile int *)x, 0);  #else      _readwritebarrier();      *x = 0;  #endif  }    #define atom_unlock(o)      store_release(&(o))    // 保证代码优化后不乱序执行  #define atom_sync()         memorybarrier()    // v 和 a 都是 long 这样数据  #define atom_add(v, a)      interlockedadd((volatile long *)&(v), (long)(a))  #define atom_set(v, a)      interlockedexchange((volatile long *)&(v), (long)(a))  #define atom_and(v, a)      interlockedand((volatile long *)&(v), (long)(a))  #define atom_inc(v)         interlockedincrement((volatile long *)&(v))  #define atom_dec(v)         interlockeddecrement((volatile long *)&(v))  //  // 对于 interlockedcompareexchange(v, c, a) 等价于下面  // long tmp = v ; v == a ? v = c : ; return tmp;  //  // 咱们的 atom_cas(v, c, a) 等价于下面  // long tmp = v ; v == c ? v = a : ; return tmp;  //  #define atom_cas(v, c, a)   ((long)(c) == interlockedcompareexchange((volatile long *)&(v), (long)(a), (long)(c)))    #endif    #endif//_h_atom

代码在改中变的有味道, 有态度. 当然更欢迎同行给予补充, 共同提高进步 ~ 

毕竟错误是难免的 : )     

 

后记 – 江湖再会

 – https://music.163.com/#/song?id=376994

structc 开源框架介绍

 


推荐阅读
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 原文地址http://balau82.wordpress.com/2010/02/28/hello-world-for-bare-metal-arm-using-qemu/最开始时 ... [详细]
  • 32位ubuntu编译android studio,32位Ubuntu编译Android 4.0.4问题
    问题一:在32位Ubuntu12.04上编译Android4.0.4源码时,出现了关于emulator的错误,关键是其Makefile里的 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文概述了JNI的原理以及常用方法。JNI提供了一种Java字节码调用C/C++的解决方案,但引用类型不能直接在Native层使用,需要进行类型转化。多维数组(包括二维数组)都是引用类型,需要使用jobjectArray类型来存取其值。此外,由于Java支持函数重载,根据函数名无法找到对应的JNI函数,因此介绍了JNI函数签名信息的解决方案。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • 【技术分享】一个 ELF 蠕虫分析
    【技术分享】一个 ELF 蠕虫分析 ... [详细]
  • Linux 程序设计学习笔记----动手编写makefile文件
    nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • Word2vec,Fasttext,Glove,Elmo,Bert,Flairpre-trainWordEmbedding源码数据Github网址:词向量预训练实现Githubf ... [详细]
  • 字符设备驱动leds
    内核版本:4.12.9编译器:arm-linux-gcc-4.4.3本驱动基于jz2440v2开发板,实现3个led设备的驱动程序。代码如下:1#include ... [详细]
  • 开发笔记:Squid代理服务
    本文由编程笔记#小编为大家整理,主要介绍了Squid代理服务相关的知识,希望对你有一定的参考价值。Squid服务基础缓存代理概述 ... [详细]
  • 本文介绍了作者在开发过程中遇到的问题,即播放框架内容安全策略设置不起作用的错误。作者通过使用编译时依赖注入的方式解决了这个问题,并分享了解决方案。文章详细描述了问题的出现情况、错误输出内容以及解决方案的具体步骤。如果你也遇到了类似的问题,本文可能对你有一定的参考价值。 ... [详细]
author-avatar
戴耳机聆听世界
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有