libcc

platform

libcc是跨平台,多线程,轻量级的C语言库,提供了更简单的接口和更丰富的协议。提供的函数功能包括:字符串、日志、双向/单向链表、哈希表、网络通信、JSON、XML、INI配置文件读取、AES、DES、MD2、MD4、MD5、base16/base58/base64编码/解码、url编码/解码、时间轮计时器等等。详细信息请参阅C文件

针对各个平台,封装了统一的接口,简化了各类开发过程中常用操作,使你在开发过程中,更加关注实际应用的开发,而不是把时间浪费在琐碎的接口兼容性上面,并且充分利用了各个平台独有的一些特性进行优化。 这个项目的目的是为了使C开发更加的灵活高效。

//跨平台,超轻量,易扩展,框架代码如下:
#define CALL(fn, ...) fn(__VA_ARGS__)

//使用示例:输出hello World!
CALL(printf, "Hello World!\n");
/*
 * 开一个玩笑:)!
 * 
 * 只是出于对C的热爱,累积一份属于自己的C代码
 */

✨ 特征

  • 跨平台(Linux, Windows, MacOS, FreeBSD, Android, iOS)
  • 提供高性能事件循环(网络IO事件、定时器事件)
  • 提供io轮询器,针对epoll, poll, select, kqueue进行跨平台封装
  • 提供高精度、低精度定时器
  • TCP支持心跳、转发、拆包、多线程安全write和close等特性
  • 提供file、directory、socket、thread、time等常用系统接口
  • 提供atomic、atomic64接口
  • 提供mutex、semaphore、spinlock等事件、互斥、信号量、自旋锁操作
  • 提供获取函数堆栈信息的接口,方便调试和错误定位
  • 提供跨平台动态库加载接口(如果系统支持的话)
  • 提供 base16/base58/base64 编解码
  • 提供 AES、DES、MD2、MD4、MD5、SHA 等常用hash算法
  • 提供 Syslog 日志输出、断言等辅助调试工具
  • 提供 URL 编解码
  • 提供 JSON、XML、INI配置文件读取
  • 简单实现 HTTPS 服务端/客户端模块
  • 简单实现 WebSocket 服务端模块
  • 简化数据库操作接口,适配各种数据源,通过统一的url来自动连接打开支持的数据库

⌛️ 安装教程

入门与体验

# 下载编译
git clone https://github.com/libcc/libcc.git
cd libcc

#1、通过Makefile:编译 (Linux,freeBSD,macOS)
make .a platform=linux debug=1
make .so platfrom=linux target=widgets all=1 debug=1
#或者
cd ./build
./build.sh debug
# Windows 下MSYS2环境 执行
./build.cmd debug

#2、通过Visual Studio编译 (Windows)
proj.Win/libcc.vcxproj

#3、通过Android JNI编译
#打开 .bash_profile 配置 $NDK_ROOT = (Android NDK目录)
cd proj.Android/JNI
$NDK/ndk-build NDK_DEBUG=1
#或者
./build_NDK.sh

#4、通过Xcode 编译(macOS,iOS)
proj.OSX/cc.xcodeproj
proj.IOS/cc.xcodeproj

Install MySQL8 devel

  • Centos
    • wget https://repo.mysql.com//mysql80-community-release-el7-7.noarch.rpm
    • rpm -ivh mysql80-community-release-el7-7.noarch.rpm
    • yum -y install mysql-devel

OpenSSL Download Page

  • https://slproweb.com/products/Win32OpenSSL.html

  • iOS

    • https://www.openssl.org/source/openssl-3.2.5.tar.gz
    • sudo ./Configure ios64-cross --prefix=/opt/libcc/include/openssl
    • make && make install

SQLite Download Page

  • https://www.sqlite.org/download.html
  • download:sqlite-amalgamation-3500100.zip sqlit3 header
  • MSYS2 build sqlite3
    • gcc -shared -o sqlite3.dll sqlite3.c -Wl,--out-implib,libsqlite3.a
    • gcc -DSQLITE_ENABLE_COLUMN_METADATA sqlite3.c -shared -o sqlite3.dll -Wl,--out-implib,libsqlite3.a

