在系统层面,linux 设置了 receive buffer 可以配置的最大值,可以在下面的文件中查看,一般是 linux 在启动的时候会根据内存大小设置一个初始值。

/proc/sys/net/core/rmem_max:允许设置的 receive buffer 最大值
/proc/sys/net/core/rmem_default:默认使用的 receive buffer 值
/proc/sys/net/core/wmem_max:允许设置的 send buffer 最大值
/proc/sys/net/core/wmem_dafault:默认使用的 send buffer 最大值

但是这些初始值并不是为了应对大流量的 UDP 报文,如果应用程序接收和发送 UDP 报文非常多,需要讲这个值调大。可以使用 sysctl 命令让它立即生效:

sysctl -w net.core.rmem_max=26214400 # 设置为 25M

也可以修改 /etc/sysctl.conf 中对应的参数在下次启动时让参数保持生效。

如果报文报文过大,可以在发送方对数据进行分割,保证每个报文的大小在 MTU 内。

另外一个可以配置的参数是 netdev_max_backlog,它表示 linux 内核从网卡驱动中读取报文后可以缓存的报文数量,默认是 1000,可以调大这个值,比如设置成 2000:

sudo sysctl -w net.core.netdev_max_backlog=2000

Referenced from:https://cizixs.com/2018/01/13/linux-udp-packet-drop-debug/

MQTTv3.1.1与MQTTv5 的对比

MQTT v5 features and v 3.1.1 features comparison
在這個版本中整體使用方式,MQTT v5與v3.1.1之間功能上的的差別在於 QoS 1 以上不再重傳訊息、retained messages、persistent sessions不再支援了。

MQTT QoS v5 、v3.1.1 之間的定義是一樣的,但v5 的版本不在TCP 連線健康的情況下重傳訊息。 原先的v3.1.1 版本若在一段時間內沒收到 ack 將會在retry,這可能造成因為效能問題導致未回傳的裝置loading 更重。
retained messages: v5中Message Expiry Interval用來取代此功能,可以將其設定一個時間後併刪除。
persistent sessions: 在v3.1.1中若中途有producer將clean session設定為true時,之前所存的message將會被一起被刪除,v5 中Session Expiry Interval 進來取代此功能。
MQTT v5 User Properties

類似http header 的概念,可以在每一個訊息上加入一個property header ,consumer 端可依賴該欄位進行運用。Broker 根據consumer 所訂閱的設定進行訊息routing。

MQTT v5 Shared Subscriptions
在v5的版本中,原生支援load balance的功能,consumer 可在建立連線的時候設定Broker shared選項綁定多個consumer 成為一個群組。
Referenced from:https://ithelp.ithome.com.tw/articles/10257223

解析一个键值对
首先是一个简单的键值对字符串,要解析的目标如下:

{"firstName":"Brett"}

要进行解析,也就是要分别获取到键与值的内容。我们很容易就能看出键为firstName,值为Brett,可是,使用cJSON怎么解析呢? 对于这个简单的例子,只需要调用cJSON的三个接口函数就可以实现解析了,这三个函数的原型如下:

cJSON*cJSON_Parse(const char *value); 
cJSON*cJSON_GetObjectItem(cJSON *object,const char *string);
voidcJSON_Delete(cJSON *c); 

下面按解析过程来描述一次:

首先调用cJSON_Parse()函数,解析JSON数据包,并按照cJSON结构体的结构序列化整个数据包。使用该函数会通过malloc()函数在内存中开辟一个空间,使用完成需要手动释放。

cJSON*root=cJSON_Parse(json_string); 

调用cJSON_GetObjectItem()函数,可从cJSON结构体中查找某个子节点名称(键名称),如果查找成功可把该子节点序列化到cJSON结构体中。

cJSON*item=cJSON_GetObjectItem(root,"firstName"); 

如果需要使用cJSON结构体中的内容,可通过cJSON结构体中的valueint和valuestring取出有价值的内容(即键的值)

本例子中,我们直接访问 item->valuestring 就获取到 "Brett" 的内容了。
通过cJSON_Delete(),释放cJSON_Parse()分配出来的内存空间。

cJSON_Delete(root);

这样就完成了一次cJSON接口调用,实现了解析工作。使用起来其实也很简单的啊,呵呵。

解析一个结构体
接下来,我们来个复杂一点的,解析一个结构体,要解析的目标如下:

{
         "person":
         {
                   "firstName":"z",
                   "lastName":"jadena",
                   "email":"jadena@126.com",
                   "age":8,
                   "height":1.17
         }        
}

看起来比一个键值对复杂多了,我们又需要学习新的接口函数了吗?还是那三个函数就可以了。当然,解析的步骤要复杂一些了,下面我按解析过程来描述一次:

