本文总阅读量 本站访客数人次 本站总访问量
gongluck's blog
C/C++ Golang 音视频流媒体
开源框架专题

四、开源框架专题

0.项目仓库

1.字符编码和压缩

1.1 字符编码

  • ASCII(American Standard Code for Information Interchange),128个字符,用7位二进制表示(00000000-01111111即0x00-0x7F);EASCII(Extended ASCII),256个字符,用8位二进制表示(00000000-11111111即0x00-0xFF)。当计算机传到了欧洲,国际标准化组织在ASCII的基础上进行了扩展,形成了ISO-8859标准,跟EASCII类似,兼容ASCII,在高128个码位上有所区别。但是由于欧洲的语言环境十分复杂,所以根据各地区的语言又形成了很多子标准,ISO-8859-1、ISO-8859- 2、ISO-8859-3、……、ISO-8859-16。

  • 双字节编码可以是变长的,也就是说同一个编码里面有些字符是单字节表示,有些字符是双字节表示。这样做的好处是,一方面可以兼容ASCII,另一方面可以节省存储容量,代价就是会损失一部分码位。

    • GBK(Chinese Internal Code Specification汉字内码扩展规范)是GB2312的扩展(gbk编码能够用来同时表示繁体字和简体字),按理说都属于双字节编码,码位是一样的,根本谈不上扩展,但实际上是预留空间在起作用。
    • UNICODE字符集(国际标准字符集),它将世界各种语言的每个字符定义一个唯一的编码,以满足跨语言、跨平台的文本信息转换。有多个编码方式,分别是UTF-8,UTF-16,UTF-32编码。
  • UTF是Unicode Transformation Format的缩写,意思是“Unicode转换格式”,后面的数字表明至少使用多少个比特位(Bit)来存储字符。

    • UFT-8:一种变长的编码方案,使用1~6个字节来存储;

      Unicode码表对应utf8编码

    • UFT-32:一种固定长度的编码方案,不管字符编号大小,直接存储Unicode编号,始终使用4个字节来存储;

    • UTF-16:介于UTF-8和UTF-32之间,使用2个或者4个字节来存储,长度既固定又可变。

      Unicode码表对应utf16编码

    • UTF格式在文件中的固定文件头

      utf固定文件头

1.2 libiconv字符编码转换库

  • libiconv例子

    /*
     * @Author: gongluck 
     * @Date: 2020-11-18 07:52:34 
     * @Last Modified by: gongluck
     * @Last Modified time: 2020-11-18 09:14:11
     */
      
    #include <stdio.h>
    #include <stdlib.h>
    #include <iconv.h>
      
    int main()
    {
        iconv_t cd = iconv_open("GBK", "UTF-8"); //UTF8 -> GBK
        if (cd == (iconv_t)-1)
        {
            printf("iconv_open failed.\n");
            exit(-1);
        }
      
        char str[] = "iconv例子";
        char *pstr = str;
        size_t len = sizeof(str);
        char out[100] = {0};
        char *pout = out;
        size_t leftlen = sizeof(out);
        while (pstr-str < sizeof(str))
        {
            pout = out;
            leftlen = sizeof(out);
            int n = iconv(cd, &pstr, &len, &pout, &leftlen);
            //printf("iconv return %d\n", n);
            if (pout != out)//有转换成功字符
            {
                printf("%.*s", (int)(sizeof(out)-leftlen), out);
            }
            else
            {
                break;
            }
              
            if(n != -1)
            {
                pout = out;
              leftlen = sizeof(out);
              n = iconv(cd, NULL, NULL, &pout, &leftlen);
              if (pout != out) // 检测iconv内部是否还有缓存
              {
                  printf("%.*s", (int)(sizeof(out)-leftlen), out);
              }
              break;
            }
        }
        printf("\n");
        iconv_close(cd);
      
        return 0;
    }
    