Linux Ubuntu/Debian

  • sudo apt-get install libsqlite3-dev
  • sudo apt-get install libmysqlclient-dev

FreeBSD

  • sudo pkg install openssl
  • sudo pkg install mysql80-client
  • sudo pkg install sqlite3

macOS Homebrew

  • brew install sqlite
  • brew install mysql-client

Download ODBC driver for MacOSX

  • https://learn.microsoft.com/en-us/sql/connect/odbc/download-odbc-driver-for-sql-server?view=sql-server-ver17&redirectedfrom=MSDN
  • brew tap microsoft/mssql-release https://github.com/Microsoft/homebrew-mssql-release
  • brew update
  • HOMEBREW_ACCEPT_EULA=Y brew install msodbcsql18 mssql-tools18

⚡️ Support

Email: libcc.cn@gmail.com

✨ TCP Server

static int times = 0;
static int c = 0;
static uint16_t port = 3000;

void test_accept(_cc_async_event_t *async, _cc_event_t *e) {
    _cc_socket_t fd;
    _cc_event_t *event;
    struct sockaddr_in remote_addr = {0};
    _cc_socklen_t remote_addr_len = sizeof(struct sockaddr_in);
    _cc_async_event_t *async2 = _cc_get_async_event();

    fd = _cc_event_accept(async, e, (_cc_sockaddr_t *)&remote_addr, &remote_addr_len);
    if (fd == _CC_INVALID_SOCKET_) {
        _cc_logger_debug(_T("thread %d accept fail %s."), _cc_get_thread_id(nullptr),
                         _cc_last_error(_cc_last_errno()));
        return ;
    }

    event = _cc_event_alloc(async2, _CC_EVENT_TIMEOUT_ | _CC_EVENT_READABLE_ | _CC_EVENT_BUFFER_);
    if (event == nullptr) {
        _cc_close_socket(fd);
        return ;
    }

    _cc_set_socket_nonblock(fd, 1);

    event->fd = fd;
    event->callback = e->callback;
    event->timeout = e->timeout;

    if (async2->attach(async2, event) == false) {
        _cc_logger_debug(_T("thread %d add socket (%d) event fial."), _cc_get_thread_id(nullptr), fd);
        _cc_free_event(async2, event);
        return ;
    }
    _cc_logger_debug(_T("%d accept."), event->ident);
}

static bool_t test_callback(_cc_async_event_t *async, _cc_event_t *e, const uint32_t which) {
    if (which & _CC_EVENT_ACCEPT_) {
        test_accept(async,e);
        return true;
    } else if (which & _CC_EVENT_CONNECTED_) {
        _cc_logger_debug(_T("%d connected."), e->ident);
        return true;
    }

    if (which & _CC_EVENT_DISCONNECT_) {
        _cc_logger_debug(_T("%d disconnect."), e->ident);
        return false;
    }

    if (which & _CC_EVENT_READABLE_) {
        _cc_event_rbuf_t *rbuf = &e->buffer->r;
        if (!_cc_event_recv(e)) {
            return false;
        }
        if (_strnicmp((char_t*)rbuf->bytes, "ping", 5) == 0){
            if (_cc_send(e->fd, (byte_t*)"pong", 5) < 0) {
                _cc_logger_debug(_T("%d send pong fail."), e->ident);
                return false;
            }
        } else if (_strnicmp((char_t*)rbuf->bytes, "close", 5) == 0){
            _cc_logger_debug(_T("%d client close."), e->ident);
            return false;
        }
        rbuf->bytes[rbuf->length] = 0;
        _cc_logger_debug("%d: %.*s",e->ident, rbuf->length, rbuf->bytes);
        rbuf->length = 0;
    }

    if (which & _CC_EVENT_WRITABLE_) {
        _cc_logger_debug(_T("%d writeable."), e->ident);
        return _cc_event_sendbuf(e) < 0;
    }

    if (which & _CC_EVENT_TIMEOUT_) {
        _cc_logger_debug(_T("%d timeout."), e->ident);
        if (times++ > 10) {
            if (_cc_send(e->fd, (byte_t*)"close", 5) < 0) {
                _cc_logger_debug(_T("%d send close fail."), e->ident);
                return false;
            }
        } else if (_cc_send(e->fd, (byte_t*)"ping", 5) < 0) {
            _cc_logger_debug(_T("%d send ping fail."), e->ident);
            return false;
        }
    }
    return true;
}

