發佈者代碼:
#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,系統庫無法用這種通信機制滿足;