1.3 zlib

  • 编译安装zlib

    #下载
    wget http://www.zlib.net/zlib-1.2.11.tar.gz
    #解压
    tar -zxvf zlib-1.2.11.tar.gz
    #进入目录
    cd zlib-1.2.11
    #配置
    ./configure
    #编译
    make -j 8
    #检查
    make check
    #安装
    sudo make install
    
  • zlib例子

    /*
     * @Author: gongluck 
     * @Date: 2020-11-18 17:02:24 
     * @Last Modified by: gongluck
     * @Last Modified time: 2020-11-18 17:05:37
     */
      
    // gcc zlib.c -lz
    // ./a.out < zlib.c > out
    // ./a.out -d < out > zlib.c
      
    #include <stdio.h>
    #include <string.h>
    #include <assert.h>
    #include "zlib.h"
      
    #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
    #include <fcntl.h>
    #include <io.h>
    #define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
    #else
    #define SET_BINARY_MODE(file)
    #endif
      
    #define CHUNK 1024
      
    /* Compress from file source to file dest until EOF on source.
       def() returns Z_OK on success, Z_MEM_ERROR if memory could not be
       allocated for processing, Z_STREAM_ERROR if an invalid compression
       level is supplied, Z_VERSION_ERROR if the version of zlib.h and the
       version of the library linked do not match, or Z_ERRNO if there is
       an error reading or writing the files. */
    int def(FILE *source, FILE *dest, int level)
    {
        int ret, flush;
        unsigned have;
        z_stream strm;
        unsigned char in[CHUNK];
        unsigned char out[CHUNK];
      
        /* allocate deflate state */
        strm.zalloc = Z_NULL;
        strm.zfree = Z_NULL;
        strm.opaque = Z_NULL;
        ret = deflateInit(&strm, level);
        if (ret != Z_OK)
            return ret;
      
        /* compress until end of file */
        do
        {
            strm.avail_in = fread(in, 1, CHUNK, source);
            if (ferror(source))
            {
                (void)deflateEnd(&strm);
                return Z_ERRNO;
            }
            flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
            strm.next_in = in;
      
            /* run deflate() on input until output buffer not full, finish
               compression if all of source has been read in */
            do
            {
                strm.avail_out = CHUNK;
                strm.next_out = out;
                ret = deflate(&strm, flush);   /* no bad return value */
                assert(ret != Z_STREAM_ERROR); /* state not clobbered */
                have = CHUNK - strm.avail_out;
                if (fwrite(out, 1, have, dest) != have || ferror(dest))
                {
                    (void)deflateEnd(&strm);
                    return Z_ERRNO;
                }
            } while (strm.avail_out == 0);
            assert(strm.avail_in == 0); /* all input will be used */
      
            /* done when last data in file processed */
        } while (flush != Z_FINISH);
        assert(ret == Z_STREAM_END); /* stream will be complete */
      
        /* clean up and return */
        (void)deflateEnd(&strm);
        return Z_OK;
    }
      
    /* Decompress from file source to file dest until stream ends or EOF.
       inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
       allocated for processing, Z_DATA_ERROR if the deflate data is
       invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
       the version of the library linked do not match, or Z_ERRNO if there
       is an error reading or writing the files. */
    int inf(FILE *source, FILE *dest)
    {
        int ret;
        unsigned have;
        z_stream strm;
        unsigned char in[CHUNK];
        unsigned char out[CHUNK];
      
        /* allocate inflate state */
        strm.zalloc = Z_NULL;
        strm.zfree = Z_NULL;
        strm.opaque = Z_NULL;
        strm.avail_in = 0;
        strm.next_in = Z_NULL;
        ret = inflateInit(&strm);
        if (ret != Z_OK)
            return ret;
      
        /* decompress until deflate stream ends or end of file */
        do
        {
            strm.avail_in = fread(in, 1, CHUNK, source);
            if (ferror(source))
            {
                (void)inflateEnd(&strm);
                return Z_ERRNO;
            }
            if (strm.avail_in == 0)
                break;
            strm.next_in = in;
      
            /* run inflate() on input until output buffer not full */
            do
            {
                strm.avail_out = CHUNK;
                strm.next_out = out;
                ret = inflate(&strm, Z_NO_FLUSH);
                assert(ret != Z_STREAM_ERROR); /* state not clobbered */
                switch (ret)
                {
                case Z_NEED_DICT:
                    ret = Z_DATA_ERROR; /* and fall through */
                case Z_DATA_ERROR:
                case Z_MEM_ERROR:
                    (void)inflateEnd(&strm);
                    return ret;
                }
                have = CHUNK - strm.avail_out;
                if (fwrite(out, 1, have, dest) != have || ferror(dest))
                {
                    (void)inflateEnd(&strm);
                    return Z_ERRNO;
                }
            } while (strm.avail_out == 0);
      
            /* done when inflate() says it's done */
        } while (ret != Z_STREAM_END);
      
        /* clean up and return */
        (void)inflateEnd(&strm);
        return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
    }
      
    /* report a zlib or i/o error */
    void zerr(int ret)
    {
        fputs("zpipe: ", stderr);
        switch (ret)
        {
        case Z_ERRNO:
            if (ferror(stdin))
                fputs("error reading stdin\n", stderr);
            if (ferror(stdout))
                fputs("error writing stdout\n", stderr);
            break;
        case Z_STREAM_ERROR:
            fputs("invalid compression level\n", stderr);
            break;
        case Z_DATA_ERROR:
            fputs("invalid or incomplete deflate data\n", stderr);
            break;
        case Z_MEM_ERROR:
            fputs("out of memory\n", stderr);
            break;
        case Z_VERSION_ERROR:
            fputs("zlib version mismatch!\n", stderr);
        }
    }
      
    /* compress or decompress from stdin to stdout */
    int main(int argc, char **argv)
    {
        int ret;
      
        /* avoid end-of-line conversions */
        SET_BINARY_MODE(stdin);
        SET_BINARY_MODE(stdout);
      
        /* do compression if no arguments */
        if (argc == 1)
        {
            ret = def(stdin, stdout, Z_DEFAULT_COMPRESSION);
            if (ret != Z_OK)
                zerr(ret);
            return ret;
        }
      
        /* do decompression if -d specified */
        else if (argc == 2 && strcmp(argv[1], "-d") == 0)
        {
            ret = inf(stdin, stdout);
            if (ret != Z_OK)
                zerr(ret);
            return ret;
        }
      
        /* otherwise, report usage */
        else
        {
            fputs("zpipe usage: zpipe [-d] < source > dest\n", stderr);
            return 1;
        }
    }
    

