发布者代码:
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "MQTTClient.h"
#define ADDRESS "tcp://localhost:1883"
#define CLIENTID "ExampleClientPub"
#define TOPIC "MQTT Examples"
#define PAYLOAD "Hello World!"
#define QOS 1
#define TIMEOUT 10000L
int GetSec(void)
{
struct timeval tv;
gettimeofday(&tv,NULL);
//printf("GetSec: sec=%d, usec=%d\n", tv.tv_sec, tv.tv_usec);
//printf("start sec=%ld, mill_sec=%ld, micr_sec=%ld\n", \
// tvBef.tv_sec, tvBef.tv_sec*1000 + tvBef.tv_usec/1000, tvBef.tv_sec*1000000 + tvBef.tv_usec);
return tv.tv_sec;
}
int GetMillSec(void)
{
struct timeval tv;
gettimeofday(&tv,NULL);
//printf("GetMillSec: sec=%d, usec=%d\n", tv.tv_sec, tv.tv_usec);
//printf("start sec=%ld, mill_sec=%ld, micr_sec=%ld\n", \
// tvBef.tv_sec, tvBef.tv_sec*1000 + tvBef.tv_usec/1000, tvBef.tv_sec*1000000 + tvBef.tv_usec);
return tv.tv_sec*1000 + tv.tv_usec/1000;
}
int GetMirc(void)
{
struct timeval tv;
gettimeofday(&tv,NULL);
//printf("GetMirc: sec=%d, usec=%d\n", tv.tv_sec, tv.tv_usec);
//printf("start sec=%ld, mill_sec=%ld, micr_sec=%ld\n", \
// tvBef.tv_sec, tvBef.tv_sec*1000 + tvBef.tv_usec/1000, tvBef.tv_sec*1000000 + tvBef.tv_usec);
return tv.tv_sec*1000000 + tv.tv_usec;
}
typedef struct{
int iThrdId; //线程ID
int iTestCnt; //测试次数
}ThrdPara;
int main_thread(void* argv)
{
MQTTClient client;
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
MQTTClient_message pubmsg = MQTTClient_message_initializer;
MQTTClient_deliveryToken token;
int rc, iCnt;
int iBf, iAf;
char szClientId[32];
char payload[128];
ThrdPara *pThrdPara;
pThrdPara = (ThrdPara*)argv;
printf("main_thread: pThrdPara->iThrdId=%d, pThrdPara->iTestCnt=%d.\n", pThrdPara->iThrdId, pThrdPara->iTestCnt);
//用户ID,每个发布消息的客户端都不一样!
memset(szClientId, 0, sizeof(szClientId));
sprintf(szClientId, "%c", pThrdPara->iThrdId);
//待发布的负载数据,通过线程号来区分
memset(payload, 0, sizeof(payload));
sprintf(payload, "hello world! thrd-%d", pThrdPara->iThrdId);
printf("payload: %s\n", payload);
//创建MQTT客户端,绑定服务器地址、客户端ID
// MQTTClient_create(&client, ADDRESS, CLIENTID,
MQTTClient_create(&client, ADDRESS, szClientId,
MQTTCLIENT_PERSISTENCE_NONE, NULL);
conn_opts.keepAliveInterval = 20;
conn_opts.cleansession = 1;
//connect服务端
if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
{
printf("Failed to connect, return code %d\n", rc);
exit(EXIT_FAILURE);
}
iCnt = 0;
printf("--------------------pThrdPara->iThrdId=%d, iCnt=%d----------------------\n", pThrdPara->iThrdId, pThrdPara->iTestCnt);
iBf = GetMirc();
while (iCnt++ < pThrdPara->iTestCnt)
{
//发布消息
//pubmsg.payload = PAYLOAD;
//pubmsg.payloadlen = strlen(PAYLOAD);
pubmsg.payload = payload;
pubmsg.payloadlen = strlen(payload);
pubmsg.qos = QOS;
pubmsg.retained = 0;
MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token);
//printf("Waiting for up to %d seconds for publication of %s\n"
// "on topic %s for client with ClientID: %s\n",
// (int)(TIMEOUT/1000), PAYLOAD, TOPIC, CLIENTID);
//等待消息发布完成
rc = MQTTClient_waitForCompletion(client, token, TIMEOUT);
//printf("Message with delivery token %d delivered\n", token);
//printf("--------------------iCnt=%d----------------------\n", iCnt);
}
//计算消息发布所有的时间,单位微妙
iAf = GetMirc();
printf("thrd-%d, Interv time MircSec=%d.\n", pThrdPara->iThrdId, iAf-iBf);
MQTTClient_disconnect(client, 10000);
MQTTClient_destroy(&client);
return rc;
}
//描述:主函数
//参数:@argc argv参数个数
// @argv:
// argv[1] 线程个数
// argv[2] 单个线程通信的次数
//Example: ./MQTTClient_publish 3 100
// 其中3表示运行3个线程,100表示每个线程发布100次消息
int main(int argc, char *argv[])
{
ThrdPara *pThrdPara;
pthread_t *pthrd;
int iThrdNum;
int iTestNum;
int iThrdIdx;
char szTestNumStr[16];
char ch;
if (argc != 3)
{
printf("main: Failed to argc, argc=%d\n", argc);
exit(0);
}
//通过主函数的参数控制创建线程的个数、每个线程下的消息发布条数
iThrdNum = atoi(argv[1]);
iTestNum = atoi(argv[2]);
printf("main: iThrdNum=%d, iTestNum=%d\n", iThrdNum, iTestNum);
pthrd = malloc(sizeof(pthread_t)*iThrdNum);
pThrdPara = malloc(sizeof(ThrdPara)*iThrdNum);
for (iThrdIdx=0; iThrdIdx<iThrdNum; iThrdIdx++)
{
pThrdPara[iThrdIdx].iThrdId = iThrdIdx;
pThrdPara[iThrdIdx].iTestCnt = iTestNum;
pthread_create(&pthrd[iThrdIdx], NULL, &main_thread, (void *)&pThrdPara[iThrdIdx]);
// sleep(5);
}
do
{
ch = getchar();
} while(ch!='Q' && ch != 'q');
return NULL;
}
订阅者代码:
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "MQTTClient.h"
#define ADDRESS "tcp://localhost:1883"
#define CLIENTID "ExampleClientSub"
#define TOPIC "MQTT Examples"
#define PAYLOAD "Hello World!"
#define QOS 1
#define TIMEOUT 10000L
volatile MQTTClient_deliveryToken deliveredtoken;
void delivered(void *context, MQTTClient_deliveryToken dt)
{
printf("Message with token value %d delivery confirmed\n", dt);
deliveredtoken = dt;
}
int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message)
{
int i;
char* payloadptr;
printf("Message arrived\n");
printf(" topic: %s\n", topicName);
printf(" message: ");
//按照我们的需求,需要在payload负载数据中携带对应数据的属性
payloadptr = message->payload;
for(i=0; i<message->payloadlen; i++)
{
putchar(*payloadptr++);
}
putchar('\n');
MQTTClient_freeMessage(&message);
MQTTClient_free(topicName);
return 1;
}
void connlost(void *context, char *cause)
{
printf("\nConnection lost\n");
printf(" cause: %s\n", cause);
}
int main(int argc, char* argv[])
{
MQTTClient client;
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
int rc;
int ch;
//创建MQTT订阅客户端,绑定服务端地址、以及当前订阅者的用户ID(每个客户端的用户ID务必保证唯一性)
MQTTClient_create(&client, ADDRESS, CLIENTID,
MQTTCLIENT_PERSISTENCE_NONE, NULL);
conn_opts.keepAliveInterval = 20;
conn_opts.cleansession = 1;
//绑定MQTT订阅者客户端的消息到达、消息发送的回调函数
MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered);
//MQTT订阅者客户端连接服务端
if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
{
printf("Failed to connect, return code %d\n", rc);
exit(EXIT_FAILURE);
}
//提交订阅者给服务端
printf("Subscribing to topic %s\nfor client %s using QoS%d\n\n"
"Press Q<Enter> to quit\n\n", TOPIC, CLIENTID, QOS);
MQTTClient_subscribe(client, TOPIC, QOS);
do
{
ch = getchar();
} while(ch!='Q' && ch != 'q');
MQTTClient_disconnect(client, 10000);
MQTTClient_destroy(&client);
return rc;
}
makfile:
arm-linux-gnueabi-gcc MQTTClient_publish.c -o MQTTClient_publish -L./linux_mqtt_c -lpaho-mqtt3c -lpthread
arm-linux-gnueabi-gcc MQTTClient_subscribe.c -o MQTTClient_subscribe -L./linux_mqtt_c -lpaho-mqtt3c -lpthread
方案验证结果:由于发布者和订阅者的消息是同步的,以下消耗时间均指发布者和订阅者的时间;
验证方案1 | ||
进程 | 发送次数 | 发送消耗时间(us) |
发布者进程1 | 100 | 10998389 |
发布者进程1 | 200 | 21997781 |
发布者进程1 | 500 | 55018353 |
结论:同一个进程发送100、200、500次所耗时间,平均每次发送时间为100ms;
验证方案2 | ||
进程 | 发送次数 | 发送消耗时间(us) |
发布者进程1 | 100 | 10997878 |
发布者进程2 | 100 | 10999326 |
发布者进程1 | 200 | 22017567 |
发布者进程2 | 200 | 21997405 |
发布者进程1 | 500 | 54998293 |
发布者进程2 | 500 | 55007166 |
结论:不同进程同时发送100、200、500次所耗时间,平均每次发送时间为100ms;
总结:综合方案1和方案2,多个进程与单个进程的负载通信所耗时间相同,所以单个进程或多个进程对MQTT服务器的负载没有影响,但每次发布一次负载消息所耗时间为100ms,系统库无法用这种通信机制满足;