根据JSON串中的对象,我们定义一个相应的结构体如下:

typedef struct
{
         char firstName[32];
         char lastName[32];
         char email[64];
         int age;
         float height;
} PERSON;

具体的对应关系,一目了然,我就不罗嗦了。让我们直奔主题,解析!

还是调用cJSON_Parse()函数,解析JSON数据包。

cJSON*root=cJSON_Parse(json_string);

调用一次cJSON_GetObjectItem()函数,获取到对象person。

cJSON *object=cJSON_GetObjectItem(root,"person");

对我们刚取出来的对象person,多次调用cJSON_GetObjectItem()函数,来获取对象的成员。此时要注意,不同的成员,访问的方法不一样:

cJSON* item;
PERSON person;
item=cJSON_GetObjectItem(object,"firstName");
memcpy(person.firstName,item->valuestring,strlen(item->valuestring));
item=cJSON_GetObjectItem(object,"lastName");
memcpy(person.lastName,item->valuestring,strlen(item->valuestring));
item=cJSON_GetObjectItem(object,"email");
memcpy(person.email,item->valuestring,strlen(item->valuestring));
item=cJSON_GetObjectItem(object,"age");
person.age=item->valueint;
item=cJSON_GetObjectItem(object,"height");
person.height=item->valuedouble;

这样,就获取到了对象的全部内容了。

通过cJSON_Delete(),释放cJSON_Parse()分配出来的内存空间。

cJSON_Delete(root);

至此,我们就使用cJSON接口完成了基于结构体的解析工作。

解析结构体数组的JSON串
最后,我们来个更复杂一些的,来解析一个数组,并且数组的成员是结构体!要解析的JSON串如下:

{
"people":[
{"firstName":"z","lastName":"Jason","email":"bbbb@126.com","height":1.67},
{"lastName":"jadena","email":"jadena@126.com","age":8,"height":1.17},
{"email":"cccc@126.com","firstName":"z","lastName":"Juliet","age":36,"height":1.55}
]
} 

此时,我们真的又需要学习新的接口了,一个是获取数组长度,一个是取数组成员,函数原型如下:

int    cJSON_GetArraySize(cJSON *array);
cJSON*cJSON_GetArrayItem(cJSON *array,int item);

由于前面已经实现了结构体的解析,这里我们只需要关注下数组的相关调用即可。

调用cJSON_Parse()函数,解析JSON数据包。

调用一次cJSON_GetObjectItem()函数,获取到数组people。

对我们刚取出来的数组people,调用cJSON_GetArraySize()函数,来获取数组中对象的个数。然后,多次调用cJSON_GetArrayItem()函数,逐个读取数组中对象的内容。

通过cJSON_Delete(),释放cJSON_Parse()分配出来的内存空间。

调用cJSON_Print(以及cJSON_PrintUnformatted)后,必须自行释放内存,对于1.5版本及以上的cJSON可以使用cJSON_free函数,其他版本直接使用free也可.
对于cJSON_Create..形式的函数,需要使用cJSON_Delete函数释放内存.
如果你把一个item添加到一个数组或者object里,不能直接释放item,因为它已经包含在被添加到数组或者object里了,删除数组或object时被添加的item同时也会被删除。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "./cjson/cJSON.h"
 
/*
    注意:
        通过cJSON_PrintUnformatted(),cJSON_Print()函数返回的字符串需要手动free(释放空间)
        cJSON_CreateObject()函数创造的数据不使用时需要使用cJSON_Delete()释放空间
*/
 
 
/*cJSON的基本封装*/
cJSON *basic_cjson_pkg(void)
{
    cJSON *root = cJSON_CreateObject();
    if (NULL == root) {
        return NULL;
    }
    cJSON_AddNumberToObject(root, "action", 666); 
    cJSON *data = cJSON_CreateObject();
    if (NULL == data) {
        goto err1;
    }
    cJSON_AddStringToObject(data, "user", "Sky.J");
    cJSON_AddStringToObject(data, "pwd", "123456");
    cJSON_AddItemToObject(root, "data", data);
    return root;
err1:
    cJSON_Delete(root);
    return NULL;
}
 