2.Json和Xml

2.1 cJSON

  • cJSON例子

    /*
     * @Author: gongluck 
     * @Date: 2020-11-18 21:27:47 
     * @Last Modified by: gongluck
     * @Last Modified time: 2020-11-18 21:41:36
     */
    
    // gcc *.c -lm
    
    #include <stdio.h>
    #include "cJSON.h"
    
    int main()
    {
        cJSON* root = cJSON_CreateObject();
        cJSON_AddBoolToObject(root, "bool", cJSON_False);
        cJSON_AddStringToObject(root, "str", "cJSON");
      
        char* str = cJSON_Print(root);
        printf("%s\n", str);
        cJSON_Delete(root);
        root = NULL;
    
        root = cJSON_Parse(str);
        cJSON* b = cJSON_GetObjectItem(root, "bool");
        if(b->type == cJSON_True || b->type == cJSON_False)
            printf("bool value : %d\n", b->type);
        cJSON* s = cJSON_GetObjectItem(root, "str");
        if(s->type == cJSON_String)
            printf("str value : %s\n", s->valuestring);
        cJSON_Delete(root);
        return 0;
    }
    

2.2 jsoncpp

  • 编译安装

    # 下载
    git clone https://github.com/open-source-parsers/jsoncpp.git
    cd jsoncpp
    mkdir -p build/release
    cd build/release
    # 编译
    cmake -DCMAKE_BUILD_TYPE=release -DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_INCLUDEDIR=include/jsoncpp -DARCHIVE_INSTALL_DIR=. -G "Unix Makefiles" ../..
    make
    # 安装
    sudo make install
    
  • jsoncpp例子

    /*
     * @Author: gongluck 
     * @Date: 2020-11-19 09:40:21 
     * @Last Modified by: gongluck
     * @Last Modified time: 2020-11-19 09:46:42
     */
      
    // g++ test.cpp -ljsoncpp
      
    #include <iostream>
    #include <string>
    #include <jsoncpp/json/json.h>
      
    void readJson()
    {
        std::string strValue = "{\"name\":\"json\",\"array\":[{\"cpp\":\"jsoncpp\"},{\"java\":\"jsoninjava\"},{\"php\":\"support\"}]}";
      
        Json::Reader reader;
        Json::Value value;
      
        if (reader.parse(strValue, value))
        {
            std::string out = value["name"].asString();
            std::cout << out << std::endl;
            const Json::Value arrayObj = value["array"];
            for (unsigned int i = 0; i < arrayObj.size(); i++)
            {
                if (!arrayObj[i].isMember("cpp"))
                    continue;
                out = arrayObj[i]["cpp"].asString();
                std::cout << out;
                if (i != (arrayObj.size() - 1))
                    std::cout << std::endl;
            }
        }
    }
      
    void writeJson()
    {
        Json::Value root;
        Json::Value arrayObj;
        Json::Value item;
      
        item["cpp"] = "jsoncpp";
        item["java"] = "jsoninjava";
        item["php"] = "support";
        arrayObj.append(item);
      
        root["name"] = "json";
        root["array"] = arrayObj;
      
        root.toStyledString();
        std::string out = root.toStyledString();
        std::cout << out << std::endl;
    }
      
    int main(int argc, char **argv)
    {
        readJson();
        writeJson();
        return 0;
    }
    