void _test_listen() {
    struct sockaddr_in sa;
    _cc_event_t *event;
    _cc_async_event_t *async = _cc_get_async_event();

    event = _cc_event_alloc(async, _CC_EVENT_ACCEPT_);
    assert(event != NULL);
    if (event == nullptr) {
        return;
    }

    event->timeout = 60000;
    event->callback = test_callback;

    _cc_inet_ipv4_addr(&sa, nullptr, port);
    if (!_cc_tcp_listen(async, event, (_cc_sockaddr_t *)&sa, sizeof(struct sockaddr_in))) {
        _cc_free_event(async, event);    
        assert(false);
    }
}

int main() {
    int i;
    _cc_alloc_async_event(0, nullptr);
    _test_listen();
    while((c = getchar()) != 'q') {
        _cc_sleep(100);
    }
    _cc_free_async_event();
    return 0;
}

✨ TCP Client

static int times = 0;
static int c = 0;
static uint16_t port = 3000;

static bool_t test_callback(_cc_async_event_t *async, _cc_event_t *e, const uint32_t which) {
    if (which & _CC_EVENT_ACCEPT_) {
        test_accept(async,e);
        return true;
    } else if (which & _CC_EVENT_CONNECTED_) {
        _cc_logger_debug(_T("%d connected."), e->ident);
        return true;
    }

    if (which & _CC_EVENT_DISCONNECT_) {
        _cc_logger_debug(_T("%d disconnect."), e->ident);
        return false;
    }

    if (which & _CC_EVENT_READABLE_) {
        _cc_event_rbuf_t *rbuf = &e->buffer->r;
        if (!_cc_event_recv(e)) {
            return false;
        }
        if (_strnicmp((char_t*)rbuf->bytes, "ping", 5) == 0){
            if (_cc_send(e->fd, (byte_t*)"pong", 5) < 0) {
                _cc_logger_debug(_T("%d send pong fail."), e->ident);
                return false;
            }
        } else if (_strnicmp((char_t*)rbuf->bytes, "close", 5) == 0){
            _cc_logger_debug(_T("%d client close."), e->ident);
            return false;
        }
        rbuf->bytes[rbuf->length] = 0;
        _cc_logger_debug("%d: %.*s",e->ident, rbuf->length, rbuf->bytes);
        rbuf->length = 0;
    }

    if (which & _CC_EVENT_WRITABLE_) {
        _cc_logger_debug(_T("%d writeable."), e->ident);
        return _cc_event_sendbuf(e) < 0;
    }

    if (which & _CC_EVENT_TIMEOUT_) {
        _cc_logger_debug(_T("%d timeout."), e->ident);
        if (times++ > 10) {
            if (_cc_send(e->fd, (byte_t*)"close", 5) < 0) {
                _cc_logger_debug(_T("%d send close fail."), e->ident);
                return false;
            }
        } else if (_cc_send(e->fd, (byte_t*)"ping", 5) < 0) {
            _cc_logger_debug(_T("%d send ping fail."), e->ident);
            return false;
        }
    }
    return true;
}

void test_tcp_connect() {
    struct sockaddr_in sa;
    _cc_event_t *event;
    _cc_async_event_t *async = _cc_get_async_event();
    assert(async != NULL);

    event = _cc_event_alloc(async, _CC_EVENT_CONNECT_|_CC_EVENT_TIMEOUT_|_CC_EVENT_BUFFER_);
    assert(event != NULL);
    if (event == nullptr) {
        return;
    }

    event->timeout = 6000;
    event->callback = test_event_callback;

    _cc_inet_ipv4_addr(&sa, "127.0.0.1", port);
    if (!_cc_tcp_connect(async, event, (_cc_sockaddr_t *)&sa, sizeof(struct sockaddr_in))) {
        _cc_free_event(async, event);
        return;
    }
}


int main() {
    int i;
    _cc_alloc_async_event(0, nullptr);

    test_tcp_connect();
    while((c = getchar()) != 'q') {
        _cc_sleep(100);
    }

    _cc_free_async_event();
    return 0;
}