/*cJSON的基本解析*/
void basic_cjson_sys(cJSON *root)
{
    if (NULL == root) {
        return ;
    }
    //将json包转为字符串显示,一般我们使用时选择str1,str2这种方式是更加容易我们观看的方式
    char *str1 = cJSON_PrintUnformatted(root);
    char *str2 = cJSON_Print(root);
    printf("str1: \r\n%s\r\n", str1);
    printf("str2: \r\n%s\r\n", str2);
    cJSON_Delete(root);
    free(str2);
    //一般我们接收到的也是cjson类型的字符串str1,下面解析
    //将字符串格式cjson数据转为cJSON类型数据
    cJSON *sys_root = cJSON_Parse(str1);
    if (NULL == sys_root) {
        goto err1;
    }
    cJSON *action = cJSON_GetObjectItem(sys_root, "action");
    if (NULL == action) {
        goto err2;
    }
    int action_value = action->valueint;
    
    cJSON *data = cJSON_GetObjectItem(sys_root, "data");
    if (NULL == data) {
        goto err2;
    }
    
    cJSON *user = cJSON_GetObjectItem(data, "user");
    if (NULL == user) {
        goto err2;
    }
    char *user_value = user->valuestring;
    
    cJSON *pwd = cJSON_GetObjectItem(data, "pwd");
    if (NULL == pwd) {
        goto err2;
    }
    char *pwd_value = pwd->valuestring;
    
    printf("action = %d, user = %s, pwd = %s\r\n", action_value, user_value, pwd_value);
err2:
    cJSON_Delete(sys_root);
err1:
    free(str1);
    return ;
}
 
/*cJSON的数组封装*/
cJSON *array_cjson_pkg(void)
{
    int i;
    char buffer[20];
    cJSON *root = cJSON_CreateObject();
    if (NULL == root) {
        return NULL;
    }
    cJSON_AddNumberToObject(root, "action", 888); 
    cJSON *data = cJSON_CreateObject();
    if (NULL == data) {
        goto err1;
    }
    cJSON_AddItemToObject(root, "data", data);
    cJSON *rows = cJSON_CreateArray();
    if (NULL == rows) {
        goto err1;
    }
    cJSON_AddItemToObject(data, "rows", rows);
    for (i = 0; i < 3; i++) {
        memset(buffer, 0, sizeof(buffer));
        snprintf(buffer, sizeof(buffer), "user%d", i);
        cJSON *row = cJSON_CreateObject();
        if (NULL == row) {
            goto err1;
        }
        //将新建项目插入数组中
        cJSON_AddItemToArray(rows, row);
        //添加详细信息
        cJSON_AddStringToObject(row, "user", buffer);
        cJSON_AddStringToObject(row, "pwd", "123456");
    }
    return root;
err1:
    cJSON_Delete(root);
    return NULL;
}
 
/*cJSON的数组解析*/
void array_cjson_sys(cJSON *root)
{
    if (NULL == root) {
        return ;
    }
    //将json包转为字符串显示,一般我们使用时选择str1,str2这种方式是更加容易我们观看的方式
    char *str1 = cJSON_PrintUnformatted(root);
    char *str2 = cJSON_Print(root);
    printf("str1: \r\n%s\r\n\r\n", str1);
    printf("str2: \r\n%s\r\n\r\n", str2);
    cJSON_Delete(root);
    free(str2);
    //一般我们接收到的也是cjson类型的字符串str1,下面解析
    //将字符串格式cjson数据转为cJSON类型数据
    cJSON *sys_root = cJSON_Parse(str1);
    if (NULL == sys_root) {
        goto err1;
    }
    cJSON *action = cJSON_GetObjectItem(sys_root, "action");
    if (NULL == action) {
        goto err2;
    }
    int action_value = action->valueint;
    
    cJSON *data = cJSON_GetObjectItem(root, "data");
    if (NULL == data) {
        goto err2;
    }
    //解析数组
    cJSON *rows = cJSON_GetObjectItem(data, "rows");
    if (NULL == rows) {
        goto err2;
    }
    int array_cnt = cJSON_GetArraySize(rows);
    printf("\r\naction = %d, array_cnt = %d\r\n", action_value, array_cnt);
    int i;
    for (i = 0; i < array_cnt; i ++) {
        //取出单个数组
        cJSON *row = cJSON_GetArrayItem(rows, i);
        if (NULL == row) {
            goto err2;
        }
        cJSON *user = cJSON_GetObjectItem(row, "user");
        if (NULL == user) {
            goto err2;
        }
        cJSON *pwd = cJSON_GetObjectItem(row, "pwd");
        if (NULL == pwd) {
            goto err2;
        }
        printf("user: %s, pwd: %s\r\n", user->valuestring, pwd->valuestring);
    }
err2:
    cJSON_Delete(sys_root);
err1:
    free(str1);
    return ;
}
 
 
 
int main()
{
    #if 1
    cJSON *root = basic_cjson_pkg();
    basic_cjson_sys(root);
    #else
    cJSON *root = array_cjson_pkg();
    array_cjson_sys(root);
    #endif
    return 0;
}