2.3 TinyXML2

  • 安装编译

    #下载 tinyxml2
    git clone https://github.com/leethomason/tinyxml2.git
    #进入相应目录并编译
    cd tinyxml2/
    cmake . 
    make -j 8
    #测试该版本的准确性
    ./xmltest
    #安装
    sudo make install
    
  • tinyxml2例子

    /*
     * @Author: gongluck 
     * @Date: 2020-11-19 17:11:24 
     * @Last Modified by: gongluck
     * @Last Modified time: 2020-11-19 17:27:23
     */
      
    // g++ test.cpp -ltinyxml2
      
    #include <iostream>
    #include "tinyxml2.h"
      
    int main(void)
    {
        const char *xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>   \
                            <note>                      \
                                <to>beijing</to>             \
                                <from>shenzhen</from>           \
                                <heading>Reminder</heading> \
                                <body>Don't forget the meeting!</body> \
                            </note>";
        tinyxml2::XMLDocument doc;
        doc.Parse(xml);
        std::cout << doc.ErrorID() << std::endl;
      
        // 1. 第一种刷新到本地
        FILE *fp = fopen("memory_1.xml", "wb");
        tinyxml2::XMLPrinter printer(fp);
        doc.Print(&printer); // 打印到文件
        fclose(fp);
      
        // 2. 第二种刷新到本地
        doc.SaveFile("memory_2.xml");
      
        return 0;
    }
    

3.ProtoBuf

3.1 序列化和反序列化

  • 序列化:把对象转换为字节序列的过程称为对象。

    常用序列化协议比较

    • TLV编码及其变体(tag, length和value的缩写):比如Protobuf。
    • 文本流编码:比如XML/JSON。
    • 固定结构编码:基本原理是,协议约定了传输字段类型和字段含义,和TLV的方式类似,但是没有了tag和len,只有value,比如TCP/IP。
    • 内存dump:把内存中的数据直接输出,不做任何序列化操作。反序列化的时候,直接还原内存。
  • 反序列化:把字节序列恢复为对象的过程称为对象的反序列化。

3.2 编译安装ProtoBuf

#下载源码
wget https://github.com/protocolbuffers/protobuf/archive/v3.1.0.tar.gz
tar -zxvf v3.1.0.tar.gz
cd protobuf-3.1.0/
#编译
./autogen.sh
./configure
make -j 8
make check
sudo make install
# refresh shared library cache.
sudo ldconfig

3.3 ProtoBuf例子

/*
 * @Author: gongluck 
 * @Date: 2020-11-20 18:16:05 
 * @Last Modified by:   gongluck 
 * @Last Modified time: 2020-11-20 18:16:05 
 */
 
// protoc --cpp_out=./ addressbook.proto

syntax = "proto3";

package Test;

message Person
{
    string name = 1;
    int32 id = 2;
    string email = 3;

    enum PhoneType
    {
        MOBLIE = 0;//首成员必须为0
        HOME = 1;
        WORK = 2;
    }
    message PhoneNumber
    {
        string number = 1;
        PhoneType type = 2;
    }
    repeated PhoneNumber phones = 4;
}

message AddressBook
{
    repeated Person people = 1;
}
/*
 * @Author: gongluck 
 * @Date: 2020-11-20 17:23:22 
 * @Last Modified by: gongluck
 * @Last Modified time: 2020-11-20 18:14:44
 */

// g++ *.cc *.cpp `pkg-config --cflags --libs protobuf`

#include <iostream>
#include "addressbook.pb.h"

using namespace std;

int main()
{
    char buf[1024];
    int len;

    GOOGLE_PROTOBUF_VERIFY_VERSION;

    Test::Person obj;
    obj.set_name("gongluck");
    obj.set_id(1);
    *obj.mutable_email() = "https://github.com.cnpmjs.org/gongluck/CVIP";
    len = obj.ByteSize();
    cout << "len = " << len << endl;
    obj.SerializeToArray(buf, len);

    Test::Person obj2;
    obj2.ParseFromArray(buf, len);
    cout << "name = " << obj2.name() << endl;
    cout << "id = " << obj2.id() << endl;
    cout << "email = " << obj2.email() << endl;

    google::protobuf::ShutdownProtobufLibrary();

    return 0;
}

