引言 - 一切才刚刚开始

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

  structc - 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. jemalloc - 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* sed2. Install Visual Studio 2015 or 2017 with Visual C++3. Add Cygwin\bin to the PATH environment variable4. 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.0sh autogen.sh
make -j4
sudo make install
sudo ldconfigcd
rm -rf jemalloc-5.1.0 jemalloc-5.1.0.tar.bz2

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

alloc.h - 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

alloc.c - 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. libuv - https://github.com/libuv/libuv  

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

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

  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.1sh autogen.sh
./configure
make -j4
sudo make install
sudo ldconfigcd
#
# 注意 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, 玩很久才敢入门见岳父岳母) . 这里包装了一层

thread.h - 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 相关代码会得心应手!

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

struct.h - 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 搞起来 ~

兄弟们是不是很亲切, 这么复杂定制化项目都可以可视化调试. 还有谁 ~ 当然 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 _DEBUGEXTERN_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 系统库设计

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

socket.h - 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_REUSEADDRtypedef 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

socket.c - 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;// 尝试一种, 默认 ipv4memcpy(&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 familyif (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;// 开始构造 addrif (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;// 还是阻塞的connectif (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 是 WSAEWOULDBLOCKif (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 看清编程源头, 不下时间是不现实的,

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

比如 atom.h - 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

转载于:https://www.cnblogs.com/life2refuel/p/9766875.html

structc 开源框架介绍相关推荐

  1. 2019年上半年收集到的人工智能开源框架介绍文章

    2019年上半年收集到的人工智能开源框架介绍文章 TensorFlow基本使用 TensorFlow.js:让你在浏览器中也能玩转机器学习 人工智能学习框架TensorFlow渐近分析 TensorF ...

  2. IOS-常用第三方开源框架介绍

    iOS开发-常用第三方开源框架介绍(你了解的ios只是冰山一角) 时间:2015-05-06 16:43:34      阅读:533      评论:0      收藏:0      [点我收藏+] ...

  3. OS开发-常用第三方开源框架介绍

    为什么80%的码农都做不了架构师?>>>    OS开发-常用第三方开源框架介绍 http://blog.csdn.net/meiwenjie110/article/details/ ...

  4. J2EE开发之常用开源框架介绍

    From: http://blog.csdn.net/kuyuyingzi/article/details/38351231 主要就我所了解的J2EE开发的框架或开源项目做个介绍,可以根据需求选用适当 ...

  5. Portal开源框架介绍

    Portal 的功能   在JSR168规范中只定义了Portal所应具有的功能的一个最小集合.然而在现实场景中,不论是开源的portal框架实现,还是商业Portal产品都在标准的基础上作了扩展.总 ...

  6. Github优秀的开源框架介绍

    本项目主要对目前 GitHub 上排名前 100 的 Android 开源库进行简单的介绍, 至于排名完全是根据GitHub搜索Java语言选择 (Best Match) 得到的结果, 然后过滤了跟A ...

  7. easyui treegrid获取父节点的id_超简单的分布式ID生成方案!美团开源框架介绍

    目录 阐述背景 Leaf snowflake 模式介绍 Leaf segment 模式介绍 Leaf 改造支持 RPC 阐述背景 不吹嘘,不夸张,项目中用到 ID 生成的场景确实挺多.比如业务要做幂等 ...

  8. 超简单的分布式ID生成方案!美团开源框架介绍

    目录 阐述背景 Leaf snowflake 模式介绍 Leaf segment 模式介绍 Leaf 改造支持 RPC 阐述背景 不吹嘘,不夸张,项目中用到 ID 生成的场景确实挺多.比如业务要做幂等 ...

  9. iOS开发-常用第三方开源框架介绍(你了解的ios只是冰山一角)--(转)

    图像:  1.图片浏览控件MWPhotoBrowser 实现了一个照片浏览器类似 iOS 自带的相册应用,可显示来自手机的图片或者是网络图片,可自动从网络下载图片并进行缓存.可对图片进行缩放等操作.  ...

最新文章

  1. ClickHouse报错解决:Code: 194. DB::Exception: Received from localhost:9000, 127.0.0.1. DB::Exception: Pas
  2. 深度学习机器学习大牛
  3. [云炬创业基础笔记]第六章商业模式测试19
  4. 【CyberSecurityLearning 24】kali中间人攻击
  5. rabbitmq管理界面的使用
  6. 深入理解nodejs的HTTP处理流程
  7. ejb 2.1 jboss_JBoss AS 8中的Java EE 7和EJB 3.2支持
  8. 解决org.apache.hadoop.io.nativeio.NativeIOException: 当文件已存在时,无法创建该文件。
  9. linux apache目录权限配置,Linux系统架构-----Apache的用户访问权限的设置
  10. dw选项卡代码_Dreamweaver中js实现竖向选项卡或滑动门代码!急!!!
  11. 物业小区管理系统源码
  12. 【图像处理】python实现对图像进行二值化处理
  13. CnOpenData中国行政区划数据
  14. java基于springboot+vue校园电动自行车管理系统
  15. 计算机网络中数据传输速率的单位是什么,计算机网络中传输介质传输速率的单位是用什么表示...
  16. 北理大编程作业:确定母亲节
  17. 老王学JAVA一个月零三天
  18. 正则表达式验证生日手机号信息
  19. 全国计算机注册时密码为什么老是错误,电脑密码正确却显示密码错误怎么办
  20. base64模块的b64encode函数

热门文章

  1. 指令集入选《京津冀物联网产业发展白皮书》
  2. [极客大挑战 2019]PHP1
  3. 斜率优化DP 总结(含凸优化)
  4. html5 英文,HTML5应用编程: 英文
  5. 汇川H3UCAN总线高性能PLC实机程序,本体应用五轴 CAN总线轴控两轴SV630总线伺服电机,最大可扩充16轴运动总线
  6. 富文本编辑器 wangEditor.js
  7. 怎么提升360网站权重?怎么查询网站在360权重
  8. java计算机毕业设计ssm同城绘本馆系统-绘本图书商城
  9. 正则 6-16位数字、大、小字母、特殊字符中至少三种组合
  10. 独家 | 你想用深度学习谱写自己的音乐吗?这篇指南来帮助你!(附代码)