欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

structc 开源框架介绍

程序员文章站 2022-03-22 18:33:39
引言 - 一切才刚刚开始 structc 是 C 结构基础库. 简单可复用. structc - https://github.com/wangzhione/structc 之前也描述过几次 structc, 文字多代码风格少. 最近加班不多, 准备详细解说哈其思考初衷. 0.0 整体结构 stru ......

引言 - 一切才刚刚开始

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

   - https://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 cygwin\bin 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:
   msvc\jemalloc_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 <stdlib.h>
#include <string.h>

// :) 高效内存分配, 莫名伤感 ~
// _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        : '\0' 结尾 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 <stdio.h>

#define off_alloc
#include "alloc.h"

#define jemalloc_no_demangle
#include <jemalloc/jemalloc.h>

//
// 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 %zu\n", 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        : '\0' 结尾 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 <pthread.h>
#include <semaphore.h>

//
// 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 <math.h>
#include "alloc.h"
#include <ctype.h>
#include <float.h>
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <stdarg.h>
#include <stdint.h>
#include <stddef.h>
#include <limits.h>
#include <stdbool.h>
#include <inttypes.h>

//
// 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:%lfs\n", ($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 <uv.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//
// 测试 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 = "\033[46;37m";
    buf[1].base = u8"(✿◡‿◡) 喵酱 ((●'-'●)) 比 ♥ 里~ \n";
    buf[2].base = "\033[0m";
    for (i = 0; i < len; ++i)
        buf[i].len = (int)strlen(buf[i].base);
    uv_try_write((uv_stream_t *)&tty, buf, len);

    // 重置终端行为
    uv_tty_reset_mode();
    uv_run(loop, uv_run_default);
}

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

1.2 system 系统库设计

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

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

#ifndef _h_socket
#define _h_socket

#include <time.h>
#include <fcntl.h>
#include "struct.h"
#include <signal.h>
#include <sys/types.h>

#ifdef __gnuc__

#include <netdb.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <sys/resource.h>

//
// 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 <ws2tcpip.h>

#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 == '\0')
        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 = '\0';
        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)
        return eparam;

    // 开始构造 addr
    if (null == addr) {
        sockaddr_t nddr;
        return socket_addr(ip, port, nddr);
    }
    return socket_addr(ip, port, addr);
}

//
// socket_tcp - 创建 tcp 详细套接字
// host     : ip:port 串  
// return   : 返回监听后套接字
//
socket_t 
socket_tcp(const char * host) {
    uint16_t port; char ip[bufsiz];
    if (host_parse(host, ip, &port) < sbase)
        return eparam;
    return socket_listens(ip, port, somaxconn);
}

//
// socket_udp - 创建 udp 详细套接字
// host     : ip:port 串  
// return   : 返回绑定后套接字
//
socket_t 
socket_udp(const char * host) {
    uint16_t port; char ip[bufsiz];
    if (host_parse(host, ip, &port) < sbase)
        return eparam;
    return socket_binds(ip, port, ipproto_udp, null);
}

//
// socket_connects - 返回链接后的阻塞套接字
// host     : ip:port 串  
// return   : 返回链接后阻塞套接字
//
socket_t 
socket_connects(const char * host) {
    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_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) return r;

    // 尝试连接, connect 返回 -1 并且 errno == einprogress 表示正在建立链接
    r = socket_connect(s, addr);
    // connect 链接中, linux 是 einprogress,winds 是 wsaewouldblock
    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 <intrin.h>
#include <intrin0.h>

/* 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 开源框架介绍