4.消息队列

4.1 消息队列比较

消息队列比较

4.2 zmq编译安装

#安装编译依赖
sudo apt-get install libtool pkg-config build-essential autoconf automake
#安装加密库
git clone git://github.com/jedisct1/libsodium.git
cd libsodium
./autogen.sh -s
./configure && make -j 8 && make check
sudo make install
sudo ldconfig
cd ..
#下载
git clone https://github.com/zeromq/libzmq.git
cd libzmq
#查看tag
#git tag
#版本 获取指定的版本
git checkout v4.3.2
./autogen.sh
./configure && make -j 8 && make check
sudo make install
sudo ldconfig
cd ..

4.3 zmq例子

  • 服务端

    /*
     * @Author: gongluck 
     * @Date: 2020-11-24 09:49:11 
     * @Last Modified by: gongluck
     * @Last Modified time: 2020-11-24 09:51:26
     */
      
    // gcc zmqserver.c -lzmq
      
    #include <zmq.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <assert.h>
      
    int main(void)
    {
        // Socket to talk to clients
        void *context = zmq_ctx_new();
        // 与客户端通信的套接字
        void *responder = zmq_socket(context, ZMQ_REP);
        int rc = zmq_bind(responder, "tcp://*:5555");
        assert(rc == 0);
      
        while (1)
        {
            // 等待客户端请求
            char buffer[10];
            zmq_recv(responder, buffer, 10, 0);
            printf("收到 %.*s\n", 10, buffer);
            // 返回应答
            zmq_send(responder, "RECVED", 6, 0);
        }
        return 0;
    }
    
  • 客户端

    /*
     * @Author: gongluck 
     * @Date: 2020-11-24 09:52:58 
     * @Last Modified by: gongluck
     * @Last Modified time: 2020-11-24 09:56:14
     */
      
    //gcc zmqclient.c -lzmq
      
    #include <zmq.h>
    #include <string.h>
    #include <stdio.h>
    #include <unistd.h>
      
    int main(void)
    {
        printf("Connecting to zmq server...\n");
        void *context = zmq_ctx_new();
        // 连接⾄服务端的套接字
        void *requester = zmq_socket(context, ZMQ_REQ);
        zmq_connect(requester, "tcp://localhost:5555");
      
        char buffer[10];
        for (int request_nbr = 0; request_nbr != 10; request_nbr++)
        {  
            printf("正在发送 %d...\n", request_nbr);
            zmq_send(requester, "Hello", 5, 0);
            zmq_recv(requester, buffer, 10, 0);
            printf("接收到 %.*s\n", 10, buffer);
        }
        zmq_close(requester);
        zmq_ctx_destroy(context);
        return 0;
    }
    

5.OpenSSL

5.1 OpenSSL编译安装

wget https://www.openssl.org/source/old/1.1.0/openssl-1.1.0l.tar.gz
tar xzvf OpenSSL-1.1.0l.tar.gz
cd OpenSSL-1.1.0l
./config --prefix=/usr/local/OpenSSL
make -j 8
sudo make install

5.2 OpenSSL例子

/*
 * @Author: gongluck 
 * @Date: 2020-11-24 18:44:16 
 * @Last Modified by: gongluck
 * @Last Modified time: 2020-11-24 18:53:20
 */

// gcc test.c -lssl -lcrypto

#include <stdio.h>
#include <string.h>
#include <openssl/lhash.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <openssl/rsa.h>

#define NAME_LENGTH 32

typedef struct _Person
{
    char name[NAME_LENGTH];
    int high;
    char otherInfo[NAME_LENGTH];
} Person;

// 自定义比较函数
static int person_cmp(const void *a, const void *b)
{
    char *namea = ((Person *)a)->name;
    char *nameb = ((Person *)b)->name;
    return strcmp(namea, nameb);
}

void print_value(void *a)
{
    Person *p = (Person *)a;
    printf("name: %s\n", p->name);
    printf("high: %d\n", p->high);
    printf("other info : %s\n", p->otherInfo);
}

int main()
{
    // hash
    OPENSSL_LHASH *h = lh_new(NULL, person_cmp);
    if (h == NULL)
    {
        printf("err.\n");
        return -1;
    }

    Person p1 = {"gongluck", 170, "xxxx"};
    Person p2 = {"ben", 175, "xxxx"};
    Person p3 = {"ken", 170, "xxxx"};
    Person p4 = {"dio", 170, "xxxx"};

    lh_insert(h, &p1);
    lh_insert(h, &p2);
    lh_insert(h, &p3);
    lh_insert(h, &p4);

    lh_doall(h, print_value);

    void *data = lh_retrieve(h, (const char *)"dio"); //person_cmp return 0
    if (data == NULL)
    {
        return -1;
    }

    print_value(data);
    lh_free(h);

    printf("\n------------------------------\n");

    //bio
    BIO *b = BIO_new(BIO_s_mem());
    int len = BIO_write(b, "OpenSSL", 4);
    len = BIO_printf(b, "%s", "gongluck");
    printf("len: %d\n", len);
    char *out = OPENSSL_malloc(len);
    len = BIO_read(b, out, len);
    printf("%s, len: %d\n", out, len);
    OPENSSL_free(out);
    BIO_free(b);

    printf("\nsocket bio\n");
    int sock = BIO_get_accept_socket("8899", 0);
    BIO *bsock = BIO_new_socket(sock, BIO_NOCLOSE);
    char *addr = NULL;
    int ret = BIO_accept(sock, &addr);
    BIO_set_fd(bsock, ret, BIO_NOCLOSE);
    while (1)
    {
        char out[128] = {0};
        BIO_read(bsock, out, 128);
        if (out[0] = 'q')
            break;
        printf("%s\n", out);
    }
    BIO_free(bsock);

    printf("\n------------------------------\n");

    //base64
    unsigned char in[30], base64[40], decode[30];
    EVP_ENCODE_CTX *ectx = EVP_ENCODE_CTX_new();
    EVP_EncodeInit(ectx);
    for (int i = 0; i < 30; i++)
    {
        in[i] = i;
    }
    int outl, inl = 30;
    EVP_EncodeUpdate(ectx, base64, &outl, in, inl);
    EVP_EncodeFinal(ectx, base64 + outl, &outl);
    EVP_ENCODE_CTX_free(ectx);
    printf("%40s\n", base64);

    printf("\n------------------------------\n");

    //rsa
    unsigned char inbuf[] = "https://github.com/gongluck/CVIP";
    unsigned char outbuf[128] = {0};
    int n = strlen(in);
    MD4(inbuf, n, outbuf);
    printf("MD4 result: \n"); // 16 byte
    for (int i = 0; i < 16; i++)
    {
        printf("%x", outbuf[i]);
    }
    printf("\n");
    MD5(inbuf, n, outbuf);
    printf("MD5 result: \n"); // 16 byte
    for (int i = 0; i < 16; i++)
    {
        printf("%x", outbuf[i]);
    }
    printf("\n");
    // SHA(inbuf, n, outbuf);
    // printf("SHA result: \n"); // 20 byte
    // for (int i = 0; i < 20; i++)
    // {
    //     printf("%x", outbuf[i]);
    // }
    // printf("\n");
    SHA1(inbuf, n, outbuf);
    printf("SHA1 result: \n"); // 20 byte
    for (int i = 0; i < 20; i++)
    {
        printf("%x", outbuf[i]);
    }
    printf("\n");
    SHA256(inbuf, n, outbuf);
    printf("SHA256 result: \n"); // 32 byte
    for (int i = 0; i < 32; i++)
    {
        printf("%x", outbuf[i]);
    }
    printf("\n");
    SHA512(inbuf, n, outbuf);
    printf("SHA512 result: \n"); // 64 byte
    for (int i = 0; i < 64; i++)
    {
        printf("%x", outbuf[i]);
    }
    printf("\n");

    return 0;
}

6.网络IO管理

6.1 信号驱动IO

/*
 * @Author: gongluck 
 * @Date: 2020-11-26 08:05:17 
 * @Last Modified by: gongluck
 * @Last Modified time: 2020-11-26 08:19:07
 */

// test : nc -uv 127.0.0.1 9096

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>

int sockfd = 0;

void do_sigio(int sig)
{
    struct sockaddr_in cli_addr;
    int clilen = sizeof(struct sockaddr_in);

    char buffer[256] = {0};
    int len = recvfrom(sockfd, buffer, 256, 0, (struct sockaddr *)&cli_addr, (socklen_t *)&clilen);
    printf("Message : %s\r\n", buffer);
    int slen = sendto(sockfd, buffer, len, 0, (struct sockaddr *)&cli_addr, clilen);
}

int main(int argc, char *argv[])
{
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    struct sigaction sigio_action;
    sigio_action.sa_flags = 0;
    sigio_action.sa_handler = do_sigio;
    sigaction(SIGIO, &sigio_action, NULL);//SIGIO call do_sigio

    struct sockaddr_in serv_addr = {0};
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(9096);
    serv_addr.sin_addr.s_addr = INADDR_ANY;

    fcntl(sockfd, F_SETOWN, getpid());
    int flags = fcntl(sockfd, F_GETFL, 0);
    flags |= O_ASYNC | O_NONBLOCK;//异步非阻塞
    fcntl(sockfd, F_SETFL, flags);

    bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    while (1)
        sleep(1);

    close(sockfd);

    return 0;
}

6.2 select IO

/*
 * @Author: gongluck 
 * @Date: 2020-11-26 08:36:17 
 * @Last Modified by: gongluck
 * @Last Modified time: 2020-11-26 08:39:02
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <netinet/tcp.h>
#include <arpa/inet.h>

#include <errno.h>
#include <fcntl.h>

#define BUFFER_LENGTH 1024

int main(int argc, char *argv[])
{
    int port = 9096;
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd < 0)
    {
        perror("socket");
        return -1;
    }

    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(listenfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0)
    {
        perror("bind");
        return -2;
    }
    if (listen(listenfd, 5) < 0)
    {
        perror("listen");
        return -3;
    }

    fd_set rfds, rset;
    FD_ZERO(&rfds);
    FD_SET(listenfd, &rfds);

    int max_fd = listenfd;
    int i = 0;

    while (1)
    {
        rset = rfds;
        int nready = select(max_fd + 1, &rset, NULL, NULL, NULL);
        if (nready < 0)
        {
            printf("select error : %d\n", errno);
            continue;
        }

        if (FD_ISSET(listenfd, &rset)) // listen的fd可读代表有连接到达
        {
            struct sockaddr_in client_addr = {0};
            socklen_t client_len = sizeof(client_addr);

            int clientfd = accept(listenfd, (struct sockaddr *)&client_addr, &client_len);
            if (clientfd <= 0)
                continue;

            char str[INET_ADDRSTRLEN] = {0};
            printf("recvived from %s at port %d, listenfd:%d, clientfd:%d\n", inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)),
                   ntohs(client_addr.sin_port), listenfd, clientfd);

            if (max_fd == FD_SETSIZE) // select的fd上限一般是1024
            {
                printf("clientfd --> out range\n");
                break;
            }
            FD_SET(clientfd, &rfds); // 将新连接fd加入到读取队列中

            if (clientfd > max_fd)
                max_fd = clientfd;

            printf("listenfd:%d, max_fd:%d, clientfd:%d\n", listenfd, max_fd, clientfd);

            if (--nready == 0)
                continue;
        }

        for (i = listenfd + 1; i <= max_fd; i++)
        {
            if (FD_ISSET(i, &rset))
            {
                char buffer[BUFFER_LENGTH] = {0};
                int ret = recv(i, buffer, BUFFER_LENGTH, 0);
                if (ret < 0)
                {
                    if (errno == EAGAIN || errno == EWOULDBLOCK) // 实际可能出现被其他线程读取掉数据的情况
                    {
                        printf("read all data");
                    }
                    FD_CLR(i, &rfds);
                    close(i);
                }
                else if (ret == 0)
                {
                    printf("disconnect %d\n", i);
                    FD_CLR(i, &rfds);
                    close(i);
                    break;
                }
                else
                {
                    printf("Recv: %s, %d Bytes\n", buffer, ret);
                }
                if (--nready == 0)
                    break;
            }
        }
    }

    return 0;
}

6.3 poll IO

/*
 * @Author: gongluck 
 * @Date: 2020-11-26 08:36:17 
 * @Last Modified by: gongluck
 * @Last Modified time: 2020-11-26 08:44:12
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/poll.h>

#include <errno.h>
#include <fcntl.h>

#define BUFFER_LENGTH 1024
#define POLL_SIZE 1024

int main(int argc, char *argv[])
{
    int port = 9096;
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd < 0)
    {
        perror("socket");
        return -1;
    }

    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(listenfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0)
    {
        perror("bind");
        return -2;
    }
    if (listen(listenfd, 5) < 0)
    {
        perror("listen");
        return -3;
    }

    struct pollfd fds[POLL_SIZE] = {0};
    fds[0].fd = listenfd;
    fds[0].events = POLLIN;

    int max_fd = 0, i = 0;
    for (i = 1; i < POLL_SIZE; i++)
    {
        fds[i].fd = -1;
    }

    while (1)
    {
        int nready = poll(fds, max_fd + 1, 5);
        if (nready <= 0)
            continue;

        if ((fds[0].revents & POLLIN) == POLLIN) // 判断listenfd是否有数据可读(新连接)
        {
            struct sockaddr_in client_addr = {0};
            socklen_t client_len = sizeof(client_addr);

            int clientfd = accept(listenfd, (struct sockaddr *)&client_addr, &client_len);
            if (clientfd <= 0)
                continue;

            char str[INET_ADDRSTRLEN] = {0};
            printf("recvived from %s at port %d, sockfd:%d, clientfd:%d\n", inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)),
                   ntohs(client_addr.sin_port), listenfd, clientfd);

            fds[clientfd].fd = clientfd;
            fds[clientfd].events = POLLIN;

            if (clientfd > max_fd)
                max_fd = clientfd;

            if (--nready == 0)
                continue;
        }

        for (i = listenfd + 1; i <= max_fd; i++)
        {
            if (fds[i].revents & (POLLIN | POLLERR))
            {
                char buffer[BUFFER_LENGTH] = {0};
                int ret = recv(i, buffer, BUFFER_LENGTH, 0);
                if (ret < 0)
                {
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                    {
                        printf("read all data");
                    }

                    close(i);
                    fds[i].fd = -1;
                }
                else if (ret == 0)
                {
                    printf(" disconnect %d\n", i);

                    close(i);
                    fds[i].fd = -1;
                    break;
                }
                else
                {
                    printf("Recv: %s, %d Bytes\n", buffer, ret);
                }
                if (--nready == 0)
                    break;
            }
        }
    }

    return 0;
}

6.4 epoll IO

/*
 * @Author: gongluck 
 * @Date: 2020-11-26 08:36:17 
 * @Last Modified by: gongluck
 * @Last Modified time: 2020-11-26 08:52:01
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/epoll.h>

#include <errno.h>
#include <fcntl.h>

#define BUFFER_LENGTH 1024
#define EPOLL_SIZE 1024

int main(int argc, char *argv[])
{
    int port = 9096;
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd < 0)
    {
        perror("socket");
        return -1;
    }

    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(listenfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0)
    {
        perror("bind");
        return -2;
    }
    if (listen(listenfd, 5) < 0)
    {
        perror("listen");
        return -3;
    }

    int epoll_fd = epoll_create(EPOLL_SIZE);
    struct epoll_event ev, events[EPOLL_SIZE] = {0};

    ev.events = EPOLLIN;
    ev.data.fd = listenfd;
    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listenfd, &ev);

    while (1)
    {
        int nready = epoll_wait(epoll_fd, events, EPOLL_SIZE, -1);
        if (nready == -1)
        {
            printf("epoll_wait\n");
            break;
        }

        for (int i = 0; i < nready; i++)
        {
            if (events[i].data.fd == listenfd)
            {
                struct sockaddr_in client_addr = {0};
                socklen_t client_len = sizeof(client_addr);

                int clientfd = accept(listenfd, (struct sockaddr *)&client_addr, &client_len);
                if (clientfd <= 0)
                    continue;

                char str[INET_ADDRSTRLEN] = {0};
                printf("recvived from %s at port %d, sockfd:%d, clientfd:%d\n", inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)),
                       ntohs(client_addr.sin_port), listenfd, clientfd);

                ev.events = EPOLLIN | EPOLLET;
                ev.data.fd = clientfd;
                epoll_ctl(epoll_fd, EPOLL_CTL_ADD, clientfd, &ev);
            }
            else
            {
                int clientfd = events[i].data.fd;

                char buffer[BUFFER_LENGTH] = {0};
                int ret = recv(clientfd, buffer, BUFFER_LENGTH, 0);
                if (ret < 0)
                {
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                    {
                        printf("read all data");
                    }

                    close(clientfd);

                    ev.events = EPOLLIN | EPOLLET;
                    ev.data.fd = clientfd;
                    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, clientfd, &ev);
                }
                else if (ret == 0)
                {
                    printf(" disconnect %d\n", clientfd);

                    close(clientfd);

                    ev.events = EPOLLIN | EPOLLET;
                    ev.data.fd = clientfd;
                    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, clientfd, &ev);

                    break;
                }
                else
                {
                    printf("Recv: %s, %d Bytes\n", buffer, ret);
                }
            }
        }
    }

    return 0;
}

Last modified on 2020-11-30