commit
e3c93b3068
|
@ -0,0 +1,47 @@
|
|||
Contributing to Paho
|
||||
====================
|
||||
|
||||
Thanks for your interest in this project.
|
||||
|
||||
Project description:
|
||||
--------------------
|
||||
|
||||
The Paho project has been created to provide scalable open-source implementations of open and standard messaging protocols aimed at new, existing, and emerging applications for Machine-to-Machine (M2M) and Internet of Things (IoT).
|
||||
Paho reflects the inherent physical and cost constraints of device connectivity. Its objectives include effective levels of decoupling between devices and applications, designed to keep markets open and encourage the rapid growth of scalable Web and Enterprise middleware and applications. Paho is being kicked off with MQTT publish/subscribe client implementations for use on embedded platforms, along with corresponding server support as determined by the community.
|
||||
|
||||
- https://projects.eclipse.org/projects/technology.paho
|
||||
|
||||
Developer resources:
|
||||
--------------------
|
||||
|
||||
Information regarding source code management, builds, coding standards, and more.
|
||||
|
||||
- https://projects.eclipse.org/projects/technology.paho/developer
|
||||
|
||||
Contributor License Agreement:
|
||||
------------------------------
|
||||
|
||||
Before your contribution can be accepted by the project, you need to create and electronically sign the Eclipse Foundation Contributor License Agreement (CLA).
|
||||
|
||||
- http://www.eclipse.org/legal/CLA.php
|
||||
|
||||
Contact:
|
||||
--------
|
||||
|
||||
Contact the project developers via the project's "dev" list.
|
||||
|
||||
- https://dev.eclipse.org/mailman/listinfo/paho-dev
|
||||
|
||||
Search for bugs:
|
||||
----------------
|
||||
|
||||
This project uses Bugzilla to track ongoing development and issues.
|
||||
|
||||
- https://bugs.eclipse.org/bugs/buglist.cgi?product=Paho
|
||||
|
||||
Create a new bug:
|
||||
-----------------
|
||||
|
||||
Be sure to search for existing bugs before you create another one. Remember that contributions are always welcome!
|
||||
|
||||
- https://bugs.eclipse.org/bugs/enter_bug.cgi?product=Paho
|
|
@ -0,0 +1,3 @@
|
|||
cp ../../src/MQTTClient.c .
|
||||
sed -e 's/""/"MQTTLinux.h"/g' ../../src/MQTTClient.h > MQTTClient.h
|
||||
gcc stdoutsub.c -I ../../src -I ../../src/linux -I ../../../MQTTPacket/src MQTTClient.c ../../src/linux/MQTTLinux.c ../../../MQTTPacket/src/MQTTFormat.c ../../../MQTTPacket/src/MQTTPacket.c ../../../MQTTPacket/src/MQTTDeserializePublish.c ../../../MQTTPacket/src/MQTTConnectClient.c ../../../MQTTPacket/src/MQTTSubscribeClient.c ../../../MQTTPacket/src/MQTTSerializePublish.c -o stdoutsub ../../../MQTTPacket/src/MQTTConnectServer.c ../../../MQTTPacket/src/MQTTSubscribeServer.c ../../../MQTTPacket/src/MQTTUnsubscribeServer.c ../../../MQTTPacket/src/MQTTUnsubscribeClient.c
|
|
@ -0,0 +1,253 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial contribution
|
||||
* Ian Craggs - change delimiter option from char to string
|
||||
* Al Stockdill-Mander - Version using the embedded C client
|
||||
*******************************************************************************/
|
||||
|
||||
/*
|
||||
|
||||
stdout subscriber
|
||||
|
||||
compulsory parameters:
|
||||
|
||||
topic to subscribe to
|
||||
|
||||
defaulted parameters:
|
||||
|
||||
--host localhost
|
||||
--port 1883
|
||||
--qos 2
|
||||
--delimiter \n
|
||||
--clientid stdout_subscriber
|
||||
|
||||
--userid none
|
||||
--password none
|
||||
|
||||
for example:
|
||||
|
||||
stdoutsub topic/of/interest --host iot.eclipse.org
|
||||
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "MQTTClient.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <memory.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
|
||||
volatile int toStop = 0;
|
||||
|
||||
|
||||
void usage()
|
||||
{
|
||||
printf("MQTT stdout subscriber\n");
|
||||
printf("Usage: stdoutsub topicname <options>, where options are:\n");
|
||||
printf(" --host <hostname> (default is localhost)\n");
|
||||
printf(" --port <port> (default is 1883)\n");
|
||||
printf(" --qos <qos> (default is 2)\n");
|
||||
printf(" --delimiter <delim> (default is \\n)\n");
|
||||
printf(" --clientid <clientid> (default is hostname+timestamp)\n");
|
||||
printf(" --username none\n");
|
||||
printf(" --password none\n");
|
||||
printf(" --showtopics <on or off> (default is on if the topic has a wildcard, else off)\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
|
||||
void cfinish(int sig)
|
||||
{
|
||||
signal(SIGINT, NULL);
|
||||
toStop = 1;
|
||||
}
|
||||
|
||||
|
||||
struct opts_struct
|
||||
{
|
||||
char* clientid;
|
||||
int nodelimiter;
|
||||
char* delimiter;
|
||||
enum QoS qos;
|
||||
char* username;
|
||||
char* password;
|
||||
char* host;
|
||||
int port;
|
||||
int showtopics;
|
||||
} opts =
|
||||
{
|
||||
(char*)"stdout-subscriber", 0, (char*)"\n", QOS2, NULL, NULL, (char*)"localhost", 1883, 0
|
||||
};
|
||||
|
||||
|
||||
void getopts(int argc, char** argv)
|
||||
{
|
||||
int count = 2;
|
||||
|
||||
while (count < argc)
|
||||
{
|
||||
if (strcmp(argv[count], "--qos") == 0)
|
||||
{
|
||||
if (++count < argc)
|
||||
{
|
||||
if (strcmp(argv[count], "0") == 0)
|
||||
opts.qos = QOS0;
|
||||
else if (strcmp(argv[count], "1") == 0)
|
||||
opts.qos = QOS1;
|
||||
else if (strcmp(argv[count], "2") == 0)
|
||||
opts.qos = QOS2;
|
||||
else
|
||||
usage();
|
||||
}
|
||||
else
|
||||
usage();
|
||||
}
|
||||
else if (strcmp(argv[count], "--host") == 0)
|
||||
{
|
||||
if (++count < argc)
|
||||
opts.host = argv[count];
|
||||
else
|
||||
usage();
|
||||
}
|
||||
else if (strcmp(argv[count], "--port") == 0)
|
||||
{
|
||||
if (++count < argc)
|
||||
opts.port = atoi(argv[count]);
|
||||
else
|
||||
usage();
|
||||
}
|
||||
else if (strcmp(argv[count], "--clientid") == 0)
|
||||
{
|
||||
if (++count < argc)
|
||||
opts.clientid = argv[count];
|
||||
else
|
||||
usage();
|
||||
}
|
||||
else if (strcmp(argv[count], "--username") == 0)
|
||||
{
|
||||
if (++count < argc)
|
||||
opts.username = argv[count];
|
||||
else
|
||||
usage();
|
||||
}
|
||||
else if (strcmp(argv[count], "--password") == 0)
|
||||
{
|
||||
if (++count < argc)
|
||||
opts.password = argv[count];
|
||||
else
|
||||
usage();
|
||||
}
|
||||
else if (strcmp(argv[count], "--delimiter") == 0)
|
||||
{
|
||||
if (++count < argc)
|
||||
opts.delimiter = argv[count];
|
||||
else
|
||||
opts.nodelimiter = 1;
|
||||
}
|
||||
else if (strcmp(argv[count], "--showtopics") == 0)
|
||||
{
|
||||
if (++count < argc)
|
||||
{
|
||||
if (strcmp(argv[count], "on") == 0)
|
||||
opts.showtopics = 1;
|
||||
else if (strcmp(argv[count], "off") == 0)
|
||||
opts.showtopics = 0;
|
||||
else
|
||||
usage();
|
||||
}
|
||||
else
|
||||
usage();
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void messageArrived(MessageData* md)
|
||||
{
|
||||
MQTTMessage* message = md->message;
|
||||
|
||||
if (opts.showtopics)
|
||||
printf("%.*s\t", md->topicName->lenstring.len, md->topicName->lenstring.data);
|
||||
if (opts.nodelimiter)
|
||||
printf("%.*s", (int)message->payloadlen, (char*)message->payload);
|
||||
else
|
||||
printf("%.*s%s", (int)message->payloadlen, (char*)message->payload, opts.delimiter);
|
||||
//fflush(stdout);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int rc = 0;
|
||||
unsigned char buf[100];
|
||||
unsigned char readbuf[100];
|
||||
|
||||
if (argc < 2)
|
||||
usage();
|
||||
|
||||
char* topic = argv[1];
|
||||
|
||||
if (strchr(topic, '#') || strchr(topic, '+'))
|
||||
opts.showtopics = 1;
|
||||
if (opts.showtopics)
|
||||
printf("topic is %s\n", topic);
|
||||
|
||||
getopts(argc, argv);
|
||||
|
||||
Network n;
|
||||
Client c;
|
||||
|
||||
signal(SIGINT, cfinish);
|
||||
signal(SIGTERM, cfinish);
|
||||
|
||||
NewNetwork(&n);
|
||||
ConnectNetwork(&n, opts.host, opts.port);
|
||||
MQTTClient(&c, &n, 1000, buf, 100, readbuf, 100);
|
||||
|
||||
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
|
||||
data.willFlag = 0;
|
||||
data.MQTTVersion = 3;
|
||||
data.clientID.cstring = opts.clientid;
|
||||
data.username.cstring = opts.username;
|
||||
data.password.cstring = opts.password;
|
||||
|
||||
data.keepAliveInterval = 10;
|
||||
data.cleansession = 1;
|
||||
printf("Connecting to %s %d\n", opts.host, opts.port);
|
||||
|
||||
rc = MQTTConnect(&c, &data);
|
||||
printf("Connected %d\n", rc);
|
||||
|
||||
printf("Subscribing to %s\n", topic);
|
||||
rc = MQTTSubscribe(&c, topic, opts.qos, messageArrived);
|
||||
printf("Subscribed %d\n", rc);
|
||||
|
||||
while (!toStop)
|
||||
{
|
||||
MQTTYield(&c, 1000);
|
||||
}
|
||||
|
||||
printf("Stopping\n");
|
||||
|
||||
MQTTDisconnect(&c);
|
||||
n.disconnect(&n);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,514 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Allan Stockdill-Mander/Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTClient.h"
|
||||
|
||||
void NewMessageData(MessageData* md, MQTTString* aTopicName, MQTTMessage* aMessgage) {
|
||||
md->topicName = aTopicName;
|
||||
md->message = aMessgage;
|
||||
}
|
||||
|
||||
|
||||
int getNextPacketId(Client *c) {
|
||||
return c->next_packetid = (c->next_packetid == MAX_PACKET_ID) ? 1 : c->next_packetid + 1;
|
||||
}
|
||||
|
||||
|
||||
int sendPacket(Client* c, int length, Timer* timer)
|
||||
{
|
||||
int rc = FAILURE,
|
||||
sent = 0;
|
||||
|
||||
while (sent < length && !expired(timer))
|
||||
{
|
||||
rc = c->ipstack->mqttwrite(c->ipstack, &c->buf[sent], length, left_ms(timer));
|
||||
if (rc < 0) // there was an error writing the data
|
||||
break;
|
||||
sent += rc;
|
||||
}
|
||||
if (sent == length)
|
||||
{
|
||||
countdown(&c->ping_timer, c->keepAliveInterval); // record the fact that we have successfully sent the packet
|
||||
rc = SUCCESS;
|
||||
}
|
||||
else
|
||||
rc = FAILURE;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void MQTTClient(Client* c, Network* network, unsigned int command_timeout_ms, unsigned char* buf, size_t buf_size, unsigned char* readbuf, size_t readbuf_size)
|
||||
{
|
||||
int i;
|
||||
c->ipstack = network;
|
||||
|
||||
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
|
||||
c->messageHandlers[i].topicFilter = 0;
|
||||
c->command_timeout_ms = command_timeout_ms;
|
||||
c->buf = buf;
|
||||
c->buf_size = buf_size;
|
||||
c->readbuf = readbuf;
|
||||
c->readbuf_size = readbuf_size;
|
||||
c->isconnected = 0;
|
||||
c->ping_outstanding = 0;
|
||||
c->defaultMessageHandler = NULL;
|
||||
InitTimer(&c->ping_timer);
|
||||
}
|
||||
|
||||
|
||||
int decodePacket(Client* c, int* value, int timeout)
|
||||
{
|
||||
unsigned char i;
|
||||
int multiplier = 1;
|
||||
int len = 0;
|
||||
const int MAX_NO_OF_REMAINING_LENGTH_BYTES = 4;
|
||||
|
||||
*value = 0;
|
||||
do
|
||||
{
|
||||
int rc = MQTTPACKET_READ_ERROR;
|
||||
|
||||
if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)
|
||||
{
|
||||
rc = MQTTPACKET_READ_ERROR; /* bad data */
|
||||
goto exit;
|
||||
}
|
||||
rc = c->ipstack->mqttread(c->ipstack, &i, 1, timeout);
|
||||
if (rc != 1)
|
||||
goto exit;
|
||||
*value += (i & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
} while ((i & 128) != 0);
|
||||
exit:
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
int readPacket(Client* c, Timer* timer)
|
||||
{
|
||||
int rc = FAILURE;
|
||||
MQTTHeader header = {0};
|
||||
int len = 0;
|
||||
int rem_len = 0;
|
||||
|
||||
/* 1. read the header byte. This has the packet type in it */
|
||||
if (c->ipstack->mqttread(c->ipstack, c->readbuf, 1, left_ms(timer)) != 1)
|
||||
goto exit;
|
||||
|
||||
len = 1;
|
||||
/* 2. read the remaining length. This is variable in itself */
|
||||
decodePacket(c, &rem_len, left_ms(timer));
|
||||
len += MQTTPacket_encode(c->readbuf + 1, rem_len); /* put the original remaining length back into the buffer */
|
||||
|
||||
/* 3. read the rest of the buffer using a callback to supply the rest of the data */
|
||||
if (rem_len > 0 && (c->ipstack->mqttread(c->ipstack, c->readbuf + len, rem_len, left_ms(timer)) != rem_len))
|
||||
goto exit;
|
||||
|
||||
header.byte = c->readbuf[0];
|
||||
rc = header.bits.type;
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
// assume topic filter and name is in correct format
|
||||
// # can only be at end
|
||||
// + and # can only be next to separator
|
||||
char isTopicMatched(char* topicFilter, MQTTString* topicName)
|
||||
{
|
||||
char* curf = topicFilter;
|
||||
char* curn = topicName->lenstring.data;
|
||||
char* curn_end = curn + topicName->lenstring.len;
|
||||
|
||||
while (*curf && curn < curn_end)
|
||||
{
|
||||
if (*curn == '/' && *curf != '/')
|
||||
break;
|
||||
if (*curf != '+' && *curf != '#' && *curf != *curn)
|
||||
break;
|
||||
if (*curf == '+')
|
||||
{ // skip until we meet the next separator, or end of string
|
||||
char* nextpos = curn + 1;
|
||||
while (nextpos < curn_end && *nextpos != '/')
|
||||
nextpos = ++curn + 1;
|
||||
}
|
||||
else if (*curf == '#')
|
||||
curn = curn_end - 1; // skip until end of string
|
||||
curf++;
|
||||
curn++;
|
||||
};
|
||||
|
||||
return (curn == curn_end) && (*curf == '\0');
|
||||
}
|
||||
|
||||
|
||||
int deliverMessage(Client* c, MQTTString* topicName, MQTTMessage* message)
|
||||
{
|
||||
int i;
|
||||
int rc = FAILURE;
|
||||
|
||||
// we have to find the right message handler - indexed by topic
|
||||
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
|
||||
{
|
||||
if (c->messageHandlers[i].topicFilter != 0 && (MQTTPacket_equals(topicName, (char*)c->messageHandlers[i].topicFilter) ||
|
||||
isTopicMatched((char*)c->messageHandlers[i].topicFilter, topicName)))
|
||||
{
|
||||
if (c->messageHandlers[i].fp != NULL)
|
||||
{
|
||||
MessageData md;
|
||||
NewMessageData(&md, topicName, message);
|
||||
c->messageHandlers[i].fp(&md);
|
||||
rc = SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == FAILURE && c->defaultMessageHandler != NULL)
|
||||
{
|
||||
MessageData md;
|
||||
NewMessageData(&md, topicName, message);
|
||||
c->defaultMessageHandler(&md);
|
||||
rc = SUCCESS;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int keepalive(Client* c)
|
||||
{
|
||||
int rc = FAILURE;
|
||||
|
||||
if (c->keepAliveInterval == 0)
|
||||
{
|
||||
rc = SUCCESS;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (expired(&c->ping_timer))
|
||||
{
|
||||
if (!c->ping_outstanding)
|
||||
{
|
||||
Timer timer;
|
||||
InitTimer(&timer);
|
||||
countdown_ms(&timer, 1000);
|
||||
int len = MQTTSerialize_pingreq(c->buf, c->buf_size);
|
||||
if (len > 0 && (rc = sendPacket(c, len, &timer)) == SUCCESS) // send the ping packet
|
||||
c->ping_outstanding = 1;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int cycle(Client* c, Timer* timer)
|
||||
{
|
||||
// read the socket, see what work is due
|
||||
unsigned short packet_type = readPacket(c, timer);
|
||||
|
||||
int len = 0,
|
||||
rc = SUCCESS;
|
||||
|
||||
switch (packet_type)
|
||||
{
|
||||
case CONNACK:
|
||||
case PUBACK:
|
||||
case SUBACK:
|
||||
break;
|
||||
case PUBLISH:
|
||||
{
|
||||
MQTTString topicName;
|
||||
MQTTMessage msg;
|
||||
if (MQTTDeserialize_publish((unsigned char*)&msg.dup, (int*)&msg.qos, (unsigned char*)&msg.retained, (unsigned short*)&msg.id, &topicName,
|
||||
(unsigned char**)&msg.payload, (int*)&msg.payloadlen, c->readbuf, c->readbuf_size) != 1)
|
||||
goto exit;
|
||||
deliverMessage(c, &topicName, &msg);
|
||||
if (msg.qos != QOS0)
|
||||
{
|
||||
if (msg.qos == QOS1)
|
||||
len = MQTTSerialize_ack(c->buf, c->buf_size, PUBACK, 0, msg.id);
|
||||
else if (msg.qos == QOS2)
|
||||
len = MQTTSerialize_ack(c->buf, c->buf_size, PUBREC, 0, msg.id);
|
||||
if (len <= 0)
|
||||
rc = FAILURE;
|
||||
else
|
||||
rc = sendPacket(c, len, timer);
|
||||
if (rc == FAILURE)
|
||||
goto exit; // there was a problem
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PUBREC:
|
||||
{
|
||||
unsigned short mypacketid;
|
||||
unsigned char dup, type;
|
||||
if (MQTTDeserialize_ack(&type, &dup, &mypacketid, c->readbuf, c->readbuf_size) != 1)
|
||||
rc = FAILURE;
|
||||
else if ((len = MQTTSerialize_ack(c->buf, c->buf_size, PUBREL, 0, mypacketid)) <= 0)
|
||||
rc = FAILURE;
|
||||
else if ((rc = sendPacket(c, len, timer)) != SUCCESS) // send the PUBREL packet
|
||||
rc = FAILURE; // there was a problem
|
||||
if (rc == FAILURE)
|
||||
goto exit; // there was a problem
|
||||
break;
|
||||
}
|
||||
case PUBCOMP:
|
||||
break;
|
||||
case PINGRESP:
|
||||
c->ping_outstanding = 0;
|
||||
break;
|
||||
}
|
||||
keepalive(c);
|
||||
exit:
|
||||
if (rc == SUCCESS)
|
||||
rc = packet_type;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTYield(Client* c, int timeout_ms)
|
||||
{
|
||||
int rc = SUCCESS;
|
||||
Timer timer;
|
||||
|
||||
InitTimer(&timer);
|
||||
countdown_ms(&timer, timeout_ms);
|
||||
while (!expired(&timer))
|
||||
{
|
||||
if (cycle(c, &timer) == FAILURE)
|
||||
{
|
||||
rc = FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
// only used in single-threaded mode where one command at a time is in process
|
||||
int waitfor(Client* c, int packet_type, Timer* timer)
|
||||
{
|
||||
int rc = FAILURE;
|
||||
|
||||
do
|
||||
{
|
||||
if (expired(timer))
|
||||
break; // we timed out
|
||||
}
|
||||
while ((rc = cycle(c, timer)) != packet_type);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTConnect(Client* c, MQTTPacket_connectData* options)
|
||||
{
|
||||
Timer connect_timer;
|
||||
int rc = FAILURE;
|
||||
MQTTPacket_connectData default_options = MQTTPacket_connectData_initializer;
|
||||
int len = 0;
|
||||
|
||||
InitTimer(&connect_timer);
|
||||
countdown_ms(&connect_timer, c->command_timeout_ms);
|
||||
|
||||
if (c->isconnected) // don't send connect packet again if we are already connected
|
||||
goto exit;
|
||||
|
||||
if (options == 0)
|
||||
options = &default_options; // set default options if none were supplied
|
||||
|
||||
c->keepAliveInterval = options->keepAliveInterval;
|
||||
countdown(&c->ping_timer, c->keepAliveInterval);
|
||||
if ((len = MQTTSerialize_connect(c->buf, c->buf_size, options)) <= 0)
|
||||
goto exit;
|
||||
if ((rc = sendPacket(c, len, &connect_timer)) != SUCCESS) // send the connect packet
|
||||
goto exit; // there was a problem
|
||||
|
||||
// this will be a blocking call, wait for the connack
|
||||
if (waitfor(c, CONNACK, &connect_timer) == CONNACK)
|
||||
{
|
||||
unsigned char connack_rc = 255;
|
||||
char sessionPresent = 0;
|
||||
if (MQTTDeserialize_connack((unsigned char*)&sessionPresent, &connack_rc, c->readbuf, c->readbuf_size) == 1)
|
||||
rc = connack_rc;
|
||||
else
|
||||
rc = FAILURE;
|
||||
}
|
||||
else
|
||||
rc = FAILURE;
|
||||
|
||||
exit:
|
||||
if (rc == SUCCESS)
|
||||
c->isconnected = 1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTSubscribe(Client* c, const char* topicFilter, enum QoS qos, messageHandler messageHandler)
|
||||
{
|
||||
int rc = FAILURE;
|
||||
Timer timer;
|
||||
int len = 0;
|
||||
MQTTString topic = MQTTString_initializer;
|
||||
topic.cstring = (char *)topicFilter;
|
||||
|
||||
InitTimer(&timer);
|
||||
countdown_ms(&timer, c->command_timeout_ms);
|
||||
|
||||
if (!c->isconnected)
|
||||
goto exit;
|
||||
|
||||
len = MQTTSerialize_subscribe(c->buf, c->buf_size, 0, getNextPacketId(c), 1, &topic, (int*)&qos);
|
||||
if (len <= 0)
|
||||
goto exit;
|
||||
if ((rc = sendPacket(c, len, &timer)) != SUCCESS) // send the subscribe packet
|
||||
goto exit; // there was a problem
|
||||
|
||||
if (waitfor(c, SUBACK, &timer) == SUBACK) // wait for suback
|
||||
{
|
||||
int count = 0, grantedQoS = -1;
|
||||
unsigned short mypacketid;
|
||||
if (MQTTDeserialize_suback(&mypacketid, 1, &count, &grantedQoS, c->readbuf, c->readbuf_size) == 1)
|
||||
rc = grantedQoS; // 0, 1, 2 or 0x80
|
||||
if (rc != 0x80)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
|
||||
{
|
||||
if (c->messageHandlers[i].topicFilter == 0)
|
||||
{
|
||||
c->messageHandlers[i].topicFilter = topicFilter;
|
||||
c->messageHandlers[i].fp = messageHandler;
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
rc = FAILURE;
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTUnsubscribe(Client* c, const char* topicFilter)
|
||||
{
|
||||
int rc = FAILURE;
|
||||
Timer timer;
|
||||
MQTTString topic = MQTTString_initializer;
|
||||
topic.cstring = (char *)topicFilter;
|
||||
int len = 0;
|
||||
|
||||
InitTimer(&timer);
|
||||
countdown_ms(&timer, c->command_timeout_ms);
|
||||
|
||||
if (!c->isconnected)
|
||||
goto exit;
|
||||
|
||||
if ((len = MQTTSerialize_unsubscribe(c->buf, c->buf_size, 0, getNextPacketId(c), 1, &topic)) <= 0)
|
||||
goto exit;
|
||||
if ((rc = sendPacket(c, len, &timer)) != SUCCESS) // send the subscribe packet
|
||||
goto exit; // there was a problem
|
||||
|
||||
if (waitfor(c, UNSUBACK, &timer) == UNSUBACK)
|
||||
{
|
||||
unsigned short mypacketid; // should be the same as the packetid above
|
||||
if (MQTTDeserialize_unsuback(&mypacketid, c->readbuf, c->readbuf_size) == 1)
|
||||
rc = 0;
|
||||
}
|
||||
else
|
||||
rc = FAILURE;
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTPublish(Client* c, const char* topicName, MQTTMessage* message)
|
||||
{
|
||||
int rc = FAILURE;
|
||||
Timer timer;
|
||||
MQTTString topic = MQTTString_initializer;
|
||||
topic.cstring = (char *)topicName;
|
||||
int len = 0;
|
||||
|
||||
InitTimer(&timer);
|
||||
countdown_ms(&timer, c->command_timeout_ms);
|
||||
|
||||
if (!c->isconnected)
|
||||
goto exit;
|
||||
|
||||
if (message->qos == QOS1 || message->qos == QOS2)
|
||||
message->id = getNextPacketId(c);
|
||||
|
||||
len = MQTTSerialize_publish(c->buf, c->buf_size, 0, message->qos, message->retained, message->id,
|
||||
topic, (unsigned char*)message->payload, message->payloadlen);
|
||||
if (len <= 0)
|
||||
goto exit;
|
||||
if ((rc = sendPacket(c, len, &timer)) != SUCCESS) // send the subscribe packet
|
||||
goto exit; // there was a problem
|
||||
|
||||
if (message->qos == QOS1)
|
||||
{
|
||||
if (waitfor(c, PUBACK, &timer) == PUBACK)
|
||||
{
|
||||
unsigned short mypacketid;
|
||||
unsigned char dup, type;
|
||||
if (MQTTDeserialize_ack(&type, &dup, &mypacketid, c->readbuf, c->readbuf_size) != 1)
|
||||
rc = FAILURE;
|
||||
}
|
||||
else
|
||||
rc = FAILURE;
|
||||
}
|
||||
else if (message->qos == QOS2)
|
||||
{
|
||||
if (waitfor(c, PUBCOMP, &timer) == PUBCOMP)
|
||||
{
|
||||
unsigned short mypacketid;
|
||||
unsigned char dup, type;
|
||||
if (MQTTDeserialize_ack(&type, &dup, &mypacketid, c->readbuf, c->readbuf_size) != 1)
|
||||
rc = FAILURE;
|
||||
}
|
||||
else
|
||||
rc = FAILURE;
|
||||
}
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTDisconnect(Client* c)
|
||||
{
|
||||
int rc = FAILURE;
|
||||
Timer timer; // we might wait for incomplete incoming publishes to complete
|
||||
int len = MQTTSerialize_disconnect(c->buf, c->buf_size);
|
||||
|
||||
InitTimer(&timer);
|
||||
countdown_ms(&timer, c->command_timeout_ms);
|
||||
|
||||
if (len > 0)
|
||||
rc = sendPacket(c, len, &timer); // send the disconnect packet
|
||||
|
||||
c->isconnected = 0;
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Allan Stockdill-Mander/Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef __MQTT_CLIENT_C_
|
||||
#define __MQTT_CLIENT_C_
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "stdio.h"
|
||||
#include "" //Platform specific implementation header file
|
||||
|
||||
#define MAX_PACKET_ID 65535
|
||||
#define MAX_MESSAGE_HANDLERS 5
|
||||
|
||||
enum QoS { QOS0, QOS1, QOS2 };
|
||||
|
||||
// all failure return codes must be negative
|
||||
enum returnCode { BUFFER_OVERFLOW = -2, FAILURE = -1, SUCCESS = 0 };
|
||||
|
||||
void NewTimer(Timer*);
|
||||
|
||||
typedef struct MQTTMessage MQTTMessage;
|
||||
|
||||
typedef struct MessageData MessageData;
|
||||
|
||||
struct MQTTMessage
|
||||
{
|
||||
enum QoS qos;
|
||||
char retained;
|
||||
char dup;
|
||||
unsigned short id;
|
||||
void *payload;
|
||||
size_t payloadlen;
|
||||
};
|
||||
|
||||
struct MessageData
|
||||
{
|
||||
MQTTMessage* message;
|
||||
MQTTString* topicName;
|
||||
};
|
||||
|
||||
typedef void (*messageHandler)(MessageData*);
|
||||
|
||||
typedef struct Client Client;
|
||||
|
||||
int MQTTConnect (Client*, MQTTPacket_connectData*);
|
||||
int MQTTPublish (Client*, const char*, MQTTMessage*);
|
||||
int MQTTSubscribe (Client*, const char*, enum QoS, messageHandler);
|
||||
int MQTTUnsubscribe (Client*, const char*);
|
||||
int MQTTDisconnect (Client*);
|
||||
int MQTTYield (Client*, int);
|
||||
|
||||
void setDefaultMessageHandler(Client*, messageHandler);
|
||||
|
||||
void MQTTClient(Client*, Network*, unsigned int, unsigned char*, size_t, unsigned char*, size_t);
|
||||
|
||||
struct Client {
|
||||
unsigned int next_packetid;
|
||||
unsigned int command_timeout_ms;
|
||||
size_t buf_size, readbuf_size;
|
||||
unsigned char *buf;
|
||||
unsigned char *readbuf;
|
||||
unsigned int keepAliveInterval;
|
||||
char ping_outstanding;
|
||||
int isconnected;
|
||||
|
||||
struct MessageHandlers
|
||||
{
|
||||
const char* topicFilter;
|
||||
void (*fp) (MessageData*);
|
||||
} messageHandlers[MAX_MESSAGE_HANDLERS]; // Message handlers are indexed by subscription topic
|
||||
|
||||
void (*defaultMessageHandler) (MessageData*);
|
||||
|
||||
Network* ipstack;
|
||||
Timer ping_timer;
|
||||
};
|
||||
|
||||
#define DefaultClient {0, 0, 0, 0, NULL, NULL, 0, 0, 0}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,196 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Allan Stockdill-Mander - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTCC3200.h"
|
||||
|
||||
unsigned long MilliTimer;
|
||||
|
||||
void SysTickIntHandler(void) {
|
||||
MilliTimer++;
|
||||
}
|
||||
|
||||
char expired(Timer* timer) {
|
||||
long left = timer->end_time - MilliTimer;
|
||||
return (left < 0);
|
||||
}
|
||||
|
||||
|
||||
void countdown_ms(Timer* timer, unsigned int timeout) {
|
||||
timer->end_time = MilliTimer + timeout;
|
||||
}
|
||||
|
||||
|
||||
void countdown(Timer* timer, unsigned int timeout) {
|
||||
timer->end_time = MilliTimer + (timeout * 1000);
|
||||
}
|
||||
|
||||
|
||||
int left_ms(Timer* timer) {
|
||||
long left = timer->end_time - MilliTimer;
|
||||
return (left < 0) ? 0 : left;
|
||||
}
|
||||
|
||||
|
||||
void InitTimer(Timer* timer) {
|
||||
timer->end_time = 0;
|
||||
}
|
||||
|
||||
|
||||
int cc3200_read(Network* n, unsigned char* buffer, int len, int timeout_ms) {
|
||||
SlTimeval_t timeVal;
|
||||
SlFdSet_t fdset;
|
||||
int rc = 0;
|
||||
int recvLen = 0;
|
||||
|
||||
SL_FD_ZERO(&fdset);
|
||||
SL_FD_SET(n->my_socket, &fdset);
|
||||
|
||||
timeVal.tv_sec = 0;
|
||||
timeVal.tv_usec = timeout_ms * 1000;
|
||||
if (sl_Select(n->my_socket + 1, &fdset, NULL, NULL, &timeVal) == 1) {
|
||||
do {
|
||||
rc = sl_Recv(n->my_socket, buffer + recvLen, len - recvLen, 0);
|
||||
recvLen += rc;
|
||||
} while(recvLen < len);
|
||||
}
|
||||
return recvLen;
|
||||
}
|
||||
|
||||
|
||||
int cc3200_write(Network* n, unsigned char* buffer, int len, int timeout_ms) {
|
||||
SlTimeval_t timeVal;
|
||||
SlFdSet_t fdset;
|
||||
int rc = 0;
|
||||
int readySock;
|
||||
|
||||
SL_FD_ZERO(&fdset);
|
||||
SL_FD_SET(n->my_socket, &fdset);
|
||||
|
||||
timeVal.tv_sec = 0;
|
||||
timeVal.tv_usec = timeout_ms * 1000;
|
||||
do {
|
||||
readySock = sl_Select(n->my_socket + 1, NULL, &fdset, NULL, &timeVal);
|
||||
} while(readySock != 1);
|
||||
rc = sl_Send(n->my_socket, buffer, len, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void cc3200_disconnect(Network* n) {
|
||||
sl_Close(n->my_socket);
|
||||
}
|
||||
|
||||
|
||||
void NewNetwork(Network* n) {
|
||||
n->my_socket = 0;
|
||||
n->mqttread = cc3200_read;
|
||||
n->mqttwrite = cc3200_write;
|
||||
n->disconnect = cc3200_disconnect;
|
||||
}
|
||||
|
||||
int TLSConnectNetwork(Network *n, char* addr, int port, SlSockSecureFiles_t* certificates, unsigned char sec_method, unsigned int cipher, char server_verify) {
|
||||
SlSockAddrIn_t sAddr;
|
||||
int addrSize;
|
||||
int retVal;
|
||||
unsigned long ipAddress;
|
||||
|
||||
retVal = sl_NetAppDnsGetHostByName(addr, strlen(addr), &ipAddress, AF_INET);
|
||||
if (retVal < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
sAddr.sin_family = AF_INET;
|
||||
sAddr.sin_port = sl_Htons((unsigned short)port);
|
||||
sAddr.sin_addr.s_addr = sl_Htonl(ipAddress);
|
||||
|
||||
addrSize = sizeof(SlSockAddrIn_t);
|
||||
|
||||
n->my_socket = sl_Socket(SL_AF_INET,SL_SOCK_STREAM, SL_SEC_SOCKET);
|
||||
if (n->my_socket < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SlSockSecureMethod method;
|
||||
method.secureMethod = sec_method;
|
||||
retVal = sl_SetSockOpt(n->my_socket, SL_SOL_SOCKET, SL_SO_SECMETHOD, &method, sizeof(method));
|
||||
if (retVal < 0) {
|
||||
return retVal;
|
||||
}
|
||||
|
||||
SlSockSecureMask mask;
|
||||
mask.secureMask = cipher;
|
||||
retVal = sl_SetSockOpt(n->my_socket, SL_SOL_SOCKET, SL_SO_SECURE_MASK, &mask, sizeof(mask));
|
||||
if (retVal < 0) {
|
||||
return retVal;
|
||||
}
|
||||
|
||||
if (certificates != NULL) {
|
||||
retVal = sl_SetSockOpt(n->my_socket, SL_SOL_SOCKET, SL_SO_SECURE_FILES, certificates->secureFiles, sizeof(SlSockSecureFiles_t));
|
||||
if(retVal < 0)
|
||||
{
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
||||
retVal = sl_Connect(n->my_socket, ( SlSockAddr_t *)&sAddr, addrSize);
|
||||
if( retVal < 0 ) {
|
||||
if (server_verify || retVal != -453) {
|
||||
sl_Close(n->my_socket);
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
||||
SysTickIntRegister(SysTickIntHandler);
|
||||
SysTickPeriodSet(80000);
|
||||
SysTickEnable();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int ConnectNetwork(Network* n, char* addr, int port)
|
||||
{
|
||||
SlSockAddrIn_t sAddr;
|
||||
int addrSize;
|
||||
int retVal;
|
||||
unsigned long ipAddress;
|
||||
|
||||
sl_NetAppDnsGetHostByName(addr, strlen(addr), &ipAddress, AF_INET);
|
||||
|
||||
sAddr.sin_family = AF_INET;
|
||||
sAddr.sin_port = sl_Htons((unsigned short)port);
|
||||
sAddr.sin_addr.s_addr = sl_Htonl(ipAddress);
|
||||
|
||||
addrSize = sizeof(SlSockAddrIn_t);
|
||||
|
||||
n->my_socket = sl_Socket(SL_AF_INET,SL_SOCK_STREAM, 0);
|
||||
if( n->my_socket < 0 ) {
|
||||
// error
|
||||
return -1;
|
||||
}
|
||||
|
||||
retVal = sl_Connect(n->my_socket, ( SlSockAddr_t *)&sAddr, addrSize);
|
||||
if( retVal < 0 ) {
|
||||
// error
|
||||
sl_Close(n->my_socket);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
SysTickIntRegister(SysTickIntHandler);
|
||||
SysTickPeriodSet(80000);
|
||||
SysTickEnable();
|
||||
|
||||
return retVal;
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Allan Stockdill-Mander - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef __MQTT_CC3200_
|
||||
#define __MQTT_CC3200_
|
||||
|
||||
#include "simplelink.h"
|
||||
#include "netapp.h"
|
||||
#include "socket.h"
|
||||
#include "hw_types.h"
|
||||
#include "systick.h"
|
||||
|
||||
typedef struct Timer Timer;
|
||||
|
||||
struct Timer {
|
||||
unsigned long systick_period;
|
||||
unsigned long end_time;
|
||||
};
|
||||
|
||||
typedef struct Network Network;
|
||||
|
||||
struct Network
|
||||
{
|
||||
int my_socket;
|
||||
int (*mqttread) (Network*, unsigned char*, int, int);
|
||||
int (*mqttwrite) (Network*, unsigned char*, int, int);
|
||||
void (*disconnect) (Network*);
|
||||
};
|
||||
|
||||
char expired(Timer*);
|
||||
void countdown_ms(Timer*, unsigned int);
|
||||
void countdown(Timer*, unsigned int);
|
||||
int left_ms(Timer*);
|
||||
|
||||
void InitTimer(Timer*);
|
||||
|
||||
int cc3200_read(Network*, unsigned char*, int, int);
|
||||
int cc3200_write(Network*, unsigned char*, int, int);
|
||||
void cc3200_disconnect(Network*);
|
||||
void NewNetwork(Network*);
|
||||
|
||||
int ConnectNetwork(Network*, char*, int);
|
||||
int TLSConnectNetwork(Network*, char*, int, SlSockSecureFiles_t*, unsigned char, unsigned int, char);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,167 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Allan Stockdill-Mander - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTLinux.h"
|
||||
|
||||
char expired(Timer* timer)
|
||||
{
|
||||
struct timeval now, res;
|
||||
gettimeofday(&now, NULL);
|
||||
timersub(&timer->end_time, &now, &res);
|
||||
return res.tv_sec < 0 || (res.tv_sec == 0 && res.tv_usec <= 0);
|
||||
}
|
||||
|
||||
|
||||
void countdown_ms(Timer* timer, unsigned int timeout)
|
||||
{
|
||||
struct timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
struct timeval interval = {timeout / 1000, (timeout % 1000) * 1000};
|
||||
timeradd(&now, &interval, &timer->end_time);
|
||||
}
|
||||
|
||||
|
||||
void countdown(Timer* timer, unsigned int timeout)
|
||||
{
|
||||
struct timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
struct timeval interval = {timeout, 0};
|
||||
timeradd(&now, &interval, &timer->end_time);
|
||||
}
|
||||
|
||||
|
||||
int left_ms(Timer* timer)
|
||||
{
|
||||
struct timeval now, res;
|
||||
gettimeofday(&now, NULL);
|
||||
timersub(&timer->end_time, &now, &res);
|
||||
//printf("left %d ms\n", (res.tv_sec < 0) ? 0 : res.tv_sec * 1000 + res.tv_usec / 1000);
|
||||
return (res.tv_sec < 0) ? 0 : res.tv_sec * 1000 + res.tv_usec / 1000;
|
||||
}
|
||||
|
||||
|
||||
void InitTimer(Timer* timer)
|
||||
{
|
||||
timer->end_time = (struct timeval){0, 0};
|
||||
}
|
||||
|
||||
|
||||
int linux_read(Network* n, unsigned char* buffer, int len, int timeout_ms)
|
||||
{
|
||||
struct timeval interval = {timeout_ms / 1000, (timeout_ms % 1000) * 1000};
|
||||
if (interval.tv_sec < 0 || (interval.tv_sec == 0 && interval.tv_usec <= 0))
|
||||
{
|
||||
interval.tv_sec = 0;
|
||||
interval.tv_usec = 100;
|
||||
}
|
||||
|
||||
setsockopt(n->my_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&interval, sizeof(struct timeval));
|
||||
|
||||
int bytes = 0;
|
||||
while (bytes < len)
|
||||
{
|
||||
int rc = recv(n->my_socket, &buffer[bytes], (size_t)(len - bytes), 0);
|
||||
if (rc == -1)
|
||||
{
|
||||
if (errno != ENOTCONN && errno != ECONNRESET)
|
||||
{
|
||||
bytes = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
bytes += rc;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
int linux_write(Network* n, unsigned char* buffer, int len, int timeout_ms)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
tv.tv_sec = 0; /* 30 Secs Timeout */
|
||||
tv.tv_usec = timeout_ms * 1000; // Not init'ing this can cause strange errors
|
||||
|
||||
setsockopt(n->my_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval));
|
||||
int rc = write(n->my_socket, buffer, len);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void linux_disconnect(Network* n)
|
||||
{
|
||||
close(n->my_socket);
|
||||
}
|
||||
|
||||
|
||||
void NewNetwork(Network* n)
|
||||
{
|
||||
n->my_socket = 0;
|
||||
n->mqttread = linux_read;
|
||||
n->mqttwrite = linux_write;
|
||||
n->disconnect = linux_disconnect;
|
||||
}
|
||||
|
||||
|
||||
int ConnectNetwork(Network* n, char* addr, int port)
|
||||
{
|
||||
int type = SOCK_STREAM;
|
||||
struct sockaddr_in address;
|
||||
int rc = -1;
|
||||
sa_family_t family = AF_INET;
|
||||
struct addrinfo *result = NULL;
|
||||
struct addrinfo hints = {0, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL};
|
||||
|
||||
if ((rc = getaddrinfo(addr, NULL, &hints, &result)) == 0)
|
||||
{
|
||||
struct addrinfo* res = result;
|
||||
|
||||
/* prefer ip4 addresses */
|
||||
while (res)
|
||||
{
|
||||
if (res->ai_family == AF_INET)
|
||||
{
|
||||
result = res;
|
||||
break;
|
||||
}
|
||||
res = res->ai_next;
|
||||
}
|
||||
|
||||
if (result->ai_family == AF_INET)
|
||||
{
|
||||
address.sin_port = htons(port);
|
||||
address.sin_family = family = AF_INET;
|
||||
address.sin_addr = ((struct sockaddr_in*)(result->ai_addr))->sin_addr;
|
||||
}
|
||||
else
|
||||
rc = -1;
|
||||
|
||||
freeaddrinfo(result);
|
||||
}
|
||||
|
||||
if (rc == 0)
|
||||
{
|
||||
n->my_socket = socket(family, type, 0);
|
||||
if (n->my_socket != -1)
|
||||
{
|
||||
int opt = 1;
|
||||
rc = connect(n->my_socket, (struct sockaddr*)&address, sizeof(address));
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Allan Stockdill-Mander - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef __MQTT_LINUX_
|
||||
#define __MQTT_LINUX_
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/select.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
typedef struct Timer Timer;
|
||||
|
||||
struct Timer {
|
||||
struct timeval end_time;
|
||||
};
|
||||
|
||||
typedef struct Network Network;
|
||||
|
||||
struct Network
|
||||
{
|
||||
int my_socket;
|
||||
int (*mqttread) (Network*, unsigned char*, int, int);
|
||||
int (*mqttwrite) (Network*, unsigned char*, int, int);
|
||||
void (*disconnect) (Network*);
|
||||
};
|
||||
|
||||
char expired(Timer*);
|
||||
void countdown_ms(Timer*, unsigned int);
|
||||
void countdown(Timer*, unsigned int);
|
||||
int left_ms(Timer*);
|
||||
|
||||
void InitTimer(Timer*);
|
||||
|
||||
int linux_read(Network*, unsigned char*, int, int);
|
||||
int linux_write(Network*, unsigned char*, int, int);
|
||||
void linux_disconnect(Network*);
|
||||
void NewNetwork(Network*);
|
||||
|
||||
int ConnectNetwork(Network*, char*, int);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,4 @@
|
|||
int main(int argc, char** argv)
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Sergio R. Caprile - clarifications and/or documentation extension
|
||||
*******************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "transport.h"
|
||||
|
||||
/* This is in order to get an asynchronous signal to stop the sample,
|
||||
as the code loops waiting for msgs on the subscribed topic.
|
||||
Your actual code will depend on your hw and approach*/
|
||||
#include <signal.h>
|
||||
|
||||
int toStop = 0;
|
||||
|
||||
void cfinish(int sig)
|
||||
{
|
||||
signal(SIGINT, NULL);
|
||||
toStop = 1;
|
||||
}
|
||||
|
||||
void stop_init(void)
|
||||
{
|
||||
signal(SIGINT, cfinish);
|
||||
signal(SIGTERM, cfinish);
|
||||
}
|
||||
/* */
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
|
||||
int rc = 0;
|
||||
int mysock = 0;
|
||||
unsigned char buf[200];
|
||||
int buflen = sizeof(buf);
|
||||
int msgid = 1;
|
||||
MQTTString topicString = MQTTString_initializer;
|
||||
int req_qos = 0;
|
||||
char* payload = "mypayload";
|
||||
int payloadlen = strlen(payload);
|
||||
int len = 0;
|
||||
char *host = "m2m.eclipse.org";
|
||||
int port = 1883;
|
||||
|
||||
stop_init();
|
||||
if (argc > 1)
|
||||
host = argv[1];
|
||||
|
||||
if (argc > 2)
|
||||
port = atoi(argv[2]);
|
||||
|
||||
mysock = transport_open(host, port);
|
||||
if(mysock < 0)
|
||||
return mysock;
|
||||
|
||||
printf("Sending to hostname %s port %d\n", host, port);
|
||||
|
||||
data.clientID.cstring = "me";
|
||||
data.keepAliveInterval = 20;
|
||||
data.cleansession = 1;
|
||||
data.username.cstring = "testuser";
|
||||
data.password.cstring = "testpassword";
|
||||
|
||||
len = MQTTSerialize_connect(buf, buflen, &data);
|
||||
rc = transport_sendPacketBuffer(mysock, buf, len);
|
||||
|
||||
/* wait for connack */
|
||||
if (MQTTPacket_read(buf, buflen, transport_getdata) == CONNACK)
|
||||
{
|
||||
unsigned char sessionPresent, connack_rc;
|
||||
|
||||
if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, buf, buflen) != 1 || connack_rc != 0)
|
||||
{
|
||||
printf("Unable to connect, return code %d\n", connack_rc);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
goto exit;
|
||||
|
||||
/* subscribe */
|
||||
topicString.cstring = "substopic";
|
||||
len = MQTTSerialize_subscribe(buf, buflen, 0, msgid, 1, &topicString, &req_qos);
|
||||
|
||||
rc = transport_sendPacketBuffer(mysock, buf, len);
|
||||
if (MQTTPacket_read(buf, buflen, transport_getdata) == SUBACK) /* wait for suback */
|
||||
{
|
||||
unsigned short submsgid;
|
||||
int subcount;
|
||||
int granted_qos;
|
||||
|
||||
rc = MQTTDeserialize_suback(&submsgid, 1, &subcount, &granted_qos, buf, buflen);
|
||||
if (granted_qos != 0)
|
||||
{
|
||||
printf("granted qos != 0, %d\n", granted_qos);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
goto exit;
|
||||
|
||||
/* loop getting msgs on subscribed topic */
|
||||
topicString.cstring = "pubtopic";
|
||||
while (!toStop)
|
||||
{
|
||||
/* transport_getdata() has a built-in 1 second timeout,
|
||||
your mileage will vary */
|
||||
if (MQTTPacket_read(buf, buflen, transport_getdata) == PUBLISH)
|
||||
{
|
||||
unsigned char dup;
|
||||
int qos;
|
||||
unsigned char retained;
|
||||
unsigned short msgid;
|
||||
int payloadlen_in;
|
||||
unsigned char* payload_in;
|
||||
int rc;
|
||||
MQTTString receivedTopic;
|
||||
|
||||
rc = MQTTDeserialize_publish(&dup, &qos, &retained, &msgid, &receivedTopic,
|
||||
&payload_in, &payloadlen_in, buf, buflen);
|
||||
printf("message arrived %.*s\n", payloadlen_in, payload_in);
|
||||
}
|
||||
|
||||
printf("publishing reading\n");
|
||||
len = MQTTSerialize_publish(buf, buflen, 0, 0, 0, 0, topicString, (unsigned char*)payload, payloadlen);
|
||||
rc = transport_sendPacketBuffer(mysock, buf, len);
|
||||
}
|
||||
|
||||
printf("disconnecting\n");
|
||||
len = MQTTSerialize_disconnect(buf, buflen);
|
||||
rc = transport_sendPacketBuffer(mysock, buf, len);
|
||||
|
||||
exit:
|
||||
transport_close(mysock);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Sergio R. Caprile - clarifications and/or documentation extension
|
||||
*******************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "transport.h"
|
||||
|
||||
/* This is in order to get an asynchronous signal to stop the sample,
|
||||
as the code loops waiting for msgs on the subscribed topic.
|
||||
Your actual code will depend on your hw and approach*/
|
||||
#include <signal.h>
|
||||
|
||||
int toStop = 0;
|
||||
|
||||
void cfinish(int sig)
|
||||
{
|
||||
signal(SIGINT, NULL);
|
||||
toStop = 1;
|
||||
}
|
||||
|
||||
void stop_init(void)
|
||||
{
|
||||
signal(SIGINT, cfinish);
|
||||
signal(SIGTERM, cfinish);
|
||||
}
|
||||
/* */
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
|
||||
int rc = 0;
|
||||
int mysock = 0;
|
||||
unsigned char buf[200];
|
||||
int buflen = sizeof(buf);
|
||||
int msgid = 1;
|
||||
MQTTString topicString = MQTTString_initializer;
|
||||
int req_qos = 0;
|
||||
char* payload = "mypayload";
|
||||
int payloadlen = strlen(payload);
|
||||
int len = 0;
|
||||
char *host = "m2m.eclipse.org";
|
||||
int port = 1883;
|
||||
MQTTTransport mytransport;
|
||||
|
||||
stop_init();
|
||||
if (argc > 1)
|
||||
host = argv[1];
|
||||
|
||||
if (argc > 2)
|
||||
port = atoi(argv[2]);
|
||||
|
||||
mysock = transport_open(host, port);
|
||||
if(mysock < 0)
|
||||
return mysock;
|
||||
|
||||
printf("Sending to hostname %s port %d\n", host, port);
|
||||
|
||||
mytransport.sck = &mysock;
|
||||
mytransport.getfn = transport_getdatanb;
|
||||
mytransport.state = 0;
|
||||
data.clientID.cstring = "me";
|
||||
data.keepAliveInterval = 20;
|
||||
data.cleansession = 1;
|
||||
data.username.cstring = "testuser";
|
||||
data.password.cstring = "testpassword";
|
||||
|
||||
len = MQTTSerialize_connect(buf, buflen, &data);
|
||||
rc = transport_sendPacketBuffer(mysock, buf, len);
|
||||
|
||||
/* wait for connack */
|
||||
if (MQTTPacket_read(buf, buflen, transport_getdata) == CONNACK)
|
||||
{
|
||||
unsigned char sessionPresent, connack_rc;
|
||||
|
||||
if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, buf, buflen) != 1 || connack_rc != 0)
|
||||
{
|
||||
printf("Unable to connect, return code %d\n", connack_rc);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
goto exit;
|
||||
|
||||
/* subscribe */
|
||||
topicString.cstring = "substopic";
|
||||
len = MQTTSerialize_subscribe(buf, buflen, 0, msgid, 1, &topicString, &req_qos);
|
||||
|
||||
rc = transport_sendPacketBuffer(mysock, buf, len);
|
||||
do {
|
||||
int frc;
|
||||
if ((frc=MQTTPacket_readnb(buf, buflen, &mytransport)) == SUBACK) /* wait for suback */
|
||||
{
|
||||
unsigned short submsgid;
|
||||
int subcount;
|
||||
int granted_qos;
|
||||
|
||||
rc = MQTTDeserialize_suback(&submsgid, 1, &subcount, &granted_qos, buf, buflen);
|
||||
if (granted_qos != 0)
|
||||
{
|
||||
printf("granted qos != 0, %d\n", granted_qos);
|
||||
goto exit;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (frc == -1)
|
||||
goto exit;
|
||||
} while (1); /* handle timeouts here */
|
||||
/* loop getting msgs on subscribed topic */
|
||||
topicString.cstring = "pubtopic";
|
||||
while (!toStop)
|
||||
{
|
||||
/* handle timeouts */
|
||||
if (MQTTPacket_readnb(buf, buflen, &mytransport) == PUBLISH)
|
||||
{
|
||||
unsigned char dup;
|
||||
int qos;
|
||||
unsigned char retained;
|
||||
unsigned short msgid;
|
||||
int payloadlen_in;
|
||||
unsigned char* payload_in;
|
||||
int rc;
|
||||
MQTTString receivedTopic;
|
||||
|
||||
rc = MQTTDeserialize_publish(&dup, &qos, &retained, &msgid, &receivedTopic,
|
||||
&payload_in, &payloadlen_in, buf, buflen);
|
||||
printf("message arrived %.*s\n", payloadlen_in, payload_in);
|
||||
printf("publishing reading\n");
|
||||
len = MQTTSerialize_publish(buf, buflen, 0, 0, 0, 0, topicString, (unsigned char*)payload, payloadlen);
|
||||
rc = transport_sendPacketBuffer(mysock, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
printf("disconnecting\n");
|
||||
len = MQTTSerialize_disconnect(buf, buflen);
|
||||
rc = transport_sendPacketBuffer(mysock, buf, len);
|
||||
|
||||
exit:
|
||||
transport_close(mysock);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Sergio R. Caprile - clarifications and/or documentation extension
|
||||
*******************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "transport.h"
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
|
||||
int rc = 0;
|
||||
char buf[200];
|
||||
int buflen = sizeof(buf);
|
||||
int mysock = 0;
|
||||
MQTTString topicString = MQTTString_initializer;
|
||||
char* payload = "mypayload";
|
||||
int payloadlen = strlen(payload);
|
||||
int len = 0;
|
||||
char *host = "m2m.eclipse.org";
|
||||
int port = 1883;
|
||||
|
||||
if (argc > 1)
|
||||
host = argv[1];
|
||||
|
||||
if (argc > 2)
|
||||
port = atoi(argv[2]);
|
||||
|
||||
mysock = transport_open(host,port);
|
||||
if(mysock < 0)
|
||||
return mysock;
|
||||
|
||||
printf("Sending to hostname %s port %d\n", host, port);
|
||||
|
||||
data.clientID.cstring = "me";
|
||||
data.keepAliveInterval = 20;
|
||||
data.cleansession = 1;
|
||||
data.username.cstring = "testuser";
|
||||
data.password.cstring = "testpassword";
|
||||
data.MQTTVersion = 4;
|
||||
|
||||
len = MQTTSerialize_connect((unsigned char *)buf, buflen, &data);
|
||||
|
||||
topicString.cstring = "mytopic";
|
||||
len += MQTTSerialize_publish((unsigned char *)(buf + len), buflen - len, 0, 0, 0, 0, topicString, (unsigned char *)payload, payloadlen);
|
||||
|
||||
len += MQTTSerialize_disconnect((unsigned char *)(buf + len), buflen - len);
|
||||
|
||||
rc = transport_sendPacketBuffer(mysock, buf, len);
|
||||
if (rc == len)
|
||||
printf("Successfully published\n");
|
||||
else
|
||||
printf("Publish failed\n");
|
||||
|
||||
exit:
|
||||
transport_close(mysock);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Sergio R. Caprile - "commonalization" from prior samples and/or documentation extension
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#if !defined(SOCKET_ERROR)
|
||||
/** error in socket operation */
|
||||
#define SOCKET_ERROR -1
|
||||
#endif
|
||||
|
||||
#if defined(WIN32)
|
||||
/* default on Windows is 64 - increase to make Linux and Windows the same */
|
||||
#define FD_SETSIZE 1024
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#define MAXHOSTNAMELEN 256
|
||||
#define EAGAIN WSAEWOULDBLOCK
|
||||
#define EINTR WSAEINTR
|
||||
#define EINVAL WSAEINVAL
|
||||
#define EINPROGRESS WSAEINPROGRESS
|
||||
#define EWOULDBLOCK WSAEWOULDBLOCK
|
||||
#define ENOTCONN WSAENOTCONN
|
||||
#define ECONNRESET WSAECONNRESET
|
||||
#define ioctl ioctlsocket
|
||||
#define socklen_t int
|
||||
#else
|
||||
#define INVALID_SOCKET SOCKET_ERROR
|
||||
#include <sys/socket.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/time.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <Iphlpapi.h>
|
||||
#else
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
This simple low-level implementation assumes a single connection for a single thread. Thus, a static
|
||||
variable is used for that connection.
|
||||
On other scenarios, the user must solve this by taking into account that the current implementation of
|
||||
MQTTPacket_read() has a function pointer for a function call to get the data to a buffer, but no provisions
|
||||
to know the caller or other indicator (the socket id): int (*getfn)(unsigned char*, int)
|
||||
*/
|
||||
static int mysock = INVALID_SOCKET;
|
||||
|
||||
|
||||
int transport_sendPacketBuffer(int sock, unsigned char* buf, int buflen)
|
||||
{
|
||||
int rc = 0;
|
||||
rc = write(sock, buf, buflen);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int transport_getdata(unsigned char* buf, int count)
|
||||
{
|
||||
int rc = recv(mysock, buf, count, 0);
|
||||
//printf("received %d bytes count %d\n", rc, (int)count);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int transport_getdatanb(void *sck, unsigned char* buf, int count)
|
||||
{
|
||||
int sock = *((int *)sck); /* sck: pointer to whatever the system may use to identify the transport */
|
||||
/* this call will return after the timeout set on initialization if no bytes;
|
||||
in your system you will use whatever you use to get whichever outstanding
|
||||
bytes your socket equivalent has ready to be extracted right now, if any,
|
||||
or return immediately */
|
||||
int rc = recv(sock, buf, count, 0);
|
||||
if (rc == -1) {
|
||||
/* check error conditions from your system here, and return -1 */
|
||||
return 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
return >=0 for a socket descriptor, <0 for an error code
|
||||
@todo Basically moved from the sample without changes, should accomodate same usage for 'sock' for clarity,
|
||||
removing indirections
|
||||
*/
|
||||
int transport_open(char* addr, int port)
|
||||
{
|
||||
int* sock = &mysock;
|
||||
int type = SOCK_STREAM;
|
||||
struct sockaddr_in address;
|
||||
#if defined(AF_INET6)
|
||||
struct sockaddr_in6 address6;
|
||||
#endif
|
||||
int rc = -1;
|
||||
#if defined(WIN32)
|
||||
short family;
|
||||
#else
|
||||
sa_family_t family = AF_INET;
|
||||
#endif
|
||||
struct addrinfo *result = NULL;
|
||||
struct addrinfo hints = {0, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL};
|
||||
static struct timeval tv;
|
||||
|
||||
*sock = -1;
|
||||
if (addr[0] == '[')
|
||||
++addr;
|
||||
|
||||
if ((rc = getaddrinfo(addr, NULL, &hints, &result)) == 0)
|
||||
{
|
||||
struct addrinfo* res = result;
|
||||
|
||||
/* prefer ip4 addresses */
|
||||
while (res)
|
||||
{
|
||||
if (res->ai_family == AF_INET)
|
||||
{
|
||||
result = res;
|
||||
break;
|
||||
}
|
||||
res = res->ai_next;
|
||||
}
|
||||
|
||||
#if defined(AF_INET6)
|
||||
if (result->ai_family == AF_INET6)
|
||||
{
|
||||
address6.sin6_port = htons(port);
|
||||
address6.sin6_family = family = AF_INET6;
|
||||
address6.sin6_addr = ((struct sockaddr_in6*)(result->ai_addr))->sin6_addr;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (result->ai_family == AF_INET)
|
||||
{
|
||||
address.sin_port = htons(port);
|
||||
address.sin_family = family = AF_INET;
|
||||
address.sin_addr = ((struct sockaddr_in*)(result->ai_addr))->sin_addr;
|
||||
}
|
||||
else
|
||||
rc = -1;
|
||||
|
||||
freeaddrinfo(result);
|
||||
}
|
||||
|
||||
if (rc == 0)
|
||||
{
|
||||
*sock = socket(family, type, 0);
|
||||
if (*sock != -1)
|
||||
{
|
||||
#if defined(NOSIGPIPE)
|
||||
int opt = 1;
|
||||
|
||||
if (setsockopt(*sock, SOL_SOCKET, SO_NOSIGPIPE, (void*)&opt, sizeof(opt)) != 0)
|
||||
Log(TRACE_MIN, -1, "Could not set SO_NOSIGPIPE for socket %d", *sock);
|
||||
#endif
|
||||
|
||||
if (family == AF_INET)
|
||||
rc = connect(*sock, (struct sockaddr*)&address, sizeof(address));
|
||||
#if defined(AF_INET6)
|
||||
else
|
||||
rc = connect(*sock, (struct sockaddr*)&address6, sizeof(address6));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (mysock == INVALID_SOCKET)
|
||||
return rc;
|
||||
|
||||
tv.tv_sec = 1; /* 1 second Timeout */
|
||||
tv.tv_usec = 0;
|
||||
setsockopt(mysock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval));
|
||||
return mysock;
|
||||
}
|
||||
|
||||
int transport_close(int sock)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = shutdown(sock, SHUT_WR);
|
||||
rc = recv(sock, NULL, (size_t)0, 0);
|
||||
rc = close(sock);
|
||||
|
||||
return rc;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Sergio R. Caprile - "commonalization" from prior samples and/or documentation extension
|
||||
*******************************************************************************/
|
||||
|
||||
int transport_sendPacketBuffer(int sock, unsigned char* buf, int buflen);
|
||||
int transport_getdata(unsigned char* buf, int count);
|
||||
int transport_getdatanb(void *sck, unsigned char* buf, int count);
|
||||
int transport_open(char* host, int port);
|
||||
int transport_close(int sock);
|
|
@ -0,0 +1,136 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Xiang Rong - 442039 Add makefile to Embedded C client
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef MQTTCONNECT_H_
|
||||
#define MQTTCONNECT_H_
|
||||
|
||||
#if !defined(DLLImport)
|
||||
#define DLLImport
|
||||
#endif
|
||||
#if !defined(DLLExport)
|
||||
#define DLLExport
|
||||
#endif
|
||||
|
||||
|
||||
typedef union
|
||||
{
|
||||
unsigned char all; /**< all connect flags */
|
||||
#if defined(REVERSED)
|
||||
struct
|
||||
{
|
||||
unsigned int username : 1; /**< 3.1 user name */
|
||||
unsigned int password : 1; /**< 3.1 password */
|
||||
unsigned int willRetain : 1; /**< will retain setting */
|
||||
unsigned int willQoS : 2; /**< will QoS value */
|
||||
unsigned int will : 1; /**< will flag */
|
||||
unsigned int cleansession : 1; /**< clean session flag */
|
||||
unsigned int : 1; /**< unused */
|
||||
} bits;
|
||||
#else
|
||||
struct
|
||||
{
|
||||
unsigned int : 1; /**< unused */
|
||||
unsigned int cleansession : 1; /**< cleansession flag */
|
||||
unsigned int will : 1; /**< will flag */
|
||||
unsigned int willQoS : 2; /**< will QoS value */
|
||||
unsigned int willRetain : 1; /**< will retain setting */
|
||||
unsigned int password : 1; /**< 3.1 password */
|
||||
unsigned int username : 1; /**< 3.1 user name */
|
||||
} bits;
|
||||
#endif
|
||||
} MQTTConnectFlags; /**< connect flags byte */
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Defines the MQTT "Last Will and Testament" (LWT) settings for
|
||||
* the connect packet.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. must be MQTW. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0 */
|
||||
int struct_version;
|
||||
/** The LWT topic to which the LWT message will be published. */
|
||||
MQTTString topicName;
|
||||
/** The LWT payload. */
|
||||
MQTTString message;
|
||||
/**
|
||||
* The retained flag for the LWT message (see MQTTAsync_message.retained).
|
||||
*/
|
||||
unsigned char retained;
|
||||
/**
|
||||
* The quality of service setting for the LWT message (see
|
||||
* MQTTAsync_message.qos and @ref qos).
|
||||
*/
|
||||
char qos;
|
||||
} MQTTPacket_willOptions;
|
||||
|
||||
|
||||
#define MQTTPacket_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 0, {NULL, {0, NULL}}, {NULL, {0, NULL}}, 0, 0 }
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. must be MQTC. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0 */
|
||||
int struct_version;
|
||||
/** Version of MQTT to be used. 3 = 3.1 4 = 3.1.1
|
||||
*/
|
||||
unsigned char MQTTVersion;
|
||||
MQTTString clientID;
|
||||
unsigned short keepAliveInterval;
|
||||
unsigned char cleansession;
|
||||
unsigned char willFlag;
|
||||
MQTTPacket_willOptions will;
|
||||
MQTTString username;
|
||||
MQTTString password;
|
||||
} MQTTPacket_connectData;
|
||||
|
||||
typedef union
|
||||
{
|
||||
unsigned char all; /**< all connack flags */
|
||||
#if defined(REVERSED)
|
||||
struct
|
||||
{
|
||||
unsigned int sessionpresent : 1; /**< session present flag */
|
||||
unsigned int : 7; /**< unused */
|
||||
} bits;
|
||||
#else
|
||||
struct
|
||||
{
|
||||
unsigned int : 7; /**< unused */
|
||||
unsigned int sessionpresent : 1; /**< session present flag */
|
||||
} bits;
|
||||
#endif
|
||||
} MQTTConnackFlags; /**< connack flags byte */
|
||||
|
||||
#define MQTTPacket_connectData_initializer { {'M', 'Q', 'T', 'C'}, 0, 4, {NULL, {0, NULL}}, 60, 1, 0, \
|
||||
MQTTPacket_willOptions_initializer, {NULL, {0, NULL}}, {NULL, {0, NULL}} }
|
||||
|
||||
DLLExport int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options);
|
||||
DLLExport int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len);
|
||||
|
||||
DLLExport int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent);
|
||||
DLLExport int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen);
|
||||
|
||||
DLLExport int MQTTSerialize_disconnect(unsigned char* buf, int buflen);
|
||||
DLLExport int MQTTSerialize_pingreq(unsigned char* buf, int buflen);
|
||||
|
||||
#endif /* MQTTCONNECT_H_ */
|
|
@ -0,0 +1,214 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Determines the length of the MQTT connect packet that would be produced using the supplied connect options.
|
||||
* @param options the options to be used to build the connect packet
|
||||
* @return the length of buffer needed to contain the serialized version of the packet
|
||||
*/
|
||||
int MQTTSerialize_connectLength(MQTTPacket_connectData* options)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
|
||||
if (options->MQTTVersion == 3)
|
||||
len = 12; /* variable depending on MQTT or MQIsdp */
|
||||
else if (options->MQTTVersion == 4)
|
||||
len = 10;
|
||||
|
||||
len += MQTTstrlen(options->clientID)+2;
|
||||
if (options->willFlag)
|
||||
len += MQTTstrlen(options->will.topicName)+2 + MQTTstrlen(options->will.message)+2;
|
||||
if (options->username.cstring || options->username.lenstring.data)
|
||||
len += MQTTstrlen(options->username)+2;
|
||||
if (options->password.cstring || options->password.lenstring.data)
|
||||
len += MQTTstrlen(options->password)+2;
|
||||
|
||||
FUNC_EXIT_RC(len);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the connect options into the buffer.
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param len the length in bytes of the supplied buffer
|
||||
* @param options the options to be used to build the connect packet
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options)
|
||||
{
|
||||
unsigned char *ptr = buf;
|
||||
MQTTHeader header = {0};
|
||||
MQTTConnectFlags flags = {0};
|
||||
int len = 0;
|
||||
int rc = -1;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (MQTTPacket_len(len = MQTTSerialize_connectLength(options)) > buflen)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
header.byte = 0;
|
||||
header.bits.type = CONNECT;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, len); /* write remaining length */
|
||||
|
||||
if (options->MQTTVersion == 4)
|
||||
{
|
||||
writeCString(&ptr, "MQTT");
|
||||
writeChar(&ptr, (char) 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeCString(&ptr, "MQIsdp");
|
||||
writeChar(&ptr, (char) 3);
|
||||
}
|
||||
|
||||
flags.all = 0;
|
||||
flags.bits.cleansession = options->cleansession;
|
||||
flags.bits.will = (options->willFlag) ? 1 : 0;
|
||||
if (flags.bits.will)
|
||||
{
|
||||
flags.bits.willQoS = options->will.qos;
|
||||
flags.bits.willRetain = options->will.retained;
|
||||
}
|
||||
|
||||
if (options->username.cstring || options->username.lenstring.data)
|
||||
flags.bits.username = 1;
|
||||
if (options->password.cstring || options->password.lenstring.data)
|
||||
flags.bits.password = 1;
|
||||
|
||||
writeChar(&ptr, flags.all);
|
||||
writeInt(&ptr, options->keepAliveInterval);
|
||||
writeMQTTString(&ptr, options->clientID);
|
||||
if (options->willFlag)
|
||||
{
|
||||
writeMQTTString(&ptr, options->will.topicName);
|
||||
writeMQTTString(&ptr, options->will.message);
|
||||
}
|
||||
if (flags.bits.username)
|
||||
writeMQTTString(&ptr, options->username);
|
||||
if (flags.bits.password)
|
||||
writeMQTTString(&ptr, options->password);
|
||||
|
||||
rc = ptr - buf;
|
||||
|
||||
exit: FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into connack data - return code
|
||||
* @param sessionPresent the session present flag returned (only for MQTT 3.1.1)
|
||||
* @param connack_rc returned integer value of the connack return code
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param len the length in bytes of the data in the supplied buffer
|
||||
* @return error code. 1 is success, 0 is failure
|
||||
*/
|
||||
int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = NULL;
|
||||
int rc = 0;
|
||||
int mylen;
|
||||
MQTTConnackFlags flags = {0};
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
if (header.bits.type != CONNACK)
|
||||
goto exit;
|
||||
|
||||
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||
enddata = curdata + mylen;
|
||||
if (enddata - curdata < 2)
|
||||
goto exit;
|
||||
|
||||
flags.all = readChar(&curdata);
|
||||
*sessionPresent = flags.bits.sessionpresent;
|
||||
*connack_rc = readChar(&curdata);
|
||||
|
||||
rc = 1;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes a 0-length packet into the supplied buffer, ready for writing to a socket
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer, to avoid overruns
|
||||
* @param packettype the message type
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_zero(unsigned char* buf, int buflen, unsigned char packettype)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
int rc = -1;
|
||||
unsigned char *ptr = buf;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (buflen < 2)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
header.byte = 0;
|
||||
header.bits.type = packettype;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, 0); /* write remaining length */
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes a disconnect packet into the supplied buffer, ready for writing to a socket
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer, to avoid overruns
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_disconnect(unsigned char* buf, int buflen)
|
||||
{
|
||||
return MQTTSerialize_zero(buf, buflen, DISCONNECT);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes a disconnect packet into the supplied buffer, ready for writing to a socket
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer, to avoid overruns
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_pingreq(unsigned char* buf, int buflen)
|
||||
{
|
||||
return MQTTSerialize_zero(buf, buflen, PINGREQ);
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "StackTrace.h"
|
||||
#include "MQTTPacket.h"
|
||||
#include <string.h>
|
||||
|
||||
#define min(a, b) ((a < b) ? a : b)
|
||||
|
||||
|
||||
/**
|
||||
* Validates MQTT protocol name and version combinations
|
||||
* @param protocol the MQTT protocol name as an MQTTString
|
||||
* @param version the MQTT protocol version number, as in the connect packet
|
||||
* @return correct MQTT combination? 1 is true, 0 is false
|
||||
*/
|
||||
int MQTTPacket_checkVersion(MQTTString* protocol, int version)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (version == 3 && memcmp(protocol->lenstring.data, "MQIsdp",
|
||||
min(6, protocol->lenstring.len)) == 0)
|
||||
rc = 1;
|
||||
else if (version == 4 && memcmp(protocol->lenstring.data, "MQTT",
|
||||
min(4, protocol->lenstring.len)) == 0)
|
||||
rc = 1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into connect data structure
|
||||
* @param data the connect data structure to be filled out
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param len the length in bytes of the data in the supplied buffer
|
||||
* @return error code. 1 is success, 0 is failure
|
||||
*/
|
||||
int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
MQTTConnectFlags flags = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = &buf[len];
|
||||
int rc = 0;
|
||||
MQTTString Protocol;
|
||||
int version;
|
||||
int mylen = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
if (header.bits.type != CONNECT)
|
||||
goto exit;
|
||||
|
||||
curdata += MQTTPacket_decodeBuf(curdata, &mylen); /* read remaining length */
|
||||
|
||||
if (!readMQTTLenString(&Protocol, &curdata, enddata) ||
|
||||
enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */
|
||||
goto exit;
|
||||
|
||||
version = (int)readChar(&curdata); /* Protocol version */
|
||||
/* If we don't recognize the protocol version, we don't parse the connect packet on the
|
||||
* basis that we don't know what the format will be.
|
||||
*/
|
||||
if (MQTTPacket_checkVersion(&Protocol, version))
|
||||
{
|
||||
flags.all = readChar(&curdata);
|
||||
data->cleansession = flags.bits.cleansession;
|
||||
data->keepAliveInterval = readInt(&curdata);
|
||||
if (!readMQTTLenString(&data->clientID, &curdata, enddata))
|
||||
goto exit;
|
||||
data->willFlag = flags.bits.will;
|
||||
if (flags.bits.will)
|
||||
{
|
||||
data->will.qos = flags.bits.willQoS;
|
||||
data->will.retained = flags.bits.willRetain;
|
||||
if (!readMQTTLenString(&data->will.topicName, &curdata, enddata) ||
|
||||
!readMQTTLenString(&data->will.message, &curdata, enddata))
|
||||
goto exit;
|
||||
}
|
||||
if (flags.bits.username)
|
||||
{
|
||||
if (enddata - curdata < 3 || !readMQTTLenString(&data->username, &curdata, enddata))
|
||||
goto exit; /* username flag set, but no username supplied - invalid */
|
||||
if (flags.bits.password &&
|
||||
(enddata - curdata < 3 || !readMQTTLenString(&data->password, &curdata, enddata)))
|
||||
goto exit; /* password flag set, but no password supplied - invalid */
|
||||
}
|
||||
else if (flags.bits.password)
|
||||
goto exit; /* password flag set without username - invalid */
|
||||
rc = 1;
|
||||
}
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the connack packet into the supplied buffer.
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param connack_rc the integer connack return code to be used
|
||||
* @param sessionPresent the MQTT 3.1.1 sessionPresent flag
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
int rc = 0;
|
||||
unsigned char *ptr = buf;
|
||||
MQTTConnackFlags flags = {0};
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (buflen < 2)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
header.byte = 0;
|
||||
header.bits.type = CONNACK;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */
|
||||
|
||||
flags.all = 0;
|
||||
flags.bits.sessionpresent = sessionPresent;
|
||||
writeChar(&ptr, flags.all);
|
||||
writeChar(&ptr, connack_rc);
|
||||
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "StackTrace.h"
|
||||
#include "MQTTPacket.h"
|
||||
#include <string.h>
|
||||
|
||||
#define min(a, b) ((a < b) ? 1 : 0)
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into publish data
|
||||
* @param dup returned integer - the MQTT dup flag
|
||||
* @param qos returned integer - the MQTT QoS value
|
||||
* @param retained returned integer - the MQTT retained flag
|
||||
* @param packetid returned integer - the MQTT packet identifier
|
||||
* @param topicName returned MQTTString - the MQTT topic in the publish
|
||||
* @param payload returned byte buffer - the MQTT publish payload
|
||||
* @param payloadlen returned integer - the length of the MQTT payload
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @return error code. 1 is success
|
||||
*/
|
||||
int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName,
|
||||
unsigned char** payload, int* payloadlen, unsigned char* buf, int buflen)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = NULL;
|
||||
int rc = 0;
|
||||
int mylen = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
if (header.bits.type != PUBLISH)
|
||||
goto exit;
|
||||
*dup = header.bits.dup;
|
||||
*qos = header.bits.qos;
|
||||
*retained = header.bits.retain;
|
||||
|
||||
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||
enddata = curdata + mylen;
|
||||
|
||||
if (!readMQTTLenString(topicName, &curdata, enddata) ||
|
||||
enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */
|
||||
goto exit;
|
||||
|
||||
if (*qos > 0)
|
||||
*packetid = readInt(&curdata);
|
||||
|
||||
*payloadlen = enddata - curdata;
|
||||
*payload = curdata;
|
||||
rc = 1;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into an ack
|
||||
* @param packettype returned integer - the MQTT packet type
|
||||
* @param dup returned integer - the MQTT dup flag
|
||||
* @param packetid returned integer - the MQTT packet identifier
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @return error code. 1 is success, 0 is failure
|
||||
*/
|
||||
int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = NULL;
|
||||
int rc = 0;
|
||||
int mylen;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
*dup = header.bits.dup;
|
||||
*packettype = header.bits.type;
|
||||
|
||||
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||
enddata = curdata + mylen;
|
||||
|
||||
if (enddata - curdata < 2)
|
||||
goto exit;
|
||||
*packetid = readInt(&curdata);
|
||||
|
||||
rc = 1;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -0,0 +1,258 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "StackTrace.h"
|
||||
#include "MQTTPacket.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
const char* MQTTPacket_names[] =
|
||||
{
|
||||
"RESERVED", "CONNECT", "CONNACK", "PUBLISH", "PUBACK", "PUBREC", "PUBREL",
|
||||
"PUBCOMP", "SUBSCRIBE", "SUBACK", "UNSUBSCRIBE", "UNSUBACK",
|
||||
"PINGREQ", "PINGRESP", "DISCONNECT"
|
||||
};
|
||||
|
||||
|
||||
const char* MQTTPacket_getName(unsigned short packetid)
|
||||
{
|
||||
return MQTTPacket_names[packetid];
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data)
|
||||
{
|
||||
int strindex = 0;
|
||||
|
||||
strindex = snprintf(strbuf, strbuflen,
|
||||
"CONNECT MQTT version %d, client id %.*s, clean session %d, keep alive %d",
|
||||
(int)data->MQTTVersion, data->clientID.lenstring.len, data->clientID.lenstring.data,
|
||||
(int)data->cleansession, data->keepAliveInterval);
|
||||
if (data->willFlag)
|
||||
strindex += snprintf(&strbuf[strindex], strbuflen - strindex,
|
||||
", will QoS %d, will retain %d, will topic %.*s, will message %.*s",
|
||||
data->will.qos, data->will.retained,
|
||||
data->will.topicName.lenstring.len, data->will.topicName.lenstring.data,
|
||||
data->will.message.lenstring.len, data->will.message.lenstring.data);
|
||||
if (data->username.lenstring.data && data->username.lenstring.len > 0)
|
||||
strindex += snprintf(&strbuf[strindex], strbuflen - strindex,
|
||||
", user name %.*s", data->username.lenstring.len, data->username.lenstring.data);
|
||||
if (data->password.lenstring.data && data->password.lenstring.len > 0)
|
||||
strindex += snprintf(&strbuf[strindex], strbuflen - strindex,
|
||||
", password %.*s", data->password.lenstring.len, data->password.lenstring.data);
|
||||
return strindex;
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent)
|
||||
{
|
||||
int strindex = snprintf(strbuf, strbuflen, "CONNACK session present %d, rc %d", sessionPresent, connack_rc);
|
||||
return strindex;
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained,
|
||||
unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen)
|
||||
{
|
||||
int strindex = snprintf(strbuf, strbuflen,
|
||||
"PUBLISH dup %d, QoS %d, retained %d, packet id %d, topic %.*s, payload length %d, payload %.*s",
|
||||
dup, qos, retained, packetid,
|
||||
(topicName.lenstring.len < 20) ? topicName.lenstring.len : 20, topicName.lenstring.data,
|
||||
payloadlen, (payloadlen < 20) ? payloadlen : 20, payload);
|
||||
return strindex;
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid)
|
||||
{
|
||||
int strindex = snprintf(strbuf, strbuflen, "%s, packet id %d", MQTTPacket_names[packettype], packetid);
|
||||
if (dup)
|
||||
strindex += snprintf(strbuf + strindex, strbuflen - strindex, ", dup %d", dup);
|
||||
return strindex;
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count,
|
||||
MQTTString topicFilters[], int requestedQoSs[])
|
||||
{
|
||||
return snprintf(strbuf, strbuflen,
|
||||
"SUBSCRIBE dup %d, packet id %d count %d topic %.*s qos %d",
|
||||
dup, packetid, count,
|
||||
topicFilters[0].lenstring.len, topicFilters[0].lenstring.data,
|
||||
requestedQoSs[0]);
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs)
|
||||
{
|
||||
return snprintf(strbuf, strbuflen,
|
||||
"SUBACK packet id %d count %d granted qos %d", packetid, count, grantedQoSs[0]);
|
||||
}
|
||||
|
||||
|
||||
int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid,
|
||||
int count, MQTTString topicFilters[])
|
||||
{
|
||||
return snprintf(strbuf, strbuflen,
|
||||
"UNSUBSCRIBE dup %d, packet id %d count %d topic %.*s",
|
||||
dup, packetid, count,
|
||||
topicFilters[0].lenstring.len, topicFilters[0].lenstring.data);
|
||||
}
|
||||
|
||||
|
||||
char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen)
|
||||
{
|
||||
int index = 0;
|
||||
int rem_length = 0;
|
||||
MQTTHeader header = {0};
|
||||
int strindex = 0;
|
||||
|
||||
header.byte = buf[index++];
|
||||
index += MQTTPacket_decodeBuf(&buf[index], &rem_length);
|
||||
|
||||
switch (header.bits.type)
|
||||
{
|
||||
case CONNACK:
|
||||
{
|
||||
unsigned char sessionPresent, connack_rc;
|
||||
if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, buf, buflen) == 1)
|
||||
strindex = MQTTStringFormat_connack(strbuf, strbuflen, connack_rc, sessionPresent);
|
||||
}
|
||||
break;
|
||||
case PUBLISH:
|
||||
{
|
||||
unsigned char dup, retained, *payload;
|
||||
unsigned short packetid;
|
||||
int qos, payloadlen;
|
||||
MQTTString topicName = MQTTString_initializer;
|
||||
if (MQTTDeserialize_publish(&dup, &qos, &retained, &packetid, &topicName,
|
||||
&payload, &payloadlen, buf, buflen) == 1)
|
||||
strindex = MQTTStringFormat_publish(strbuf, strbuflen, dup, qos, retained, packetid,
|
||||
topicName, payload, payloadlen);
|
||||
}
|
||||
break;
|
||||
case PUBACK:
|
||||
case PUBREC:
|
||||
case PUBREL:
|
||||
case PUBCOMP:
|
||||
{
|
||||
unsigned char packettype, dup;
|
||||
unsigned short packetid;
|
||||
if (MQTTDeserialize_ack(&packettype, &dup, &packetid, buf, buflen) == 1)
|
||||
strindex = MQTTStringFormat_ack(strbuf, strbuflen, packettype, dup, packetid);
|
||||
}
|
||||
break;
|
||||
case SUBACK:
|
||||
{
|
||||
unsigned short packetid;
|
||||
int maxcount = 1, count = 0;
|
||||
int grantedQoSs[1];
|
||||
if (MQTTDeserialize_suback(&packetid, maxcount, &count, grantedQoSs, buf, buflen) == 1)
|
||||
strindex = MQTTStringFormat_suback(strbuf, strbuflen, packetid, count, grantedQoSs);
|
||||
}
|
||||
break;
|
||||
case UNSUBACK:
|
||||
{
|
||||
unsigned short packetid;
|
||||
if (MQTTDeserialize_unsuback(&packetid, buf, buflen) == 1)
|
||||
strindex = MQTTStringFormat_ack(strbuf, strbuflen, UNSUBACK, 0, packetid);
|
||||
}
|
||||
break;
|
||||
case PINGREQ:
|
||||
case PINGRESP:
|
||||
case DISCONNECT:
|
||||
strindex = snprintf(strbuf, strbuflen, "%s", MQTTPacket_names[header.bits.type]);
|
||||
break;
|
||||
}
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
|
||||
char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen)
|
||||
{
|
||||
int index = 0;
|
||||
int rem_length = 0;
|
||||
MQTTHeader header = {0};
|
||||
int strindex = 0;
|
||||
|
||||
header.byte = buf[index++];
|
||||
index += MQTTPacket_decodeBuf(&buf[index], &rem_length);
|
||||
|
||||
switch (header.bits.type)
|
||||
{
|
||||
case CONNECT:
|
||||
{
|
||||
MQTTPacket_connectData data;
|
||||
int rc;
|
||||
if ((rc = MQTTDeserialize_connect(&data, buf, buflen)) == 1)
|
||||
strindex = MQTTStringFormat_connect(strbuf, strbuflen, &data);
|
||||
}
|
||||
break;
|
||||
case PUBLISH:
|
||||
{
|
||||
unsigned char dup, retained, *payload;
|
||||
unsigned short packetid;
|
||||
int qos, payloadlen;
|
||||
MQTTString topicName = MQTTString_initializer;
|
||||
if (MQTTDeserialize_publish(&dup, &qos, &retained, &packetid, &topicName,
|
||||
&payload, &payloadlen, buf, buflen) == 1)
|
||||
strindex = MQTTStringFormat_publish(strbuf, strbuflen, dup, qos, retained, packetid,
|
||||
topicName, payload, payloadlen);
|
||||
}
|
||||
break;
|
||||
case PUBACK:
|
||||
case PUBREC:
|
||||
case PUBREL:
|
||||
case PUBCOMP:
|
||||
{
|
||||
unsigned char packettype, dup;
|
||||
unsigned short packetid;
|
||||
if (MQTTDeserialize_ack(&packettype, &dup, &packetid, buf, buflen) == 1)
|
||||
strindex = MQTTStringFormat_ack(strbuf, strbuflen, packettype, dup, packetid);
|
||||
}
|
||||
break;
|
||||
case SUBSCRIBE:
|
||||
{
|
||||
unsigned char dup;
|
||||
unsigned short packetid;
|
||||
int maxcount = 1, count = 0;
|
||||
MQTTString topicFilters[1];
|
||||
int requestedQoSs[1];
|
||||
if (MQTTDeserialize_subscribe(&dup, &packetid, maxcount, &count,
|
||||
topicFilters, requestedQoSs, buf, buflen) == 1)
|
||||
strindex = MQTTStringFormat_subscribe(strbuf, strbuflen, dup, packetid, count, topicFilters, requestedQoSs);;
|
||||
}
|
||||
break;
|
||||
case UNSUBSCRIBE:
|
||||
{
|
||||
unsigned char dup;
|
||||
unsigned short packetid;
|
||||
int maxcount = 1, count = 0;
|
||||
MQTTString topicFilters[1];
|
||||
if (MQTTDeserialize_unsubscribe(&dup, &packetid, maxcount, &count, topicFilters, buf, buflen) == 1)
|
||||
strindex = MQTTStringFormat_unsubscribe(strbuf, strbuflen, dup, packetid, count, topicFilters);
|
||||
}
|
||||
break;
|
||||
case PINGREQ:
|
||||
case PINGRESP:
|
||||
case DISCONNECT:
|
||||
strindex = snprintf(strbuf, strbuflen, "%s", MQTTPacket_names[header.bits.type]);
|
||||
break;
|
||||
}
|
||||
strbuf[strbuflen] = '\0';
|
||||
return strbuf;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTFORMAT_H)
|
||||
#define MQTTFORMAT_H
|
||||
|
||||
#include "StackTrace.h"
|
||||
#include "MQTTPacket.h"
|
||||
|
||||
const char* MQTTPacket_getName(unsigned short packetid);
|
||||
int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data);
|
||||
int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent);
|
||||
int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained,
|
||||
unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen);
|
||||
int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid);
|
||||
int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count,
|
||||
MQTTString topicFilters[], int requestedQoSs[]);
|
||||
int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs);
|
||||
int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid,
|
||||
int count, MQTTString topicFilters[]);
|
||||
char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen);
|
||||
char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,410 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Sergio R. Caprile - non-blocking packet read functions for stream transport
|
||||
*******************************************************************************/
|
||||
|
||||
#include "StackTrace.h"
|
||||
#include "MQTTPacket.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Encodes the message length according to the MQTT algorithm
|
||||
* @param buf the buffer into which the encoded data is written
|
||||
* @param length the length to be encoded
|
||||
* @return the number of bytes written to buffer
|
||||
*/
|
||||
int MQTTPacket_encode(unsigned char* buf, int length)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
do
|
||||
{
|
||||
char d = length % 128;
|
||||
length /= 128;
|
||||
/* if there are more digits to encode, set the top bit of this digit */
|
||||
if (length > 0)
|
||||
d |= 0x80;
|
||||
buf[rc++] = d;
|
||||
} while (length > 0);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decodes the message length according to the MQTT algorithm
|
||||
* @param getcharfn pointer to function to read the next character from the data source
|
||||
* @param value the decoded length returned
|
||||
* @return the number of bytes read from the socket
|
||||
*/
|
||||
int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value)
|
||||
{
|
||||
unsigned char c;
|
||||
int multiplier = 1;
|
||||
int len = 0;
|
||||
#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4
|
||||
|
||||
FUNC_ENTRY;
|
||||
*value = 0;
|
||||
do
|
||||
{
|
||||
int rc = MQTTPACKET_READ_ERROR;
|
||||
|
||||
if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)
|
||||
{
|
||||
rc = MQTTPACKET_READ_ERROR; /* bad data */
|
||||
goto exit;
|
||||
}
|
||||
rc = (*getcharfn)(&c, 1);
|
||||
if (rc != 1)
|
||||
goto exit;
|
||||
*value += (c & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
} while ((c & 128) != 0);
|
||||
exit:
|
||||
FUNC_EXIT_RC(len);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
int MQTTPacket_len(int rem_len)
|
||||
{
|
||||
rem_len += 1; /* header byte */
|
||||
|
||||
/* now remaining_length field */
|
||||
if (rem_len < 128)
|
||||
rem_len += 1;
|
||||
else if (rem_len < 16384)
|
||||
rem_len += 2;
|
||||
else if (rem_len < 2097151)
|
||||
rem_len += 3;
|
||||
else
|
||||
rem_len += 4;
|
||||
return rem_len;
|
||||
}
|
||||
|
||||
|
||||
static unsigned char* bufptr;
|
||||
|
||||
int bufchar(unsigned char* c, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
*c = *bufptr++;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int MQTTPacket_decodeBuf(unsigned char* buf, int* value)
|
||||
{
|
||||
bufptr = buf;
|
||||
return MQTTPacket_decode(bufchar, value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates an integer from two bytes read from the input buffer
|
||||
* @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
|
||||
* @return the integer value calculated
|
||||
*/
|
||||
int readInt(unsigned char** pptr)
|
||||
{
|
||||
unsigned char* ptr = *pptr;
|
||||
int len = 256*(*ptr) + (*(ptr+1));
|
||||
*pptr += 2;
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads one character from the input buffer.
|
||||
* @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
|
||||
* @return the character read
|
||||
*/
|
||||
char readChar(unsigned char** pptr)
|
||||
{
|
||||
char c = **pptr;
|
||||
(*pptr)++;
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes one character to an output buffer.
|
||||
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
* @param c the character to write
|
||||
*/
|
||||
void writeChar(unsigned char** pptr, char c)
|
||||
{
|
||||
**pptr = c;
|
||||
(*pptr)++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes an integer as 2 bytes to an output buffer.
|
||||
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
* @param anInt the integer to write
|
||||
*/
|
||||
void writeInt(unsigned char** pptr, int anInt)
|
||||
{
|
||||
**pptr = (unsigned char)(anInt / 256);
|
||||
(*pptr)++;
|
||||
**pptr = (unsigned char)(anInt % 256);
|
||||
(*pptr)++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes a "UTF" string to an output buffer. Converts C string to length-delimited.
|
||||
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
* @param string the C string to write
|
||||
*/
|
||||
void writeCString(unsigned char** pptr, const char* string)
|
||||
{
|
||||
int len = strlen(string);
|
||||
writeInt(pptr, len);
|
||||
memcpy(*pptr, string, len);
|
||||
*pptr += len;
|
||||
}
|
||||
|
||||
|
||||
int getLenStringLen(char* ptr)
|
||||
{
|
||||
int len = 256*((unsigned char)(*ptr)) + (unsigned char)(*(ptr+1));
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
void writeMQTTString(unsigned char** pptr, MQTTString mqttstring)
|
||||
{
|
||||
if (mqttstring.lenstring.len > 0)
|
||||
{
|
||||
writeInt(pptr, mqttstring.lenstring.len);
|
||||
memcpy(*pptr, mqttstring.lenstring.data, mqttstring.lenstring.len);
|
||||
*pptr += mqttstring.lenstring.len;
|
||||
}
|
||||
else if (mqttstring.cstring)
|
||||
writeCString(pptr, mqttstring.cstring);
|
||||
else
|
||||
writeInt(pptr, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param mqttstring the MQTTString structure into which the data is to be read
|
||||
* @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
|
||||
* @param enddata pointer to the end of the data: do not read beyond
|
||||
* @return 1 if successful, 0 if not
|
||||
*/
|
||||
int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
/* the first two bytes are the length of the string */
|
||||
if (enddata - (*pptr) > 1) /* enough length to read the integer? */
|
||||
{
|
||||
mqttstring->lenstring.len = readInt(pptr); /* increments pptr to point past length */
|
||||
if (&(*pptr)[mqttstring->lenstring.len] <= enddata)
|
||||
{
|
||||
mqttstring->lenstring.data = (char*)*pptr;
|
||||
*pptr += mqttstring->lenstring.len;
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
mqttstring->cstring = NULL;
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the length of the MQTTstring - C string if there is one, otherwise the length delimited string
|
||||
* @param mqttstring the string to return the length of
|
||||
* @return the length of the string
|
||||
*/
|
||||
int MQTTstrlen(MQTTString mqttstring)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (mqttstring.cstring)
|
||||
rc = strlen(mqttstring.cstring);
|
||||
else
|
||||
rc = mqttstring.lenstring.len;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares an MQTTString to a C string
|
||||
* @param a the MQTTString to compare
|
||||
* @param bptr the C string to compare
|
||||
* @return boolean - equal or not
|
||||
*/
|
||||
int MQTTPacket_equals(MQTTString* a, char* bptr)
|
||||
{
|
||||
int alen = 0,
|
||||
blen = 0;
|
||||
char *aptr;
|
||||
|
||||
if (a->cstring)
|
||||
{
|
||||
aptr = a->cstring;
|
||||
alen = strlen(a->cstring);
|
||||
}
|
||||
else
|
||||
{
|
||||
aptr = a->lenstring.data;
|
||||
alen = a->lenstring.len;
|
||||
}
|
||||
blen = strlen(bptr);
|
||||
|
||||
return (alen == blen) && (strncmp(aptr, bptr, alen) == 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to read packet data from some source into a buffer
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param getfn pointer to a function which will read any number of bytes from the needed source
|
||||
* @return integer MQTT packet type, or -1 on error
|
||||
* @note the whole message must fit into the caller's buffer
|
||||
*/
|
||||
int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int))
|
||||
{
|
||||
int rc = -1;
|
||||
MQTTHeader header = {0};
|
||||
int len = 0;
|
||||
int rem_len = 0;
|
||||
|
||||
/* 1. read the header byte. This has the packet type in it */
|
||||
if ((*getfn)(buf, 1) != 1)
|
||||
goto exit;
|
||||
|
||||
len = 1;
|
||||
/* 2. read the remaining length. This is variable in itself */
|
||||
MQTTPacket_decode(getfn, &rem_len);
|
||||
len += MQTTPacket_encode(buf + 1, rem_len); /* put the original remaining length back into the buffer */
|
||||
|
||||
/* 3. read the rest of the buffer using a callback to supply the rest of the data */
|
||||
if((rem_len + len) > buflen)
|
||||
goto exit;
|
||||
if ((*getfn)(buf + len, rem_len) != rem_len)
|
||||
goto exit;
|
||||
|
||||
header.byte = buf[0];
|
||||
rc = header.bits.type;
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the message length according to the MQTT algorithm, non-blocking
|
||||
* @param trp pointer to a transport structure holding what is needed to solve getting data from it
|
||||
* @param value the decoded length returned
|
||||
* @return integer the number of bytes read from the socket, 0 for call again, or -1 on error
|
||||
*/
|
||||
static int MQTTPacket_decodenb(MQTTTransport *trp)
|
||||
{
|
||||
unsigned char c;
|
||||
int rc = MQTTPACKET_READ_ERROR;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if(trp->len == 0){ /* initialize on first call */
|
||||
trp->multiplier = 1;
|
||||
trp->rem_len = 0;
|
||||
}
|
||||
do {
|
||||
int frc;
|
||||
if (++(trp->len) > MAX_NO_OF_REMAINING_LENGTH_BYTES)
|
||||
goto exit;
|
||||
if ((frc=(*trp->getfn)(trp->sck, &c, 1)) == -1)
|
||||
goto exit;
|
||||
if (frc == 0){
|
||||
rc = 0;
|
||||
goto exit;
|
||||
}
|
||||
trp->rem_len += (c & 127) * trp->multiplier;
|
||||
trp->multiplier *= 128;
|
||||
} while ((c & 128) != 0);
|
||||
rc = trp->len;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to read packet data from some source into a buffer, non-blocking
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param trp pointer to a transport structure holding what is needed to solve getting data from it
|
||||
* @return integer MQTT packet type, 0 for call again, or -1 on error
|
||||
* @note the whole message must fit into the caller's buffer
|
||||
*/
|
||||
int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp)
|
||||
{
|
||||
int rc = -1, frc;
|
||||
MQTTHeader header = {0};
|
||||
|
||||
switch(trp->state){
|
||||
default:
|
||||
trp->state = 0;
|
||||
/*FALLTHROUGH*/
|
||||
case 0:
|
||||
/* read the header byte. This has the packet type in it */
|
||||
if ((frc=(*trp->getfn)(trp->sck, buf, 1)) == -1)
|
||||
goto exit;
|
||||
if (frc == 0)
|
||||
return 0;
|
||||
trp->len = 0;
|
||||
++trp->state;
|
||||
/*FALLTHROUGH*/
|
||||
/* read the remaining length. This is variable in itself */
|
||||
case 1:
|
||||
if((frc=MQTTPacket_decodenb(trp)) == MQTTPACKET_READ_ERROR)
|
||||
goto exit;
|
||||
if(frc == 0)
|
||||
return 0;
|
||||
trp->len = 1 + MQTTPacket_encode(buf + 1, trp->rem_len); /* put the original remaining length back into the buffer */
|
||||
if((trp->rem_len + trp->len) > buflen)
|
||||
goto exit;
|
||||
++trp->state;
|
||||
/*FALLTHROUGH*/
|
||||
case 2:
|
||||
/* read the rest of the buffer using a callback to supply the rest of the data */
|
||||
if ((frc=(*trp->getfn)(trp->sck, buf + trp->len, trp->rem_len)) == -1)
|
||||
goto exit;
|
||||
if (frc == 0)
|
||||
return 0;
|
||||
trp->rem_len -= frc;
|
||||
trp->len += frc;
|
||||
if(trp->rem_len)
|
||||
return 0;
|
||||
|
||||
header.byte = buf[0];
|
||||
rc = header.bits.type;
|
||||
break;
|
||||
}
|
||||
|
||||
exit:
|
||||
trp->state = 0;
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Xiang Rong - 442039 Add makefile to Embedded C client
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef MQTTPACKET_H_
|
||||
#define MQTTPACKET_H_
|
||||
|
||||
#if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(WIN32_DLL) || defined(WIN64_DLL)
|
||||
#define DLLImport __declspec(dllimport)
|
||||
#define DLLExport __declspec(dllexport)
|
||||
#elif defined(LINUX_SO)
|
||||
#define DLLImport extern
|
||||
#define DLLExport __attribute__ ((visibility ("default")))
|
||||
#else
|
||||
#define DLLImport
|
||||
#define DLLExport
|
||||
#endif
|
||||
|
||||
enum errors
|
||||
{
|
||||
MQTTPACKET_BUFFER_TOO_SHORT = -2,
|
||||
MQTTPACKET_READ_ERROR = -1,
|
||||
MQTTPACKET_READ_COMPLETE
|
||||
};
|
||||
|
||||
enum msgTypes
|
||||
{
|
||||
CONNECT = 1, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL,
|
||||
PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK,
|
||||
PINGREQ, PINGRESP, DISCONNECT
|
||||
};
|
||||
|
||||
/**
|
||||
* Bitfields for the MQTT header byte.
|
||||
*/
|
||||
typedef union
|
||||
{
|
||||
unsigned char byte; /**< the whole byte */
|
||||
#if defined(REVERSED)
|
||||
struct
|
||||
{
|
||||
unsigned int type : 4; /**< message type nibble */
|
||||
unsigned int dup : 1; /**< DUP flag bit */
|
||||
unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */
|
||||
unsigned int retain : 1; /**< retained flag bit */
|
||||
} bits;
|
||||
#else
|
||||
struct
|
||||
{
|
||||
unsigned int retain : 1; /**< retained flag bit */
|
||||
unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */
|
||||
unsigned int dup : 1; /**< DUP flag bit */
|
||||
unsigned int type : 4; /**< message type nibble */
|
||||
} bits;
|
||||
#endif
|
||||
} MQTTHeader;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int len;
|
||||
char* data;
|
||||
} MQTTLenString;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char* cstring;
|
||||
MQTTLenString lenstring;
|
||||
} MQTTString;
|
||||
|
||||
#define MQTTString_initializer {NULL, {0, NULL}}
|
||||
|
||||
int MQTTstrlen(MQTTString mqttstring);
|
||||
|
||||
#include "MQTTConnect.h"
|
||||
#include "MQTTPublish.h"
|
||||
#include "MQTTSubscribe.h"
|
||||
#include "MQTTUnsubscribe.h"
|
||||
#include "MQTTFormat.h"
|
||||
|
||||
int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char type, unsigned char dup, unsigned short packetid);
|
||||
int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen);
|
||||
|
||||
int MQTTPacket_len(int rem_len);
|
||||
int MQTTPacket_equals(MQTTString* a, char* b);
|
||||
|
||||
int MQTTPacket_encode(unsigned char* buf, int length);
|
||||
int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value);
|
||||
int MQTTPacket_decodeBuf(unsigned char* buf, int* value);
|
||||
|
||||
int readInt(unsigned char** pptr);
|
||||
char readChar(unsigned char** pptr);
|
||||
void writeChar(unsigned char** pptr, char c);
|
||||
void writeInt(unsigned char** pptr, int anInt);
|
||||
int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata);
|
||||
void writeCString(unsigned char** pptr, const char* string);
|
||||
void writeMQTTString(unsigned char** pptr, MQTTString mqttstring);
|
||||
|
||||
DLLExport int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int));
|
||||
|
||||
typedef struct {
|
||||
int (*getfn)(void *, unsigned char*, int); /* must return -1 for error, 0 for call again, or the number of bytes read */
|
||||
void *sck; /* pointer to whatever the system may use to identify the transport */
|
||||
int multiplier;
|
||||
int rem_len;
|
||||
int len;
|
||||
char state;
|
||||
}MQTTTransport;
|
||||
|
||||
int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp);
|
||||
|
||||
#ifdef __cplusplus /* If this is a C++ compiler, use C linkage */
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* MQTTPACKET_H_ */
|
|
@ -0,0 +1,38 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Xiang Rong - 442039 Add makefile to Embedded C client
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef MQTTPUBLISH_H_
|
||||
#define MQTTPUBLISH_H_
|
||||
|
||||
#if !defined(DLLImport)
|
||||
#define DLLImport
|
||||
#endif
|
||||
#if !defined(DLLExport)
|
||||
#define DLLExport
|
||||
#endif
|
||||
|
||||
DLLExport int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid,
|
||||
MQTTString topicName, unsigned char* payload, int payloadlen);
|
||||
|
||||
DLLExport int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName,
|
||||
unsigned char** payload, int* payloadlen, unsigned char* buf, int len);
|
||||
|
||||
DLLExport int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid);
|
||||
DLLExport int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid);
|
||||
DLLExport int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid);
|
||||
|
||||
#endif /* MQTTPUBLISH_H_ */
|
|
@ -0,0 +1,169 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=453144
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/**
|
||||
* Determines the length of the MQTT publish packet that would be produced using the supplied parameters
|
||||
* @param qos the MQTT QoS of the publish (packetid is omitted for QoS 0)
|
||||
* @param topicName the topic name to be used in the publish
|
||||
* @param payloadlen the length of the payload to be sent
|
||||
* @return the length of buffer needed to contain the serialized version of the packet
|
||||
*/
|
||||
int MQTTSerialize_publishLength(int qos, MQTTString topicName, int payloadlen)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
len += 2 + MQTTstrlen(topicName) + payloadlen;
|
||||
if (qos > 0)
|
||||
len += 2; /* packetid */
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the supplied publish data into the supplied buffer, ready for sending
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param dup integer - the MQTT dup flag
|
||||
* @param qos integer - the MQTT QoS value
|
||||
* @param retained integer - the MQTT retained flag
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @param topicName MQTTString - the MQTT topic in the publish
|
||||
* @param payload byte buffer - the MQTT publish payload
|
||||
* @param payloadlen integer - the length of the MQTT payload
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid,
|
||||
MQTTString topicName, unsigned char* payload, int payloadlen)
|
||||
{
|
||||
unsigned char *ptr = buf;
|
||||
MQTTHeader header = {0};
|
||||
int rem_len = 0;
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (MQTTPacket_len(rem_len = MQTTSerialize_publishLength(qos, topicName, payloadlen)) > buflen)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
header.bits.type = PUBLISH;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = qos;
|
||||
header.bits.retain = retained;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
|
||||
|
||||
writeMQTTString(&ptr, topicName);
|
||||
|
||||
if (qos > 0)
|
||||
writeInt(&ptr, packetid);
|
||||
|
||||
memcpy(ptr, payload, payloadlen);
|
||||
ptr += payloadlen;
|
||||
|
||||
rc = ptr - buf;
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the ack packet into the supplied buffer.
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param type the MQTT packet type
|
||||
* @param dup the MQTT dup flag
|
||||
* @param packetid the MQTT packet identifier
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char packettype, unsigned char dup, unsigned short packetid)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
int rc = 0;
|
||||
unsigned char *ptr = buf;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (buflen < 4)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
header.bits.type = packettype;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = (packettype == PUBREL) ? 1 : 0;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */
|
||||
writeInt(&ptr, packetid);
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes a puback packet into the supplied buffer.
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid)
|
||||
{
|
||||
return MQTTSerialize_ack(buf, buflen, PUBACK, 0, packetid);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes a pubrel packet into the supplied buffer.
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param dup integer - the MQTT dup flag
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid)
|
||||
{
|
||||
return MQTTSerialize_ack(buf, buflen, PUBREL, dup, packetid);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes a pubrel packet into the supplied buffer.
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @return serialized length, or error if 0
|
||||
*/
|
||||
int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid)
|
||||
{
|
||||
return MQTTSerialize_ack(buf, buflen, PUBCOMP, 0, packetid);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Xiang Rong - 442039 Add makefile to Embedded C client
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef MQTTSUBSCRIBE_H_
|
||||
#define MQTTSUBSCRIBE_H_
|
||||
|
||||
#if !defined(DLLImport)
|
||||
#define DLLImport
|
||||
#endif
|
||||
#if !defined(DLLExport)
|
||||
#define DLLExport
|
||||
#endif
|
||||
|
||||
DLLExport int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
|
||||
int count, MQTTString topicFilters[], int requestedQoSs[]);
|
||||
|
||||
DLLExport int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid,
|
||||
int maxcount, int* count, MQTTString topicFilters[], int requestedQoSs[], unsigned char* buf, int len);
|
||||
|
||||
DLLExport int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs);
|
||||
|
||||
DLLExport int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int len);
|
||||
|
||||
|
||||
#endif /* MQTTSUBSCRIBE_H_ */
|
|
@ -0,0 +1,137 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Determines the length of the MQTT subscribe packet that would be produced using the supplied parameters
|
||||
* @param count the number of topic filter strings in topicFilters
|
||||
* @param topicFilters the array of topic filter strings to be used in the publish
|
||||
* @return the length of buffer needed to contain the serialized version of the packet
|
||||
*/
|
||||
int MQTTSerialize_subscribeLength(int count, MQTTString topicFilters[])
|
||||
{
|
||||
int i;
|
||||
int len = 2; /* packetid */
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
len += 2 + MQTTstrlen(topicFilters[i]) + 1; /* length + topic + req_qos */
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the supplied subscribe data into the supplied buffer, ready for sending
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied bufferr
|
||||
* @param dup integer - the MQTT dup flag
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @param count - number of members in the topicFilters and reqQos arrays
|
||||
* @param topicFilters - array of topic filter names
|
||||
* @param requestedQoSs - array of requested QoS
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, int count,
|
||||
MQTTString topicFilters[], int requestedQoSs[])
|
||||
{
|
||||
unsigned char *ptr = buf;
|
||||
MQTTHeader header = {0};
|
||||
int rem_len = 0;
|
||||
int rc = 0;
|
||||
int i = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (MQTTPacket_len(rem_len = MQTTSerialize_subscribeLength(count, topicFilters)) > buflen)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
header.byte = 0;
|
||||
header.bits.type = SUBSCRIBE;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = 1;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
|
||||
|
||||
writeInt(&ptr, packetid);
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
writeMQTTString(&ptr, topicFilters[i]);
|
||||
writeChar(&ptr, requestedQoSs[i]);
|
||||
}
|
||||
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into suback data
|
||||
* @param packetid returned integer - the MQTT packet identifier
|
||||
* @param maxcount - the maximum number of members allowed in the grantedQoSs array
|
||||
* @param count returned integer - number of members in the grantedQoSs array
|
||||
* @param grantedQoSs returned array of integers - the granted qualities of service
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @return error code. 1 is success, 0 is failure
|
||||
*/
|
||||
int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int buflen)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = NULL;
|
||||
int rc = 0;
|
||||
int mylen;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
if (header.bits.type != SUBACK)
|
||||
goto exit;
|
||||
|
||||
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||
enddata = curdata + mylen;
|
||||
if (enddata - curdata < 2)
|
||||
goto exit;
|
||||
|
||||
*packetid = readInt(&curdata);
|
||||
|
||||
*count = 0;
|
||||
while (curdata < enddata)
|
||||
{
|
||||
if (*count > maxcount)
|
||||
{
|
||||
rc = -1;
|
||||
goto exit;
|
||||
}
|
||||
grantedQoSs[(*count)++] = readChar(&curdata);
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into subscribe data
|
||||
* @param dup integer returned - the MQTT dup flag
|
||||
* @param packetid integer returned - the MQTT packet identifier
|
||||
* @param maxcount - the maximum number of members allowed in the topicFilters and requestedQoSs arrays
|
||||
* @param count - number of members in the topicFilters and requestedQoSs arrays
|
||||
* @param topicFilters - array of topic filter names
|
||||
* @param requestedQoSs - array of requested QoS
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[],
|
||||
int requestedQoSs[], unsigned char* buf, int buflen)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = NULL;
|
||||
int rc = -1;
|
||||
int mylen = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
if (header.bits.type != SUBSCRIBE)
|
||||
goto exit;
|
||||
*dup = header.bits.dup;
|
||||
|
||||
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||
enddata = curdata + mylen;
|
||||
|
||||
*packetid = readInt(&curdata);
|
||||
|
||||
*count = 0;
|
||||
while (curdata < enddata)
|
||||
{
|
||||
if (!readMQTTLenString(&topicFilters[*count], &curdata, enddata))
|
||||
goto exit;
|
||||
if (curdata >= enddata) /* do we have enough data to read the req_qos version byte? */
|
||||
goto exit;
|
||||
requestedQoSs[*count] = readChar(&curdata);
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the supplied suback data into the supplied buffer, ready for sending
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @param count - number of members in the grantedQoSs array
|
||||
* @param grantedQoSs - array of granted QoS
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
int rc = -1;
|
||||
unsigned char *ptr = buf;
|
||||
int i;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (buflen < 2 + count)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
header.byte = 0;
|
||||
header.bits.type = SUBACK;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, 2 + count); /* write remaining length */
|
||||
|
||||
writeInt(&ptr, packetid);
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
writeChar(&ptr, grantedQoSs[i]);
|
||||
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Xiang Rong - 442039 Add makefile to Embedded C client
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef MQTTUNSUBSCRIBE_H_
|
||||
#define MQTTUNSUBSCRIBE_H_
|
||||
|
||||
#if !defined(DLLImport)
|
||||
#define DLLImport
|
||||
#endif
|
||||
#if !defined(DLLExport)
|
||||
#define DLLExport
|
||||
#endif
|
||||
|
||||
DLLExport int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
|
||||
int count, MQTTString topicFilters[]);
|
||||
|
||||
DLLExport int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int max_count, int* count, MQTTString topicFilters[],
|
||||
unsigned char* buf, int len);
|
||||
|
||||
DLLExport int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid);
|
||||
|
||||
DLLExport int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int len);
|
||||
|
||||
#endif /* MQTTUNSUBSCRIBE_H_ */
|
|
@ -0,0 +1,106 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Determines the length of the MQTT unsubscribe packet that would be produced using the supplied parameters
|
||||
* @param count the number of topic filter strings in topicFilters
|
||||
* @param topicFilters the array of topic filter strings to be used in the publish
|
||||
* @return the length of buffer needed to contain the serialized version of the packet
|
||||
*/
|
||||
int MQTTSerialize_unsubscribeLength(int count, MQTTString topicFilters[])
|
||||
{
|
||||
int i;
|
||||
int len = 2; /* packetid */
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
len += 2 + MQTTstrlen(topicFilters[i]); /* length + topic*/
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the supplied unsubscribe data into the supplied buffer, ready for sending
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @param dup integer - the MQTT dup flag
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @param count - number of members in the topicFilters array
|
||||
* @param topicFilters - array of topic filter names
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
|
||||
int count, MQTTString topicFilters[])
|
||||
{
|
||||
unsigned char *ptr = buf;
|
||||
MQTTHeader header = {0};
|
||||
int rem_len = 0;
|
||||
int rc = -1;
|
||||
int i = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (MQTTPacket_len(rem_len = MQTTSerialize_unsubscribeLength(count, topicFilters)) > buflen)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
header.byte = 0;
|
||||
header.bits.type = UNSUBSCRIBE;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = 1;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
|
||||
|
||||
writeInt(&ptr, packetid);
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
writeMQTTString(&ptr, topicFilters[i]);
|
||||
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into unsuback data
|
||||
* @param packetid returned integer - the MQTT packet identifier
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @return error code. 1 is success, 0 is failure
|
||||
*/
|
||||
int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int buflen)
|
||||
{
|
||||
unsigned char type = 0;
|
||||
unsigned char dup = 0;
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
rc = MQTTDeserialize_ack(&type, &dup, packetid, buf, buflen);
|
||||
if (type == UNSUBACK)
|
||||
rc = 1;
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes the supplied (wire) buffer into unsubscribe data
|
||||
* @param dup integer returned - the MQTT dup flag
|
||||
* @param packetid integer returned - the MQTT packet identifier
|
||||
* @param maxcount - the maximum number of members allowed in the topicFilters and requestedQoSs arrays
|
||||
* @param count - number of members in the topicFilters and requestedQoSs arrays
|
||||
* @param topicFilters - array of topic filter names
|
||||
* @param buf the raw buffer data, of the correct length determined by the remaining length field
|
||||
* @param buflen the length in bytes of the data in the supplied buffer
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[],
|
||||
unsigned char* buf, int len)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
unsigned char* curdata = buf;
|
||||
unsigned char* enddata = NULL;
|
||||
int rc = 0;
|
||||
int mylen = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = readChar(&curdata);
|
||||
if (header.bits.type != UNSUBSCRIBE)
|
||||
goto exit;
|
||||
*dup = header.bits.dup;
|
||||
|
||||
curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
|
||||
enddata = curdata + mylen;
|
||||
|
||||
*packetid = readInt(&curdata);
|
||||
|
||||
*count = 0;
|
||||
while (curdata < enddata)
|
||||
{
|
||||
if (!readMQTTLenString(&topicFilters[*count], &curdata, enddata))
|
||||
goto exit;
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the supplied unsuback data into the supplied buffer, ready for sending
|
||||
* @param buf the buffer into which the packet will be serialized
|
||||
* @param buflen the length in bytes of the supplied buffer
|
||||
* @param packetid integer - the MQTT packet identifier
|
||||
* @return the length of the serialized data. <= 0 indicates error
|
||||
*/
|
||||
int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid)
|
||||
{
|
||||
MQTTHeader header = {0};
|
||||
int rc = 0;
|
||||
unsigned char *ptr = buf;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (buflen < 2)
|
||||
{
|
||||
rc = MQTTPACKET_BUFFER_TOO_SHORT;
|
||||
goto exit;
|
||||
}
|
||||
header.byte = 0;
|
||||
header.bits.type = UNSUBACK;
|
||||
writeChar(&ptr, header.byte); /* write header */
|
||||
|
||||
ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */
|
||||
|
||||
writeInt(&ptr, packetid);
|
||||
|
||||
rc = ptr - buf;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - fix for bug #434081
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef STACKTRACE_H_
|
||||
#define STACKTRACE_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#define NOSTACKTRACE 1
|
||||
|
||||
#if defined(NOSTACKTRACE)
|
||||
#define FUNC_ENTRY
|
||||
#define FUNC_ENTRY_NOLOG
|
||||
#define FUNC_ENTRY_MED
|
||||
#define FUNC_ENTRY_MAX
|
||||
#define FUNC_EXIT
|
||||
#define FUNC_EXIT_NOLOG
|
||||
#define FUNC_EXIT_MED
|
||||
#define FUNC_EXIT_MAX
|
||||
#define FUNC_EXIT_RC(x)
|
||||
#define FUNC_EXIT_MED_RC(x)
|
||||
#define FUNC_EXIT_MAX_RC(x)
|
||||
|
||||
#else
|
||||
|
||||
#if defined(WIN32)
|
||||
#define inline __inline
|
||||
#define FUNC_ENTRY StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MINIMUM)
|
||||
#define FUNC_ENTRY_NOLOG StackTrace_entry(__FUNCTION__, __LINE__, -1)
|
||||
#define FUNC_ENTRY_MED StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MEDIUM)
|
||||
#define FUNC_ENTRY_MAX StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MAXIMUM)
|
||||
#define FUNC_EXIT StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MINIMUM)
|
||||
#define FUNC_EXIT_NOLOG StackTrace_exit(__FUNCTION__, __LINE__, -1)
|
||||
#define FUNC_EXIT_MED StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MEDIUM)
|
||||
#define FUNC_EXIT_MAX StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MAXIMUM)
|
||||
#define FUNC_EXIT_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MINIMUM)
|
||||
#define FUNC_EXIT_MED_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MEDIUM)
|
||||
#define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MAXIMUM)
|
||||
#else
|
||||
#define FUNC_ENTRY StackTrace_entry(__func__, __LINE__, TRACE_MINIMUM)
|
||||
#define FUNC_ENTRY_NOLOG StackTrace_entry(__func__, __LINE__, -1)
|
||||
#define FUNC_ENTRY_MED StackTrace_entry(__func__, __LINE__, TRACE_MEDIUM)
|
||||
#define FUNC_ENTRY_MAX StackTrace_entry(__func__, __LINE__, TRACE_MAXIMUM)
|
||||
#define FUNC_EXIT StackTrace_exit(__func__, __LINE__, NULL, TRACE_MINIMUM)
|
||||
#define FUNC_EXIT_NOLOG StackTrace_exit(__func__, __LINE__, NULL, -1)
|
||||
#define FUNC_EXIT_MED StackTrace_exit(__func__, __LINE__, NULL, TRACE_MEDIUM)
|
||||
#define FUNC_EXIT_MAX StackTrace_exit(__func__, __LINE__, NULL, TRACE_MAXIMUM)
|
||||
#define FUNC_EXIT_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MINIMUM)
|
||||
#define FUNC_EXIT_MED_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MEDIUM)
|
||||
#define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MAXIMUM)
|
||||
|
||||
void StackTrace_entry(const char* name, int line, int trace);
|
||||
void StackTrace_exit(const char* name, int line, void* return_value, int trace);
|
||||
|
||||
void StackTrace_printStack(FILE* dest);
|
||||
char* StackTrace_get(unsigned long);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* STACKTRACE_H_ */
|
|
@ -0,0 +1 @@
|
|||
gcc -Wall test1.c -o test1 -I../src ../src/MQTTConnectClient.c ../src/MQTTConnectServer.c ../src/MQTTPacket.c ../src/MQTTSerializePublish.c ../src/MQTTDeserializePublish.c ../src/MQTTSubscribeServer.c ../src/MQTTSubscribeClient.c ../src/MQTTUnsubscribeServer.c ../src/MQTTUnsubscribeClient.c
|
|
@ -0,0 +1,635 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if !defined(_WINDOWS)
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#else
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#define MAXHOSTNAMELEN 256
|
||||
#define EAGAIN WSAEWOULDBLOCK
|
||||
#define EINTR WSAEINTR
|
||||
#define EINPROGRESS WSAEINPROGRESS
|
||||
#define EWOULDBLOCK WSAEWOULDBLOCK
|
||||
#define ENOTCONN WSAENOTCONN
|
||||
#define ECONNRESET WSAECONNRESET
|
||||
#endif
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||
|
||||
struct Options
|
||||
{
|
||||
char* connection; /**< connection to system under test. */
|
||||
char** haconnections;
|
||||
int hacount;
|
||||
int verbose;
|
||||
int test_no;
|
||||
} options =
|
||||
{
|
||||
"tcp://m2m.eclipse.org:1883",
|
||||
NULL,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
};
|
||||
|
||||
void usage()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void getopts(int argc, char** argv)
|
||||
{
|
||||
int count = 1;
|
||||
|
||||
while (count < argc)
|
||||
{
|
||||
if (strcmp(argv[count], "--test_no") == 0)
|
||||
{
|
||||
if (++count < argc)
|
||||
options.test_no = atoi(argv[count]);
|
||||
else
|
||||
usage();
|
||||
}
|
||||
else if (strcmp(argv[count], "--connection") == 0)
|
||||
{
|
||||
if (++count < argc)
|
||||
{
|
||||
options.connection = argv[count];
|
||||
printf("\nSetting connection to %s\n", options.connection);
|
||||
}
|
||||
else
|
||||
usage();
|
||||
}
|
||||
else if (strcmp(argv[count], "--haconnections") == 0)
|
||||
{
|
||||
if (++count < argc)
|
||||
{
|
||||
char* tok = strtok(argv[count], " ");
|
||||
options.hacount = 0;
|
||||
options.haconnections = malloc(sizeof(char*) * 5);
|
||||
while (tok)
|
||||
{
|
||||
options.haconnections[options.hacount] = malloc(strlen(tok) + 1);
|
||||
strcpy(options.haconnections[options.hacount], tok);
|
||||
options.hacount++;
|
||||
tok = strtok(NULL, " ");
|
||||
}
|
||||
}
|
||||
else
|
||||
usage();
|
||||
}
|
||||
else if (strcmp(argv[count], "--verbose") == 0)
|
||||
{
|
||||
options.verbose = 1;
|
||||
printf("\nSetting verbose on\n");
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define LOGA_DEBUG 0
|
||||
#define LOGA_INFO 1
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#include <sys/timeb.h>
|
||||
void MyLog(int LOGA_level, char* format, ...)
|
||||
{
|
||||
static char msg_buf[256];
|
||||
va_list args;
|
||||
struct timeb ts;
|
||||
|
||||
struct tm *timeinfo;
|
||||
|
||||
if (LOGA_level == LOGA_DEBUG && options.verbose == 0)
|
||||
return;
|
||||
|
||||
ftime(&ts);
|
||||
timeinfo = localtime(&ts.time);
|
||||
strftime(msg_buf, 80, "%Y%m%d %H%M%S", timeinfo);
|
||||
|
||||
sprintf(&msg_buf[strlen(msg_buf)], ".%.3hu ", ts.millitm);
|
||||
|
||||
va_start(args, format);
|
||||
vsnprintf(&msg_buf[strlen(msg_buf)], sizeof(msg_buf) - strlen(msg_buf), format, args);
|
||||
va_end(args);
|
||||
|
||||
printf("%s\n", msg_buf);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
|
||||
#if defined(WIN32) || defined(_WINDOWS)
|
||||
#define mqsleep(A) Sleep(1000*A)
|
||||
#define START_TIME_TYPE DWORD
|
||||
static DWORD start_time = 0;
|
||||
START_TIME_TYPE start_clock(void)
|
||||
{
|
||||
return GetTickCount();
|
||||
}
|
||||
#elif defined(AIX)
|
||||
#define mqsleep sleep
|
||||
#define START_TIME_TYPE struct timespec
|
||||
START_TIME_TYPE start_clock(void)
|
||||
{
|
||||
static struct timespec start;
|
||||
clock_gettime(CLOCK_REALTIME, &start);
|
||||
return start;
|
||||
}
|
||||
#else
|
||||
#define mqsleep sleep
|
||||
#define START_TIME_TYPE struct timeval
|
||||
/* TODO - unused - remove? static struct timeval start_time; */
|
||||
START_TIME_TYPE start_clock(void)
|
||||
{
|
||||
struct timeval start_time;
|
||||
gettimeofday(&start_time, NULL);
|
||||
return start_time;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(WIN32)
|
||||
long elapsed(START_TIME_TYPE start_time)
|
||||
{
|
||||
return GetTickCount() - start_time;
|
||||
}
|
||||
#elif defined(AIX)
|
||||
#define assert(a)
|
||||
long elapsed(struct timespec start)
|
||||
{
|
||||
struct timespec now, res;
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
ntimersub(now, start, res);
|
||||
return (res.tv_sec)*1000L + (res.tv_nsec)/1000000L;
|
||||
}
|
||||
#else
|
||||
long elapsed(START_TIME_TYPE start_time)
|
||||
{
|
||||
struct timeval now, res;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
timersub(&now, &start_time, &res);
|
||||
return (res.tv_sec)*1000 + (res.tv_usec)/1000;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#define assert(a, b, c, d) myassert(__FILE__, __LINE__, a, b, c, d)
|
||||
#define assert1(a, b, c, d, e) myassert(__FILE__, __LINE__, a, b, c, d, e)
|
||||
|
||||
int tests = 0;
|
||||
int failures = 0;
|
||||
FILE* xml;
|
||||
START_TIME_TYPE global_start_time;
|
||||
char output[3000];
|
||||
char* cur_output = output;
|
||||
|
||||
|
||||
void write_test_result()
|
||||
{
|
||||
long duration = elapsed(global_start_time);
|
||||
|
||||
fprintf(xml, " time=\"%ld.%.3ld\" >\n", duration / 1000, duration % 1000);
|
||||
if (cur_output != output)
|
||||
{
|
||||
fprintf(xml, "%s", output);
|
||||
cur_output = output;
|
||||
}
|
||||
fprintf(xml, "</testcase>\n");
|
||||
}
|
||||
|
||||
|
||||
void myassert(char* filename, int lineno, char* description, int value, char* format, ...)
|
||||
{
|
||||
++tests;
|
||||
if (!value)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
++failures;
|
||||
printf("Assertion failed, file %s, line %d, description: %s\n", filename, lineno, description);
|
||||
|
||||
va_start(args, format);
|
||||
vprintf(format, args);
|
||||
va_end(args);
|
||||
|
||||
cur_output += sprintf(cur_output, "<failure type=\"%s\">file %s, line %d </failure>\n",
|
||||
description, filename, lineno);
|
||||
}
|
||||
else
|
||||
MyLog(LOGA_DEBUG, "Assertion succeeded, file %s, line %d, description: %s", filename, lineno, description);
|
||||
}
|
||||
|
||||
#define min(a, b) ((a < b) ? a : b)
|
||||
|
||||
int checkMQTTStrings(MQTTString a, MQTTString b)
|
||||
{
|
||||
if (!a.lenstring.data)
|
||||
{
|
||||
a.lenstring.data = a.cstring;
|
||||
if (a.cstring)
|
||||
a.lenstring.len = strlen(a.cstring);
|
||||
}
|
||||
if (!b.lenstring.data)
|
||||
{
|
||||
b.lenstring.data = b.cstring;
|
||||
if (b.cstring)
|
||||
b.lenstring.len = strlen(b.cstring);
|
||||
}
|
||||
return memcmp(a.lenstring.data, b.lenstring.data, min(a.lenstring.len, b.lenstring.len)) == 0;
|
||||
}
|
||||
|
||||
|
||||
int checkConnectPackets(MQTTPacket_connectData* before, MQTTPacket_connectData* after)
|
||||
{
|
||||
int rc = 0;
|
||||
int start_failures = failures;
|
||||
|
||||
assert("struct_ids should be the same",
|
||||
memcmp(before->struct_id, after->struct_id, 4) == 0, "struct_ids were different %.4s\n", after->struct_id);
|
||||
|
||||
assert("struct_versions should be the same",
|
||||
before->struct_version == after->struct_version, "struct_versions were different\n", rc);
|
||||
|
||||
assert("MQTT versions should be the same",
|
||||
before->MQTTVersion == after->MQTTVersion, "MQTT versions were different\n", rc);
|
||||
|
||||
assert("ClientIDs should be the same",
|
||||
checkMQTTStrings(before->clientID, after->clientID), "ClientIDs were different\n", rc);
|
||||
|
||||
assert("keepAliveIntervals should be the same",
|
||||
before->keepAliveInterval == after->keepAliveInterval, "keepAliveIntervals were different %d\n", after->keepAliveInterval);
|
||||
|
||||
assert("cleansessions should be the same",
|
||||
before->cleansession == after->cleansession, "cleansessions were different\n", rc);
|
||||
|
||||
assert("willFlags should be the same",
|
||||
before->willFlag == after->willFlag, "willFlags were different\n", rc);
|
||||
|
||||
if (before->willFlag)
|
||||
{
|
||||
assert("will struct_ids should be the same",
|
||||
memcmp(before->will.struct_id, after->will.struct_id, 4) == 0, "will struct_ids were different %.4s\n", after->struct_id);
|
||||
|
||||
assert("will struct_versions should be the same",
|
||||
before->will.struct_version == after->will.struct_version, "will struct_versions were different\n", rc);
|
||||
|
||||
assert("topic names should be the same",
|
||||
checkMQTTStrings(before->will.topicName, after->will.topicName), "topic names were different\n", rc);
|
||||
|
||||
assert("messages should be the same",
|
||||
checkMQTTStrings(before->will.message, after->will.message), "messages were different\n", rc);
|
||||
|
||||
assert("retained flags should be the same",
|
||||
before->will.retained == after->will.retained, "retained flags were different\n", rc);
|
||||
|
||||
assert("will qos should be the same",
|
||||
before->will.qos == after->will.qos, "will qos were different\n", rc);
|
||||
}
|
||||
|
||||
assert("usernames should be the same",
|
||||
checkMQTTStrings(before->clientID, after->clientID), "usernames were different\n", rc);
|
||||
assert("passwords should be the same",
|
||||
checkMQTTStrings(before->password, after->password), "passwords were different\n", rc);
|
||||
return failures == start_failures;
|
||||
}
|
||||
|
||||
int test1(struct Options options)
|
||||
{
|
||||
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
|
||||
MQTTPacket_connectData data_after = MQTTPacket_connectData_initializer;
|
||||
int rc = 0;
|
||||
unsigned char buf[100];
|
||||
int buflen = sizeof(buf);
|
||||
|
||||
fprintf(xml, "<testcase classname=\"test1\" name=\"de/serialization\"");
|
||||
global_start_time = start_clock();
|
||||
failures = 0;
|
||||
MyLog(LOGA_INFO, "Starting test 1 - serialization of connect and back");
|
||||
|
||||
data.clientID.cstring = "me";
|
||||
|
||||
data.keepAliveInterval = 20;
|
||||
data.cleansession = 1;
|
||||
data.username.cstring = "testuser";
|
||||
data.password.cstring = "testpassword";
|
||||
|
||||
data.willFlag = 1;
|
||||
data.will.message.cstring = "will message";
|
||||
data.will.qos = 1;
|
||||
data.will.retained = 0;
|
||||
data.will.topicName.cstring = "will topic";
|
||||
|
||||
rc = MQTTSerialize_connect(buf, buflen, &data);
|
||||
assert("good rc from serialize connect", rc > 0, "rc was %d\n", rc);
|
||||
|
||||
rc = MQTTDeserialize_connect(&data_after, buf, buflen);
|
||||
assert("good rc from deserialize connect", rc == 1, "rc was %d\n", rc);
|
||||
|
||||
/* data after should be the same as data before */
|
||||
rc = checkConnectPackets(&data, &data_after);
|
||||
assert("packets should be the same", rc == 1, "packets were different\n", rc);
|
||||
|
||||
/* exit: */
|
||||
MyLog(LOGA_INFO, "TEST1: test %s. %d tests run, %d failures.",
|
||||
(failures == 0) ? "passed" : "failed", tests, failures);
|
||||
write_test_result();
|
||||
return failures;
|
||||
}
|
||||
|
||||
|
||||
int test2(struct Options options)
|
||||
{
|
||||
int rc = 0;
|
||||
unsigned char buf[100];
|
||||
int buflen = sizeof(buf);
|
||||
|
||||
unsigned char dup = 0;
|
||||
int qos = 2;
|
||||
unsigned char retained = 0;
|
||||
unsigned short msgid = 23;
|
||||
MQTTString topicString = MQTTString_initializer;
|
||||
unsigned char *payload = (unsigned char*)"kkhkhkjkj jkjjk jk jk ";
|
||||
int payloadlen = strlen((char*)payload);
|
||||
|
||||
unsigned char dup2 = 1;
|
||||
int qos2 = 1;
|
||||
unsigned char retained2 = 1;
|
||||
unsigned short msgid2 = 3243;
|
||||
MQTTString topicString2 = MQTTString_initializer;
|
||||
unsigned char *payload2 = NULL;
|
||||
int payloadlen2 = 0;
|
||||
|
||||
fprintf(xml, "<testcase classname=\"test1\" name=\"de/serialization\"");
|
||||
global_start_time = start_clock();
|
||||
failures = 0;
|
||||
MyLog(LOGA_INFO, "Starting test 2 - serialization of publish and back");
|
||||
|
||||
topicString.cstring = "mytopic";
|
||||
rc = MQTTSerialize_publish(buf, buflen, dup, qos, retained, msgid, topicString,
|
||||
payload, payloadlen);
|
||||
assert("good rc from serialize publish", rc > 0, "rc was %d\n", rc);
|
||||
|
||||
rc = MQTTDeserialize_publish(&dup2, &qos2, &retained2, &msgid2, &topicString2,
|
||||
&payload2, &payloadlen2, buf, buflen);
|
||||
assert("good rc from deserialize publish", rc == 1, "rc was %d\n", rc);
|
||||
|
||||
/* data after should be the same as data before */
|
||||
assert("dups should be the same", dup == dup2, "dups were different %d\n", dup2);
|
||||
assert("qoss should be the same", qos == qos2, "qoss were different %d\n", qos2);
|
||||
assert("retaineds should be the same", retained == retained2, "retaineds were different %d\n", retained2);
|
||||
assert("msgids should be the same", msgid == msgid2, "msgids were different %d\n", msgid2);
|
||||
|
||||
assert("topics should be the same",
|
||||
checkMQTTStrings(topicString, topicString2), "topics were different %s\n", ""); //topicString2);
|
||||
|
||||
assert("payload lengths should be the same",
|
||||
payloadlen == payloadlen2, "payload lengths were different %d\n", payloadlen2);
|
||||
|
||||
assert("payloads should be the same",
|
||||
memcmp(payload, payload2, payloadlen) == 0, "payloads were different %s\n", "");
|
||||
|
||||
/*exit:*/
|
||||
MyLog(LOGA_INFO, "TEST2: test %s. %d tests run, %d failures.",
|
||||
(failures == 0) ? "passed" : "failed", tests, failures);
|
||||
write_test_result();
|
||||
return failures;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int test3(struct Options options)
|
||||
{
|
||||
int i = 0;
|
||||
int rc = 0;
|
||||
unsigned char buf[100];
|
||||
int buflen = sizeof(buf);
|
||||
#define TOPIC_COUNT 2
|
||||
|
||||
unsigned char dup = 0;
|
||||
unsigned short msgid = 23;
|
||||
int count = TOPIC_COUNT;
|
||||
MQTTString topicStrings[TOPIC_COUNT] = { MQTTString_initializer, MQTTString_initializer };
|
||||
int req_qoss[TOPIC_COUNT] = {2, 1};
|
||||
|
||||
unsigned char dup2 = 1;
|
||||
unsigned short msgid2 = 2223;
|
||||
int count2 = 0;
|
||||
MQTTString topicStrings2[TOPIC_COUNT] = { MQTTString_initializer, MQTTString_initializer };
|
||||
int req_qoss2[TOPIC_COUNT] = {0, 0};
|
||||
|
||||
fprintf(xml, "<testcase classname=\"test1\" name=\"de/serialization\"");
|
||||
global_start_time = start_clock();
|
||||
failures = 0;
|
||||
MyLog(LOGA_INFO, "Starting test 2 - serialization of subscribe and back");
|
||||
|
||||
topicStrings[0].cstring = "mytopic";
|
||||
topicStrings[1].cstring = "mytopic2";
|
||||
rc = MQTTSerialize_subscribe(buf, buflen, dup, msgid, count, topicStrings, req_qoss);
|
||||
assert("good rc from serialize subscribe", rc > 0, "rc was %d\n", rc);
|
||||
|
||||
rc = MQTTDeserialize_subscribe(&dup2, &msgid2, 2, &count2, topicStrings2, req_qoss2, buf, buflen);
|
||||
assert("good rc from deserialize subscribe", rc == 1, "rc was %d\n", rc);
|
||||
|
||||
/* data after should be the same as data before */
|
||||
assert("dups should be the same", dup == dup2, "dups were different %d\n", dup2);
|
||||
assert("msgids should be the same", msgid == msgid2, "msgids were different %d\n", msgid2);
|
||||
|
||||
assert("count should be the same", count == count2, "counts were different %d\n", count2);
|
||||
|
||||
for (i = 0; i < count2; ++i)
|
||||
{
|
||||
assert("topics should be the same",
|
||||
checkMQTTStrings(topicStrings[i], topicStrings2[i]), "topics were different %s\n", "");
|
||||
|
||||
assert("qoss should be the same", req_qoss[i] == req_qoss2[i], "qoss were different %d\n", req_qoss2[i]);
|
||||
}
|
||||
|
||||
/*exit:*/
|
||||
MyLog(LOGA_INFO, "TEST3: test %s. %d tests run, %d failures.",
|
||||
(failures == 0) ? "passed" : "failed", tests, failures);
|
||||
write_test_result();
|
||||
return failures;
|
||||
}
|
||||
|
||||
|
||||
int test4(struct Options options)
|
||||
{
|
||||
int i = 0;
|
||||
int rc = 0;
|
||||
unsigned char buf[100];
|
||||
int buflen = sizeof(buf);
|
||||
#define TOPIC_COUNT 2
|
||||
|
||||
int msgid = 23;
|
||||
int count = TOPIC_COUNT;
|
||||
int granted_qoss[TOPIC_COUNT] = {2, 1};
|
||||
|
||||
unsigned short msgid2 = 2223;
|
||||
int count2 = 0;
|
||||
int granted_qoss2[TOPIC_COUNT] = {0, 0};
|
||||
|
||||
fprintf(xml, "<testcase classname=\"test1\" name=\"de/serialization\"");
|
||||
global_start_time = start_clock();
|
||||
failures = 0;
|
||||
MyLog(LOGA_INFO, "Starting test 4 - serialization of suback and back");
|
||||
|
||||
rc = MQTTSerialize_suback(buf, buflen, msgid, count, granted_qoss);
|
||||
assert("good rc from serialize suback", rc > 0, "rc was %d\n", rc);
|
||||
|
||||
rc = MQTTDeserialize_suback(&msgid2, 2, &count2, granted_qoss2, buf, buflen);
|
||||
assert("good rc from deserialize suback", rc == 1, "rc was %d\n", rc);
|
||||
|
||||
/* data after should be the same as data before */
|
||||
assert("msgids should be the same", msgid == msgid2, "msgids were different %d\n", msgid2);
|
||||
|
||||
assert("count should be the same", count == count2, "counts were different %d\n", count2);
|
||||
|
||||
for (i = 0; i < count2; ++i)
|
||||
assert("qoss should be the same", granted_qoss[i] == granted_qoss2[i], "qoss were different %d\n", granted_qoss2[i]);
|
||||
|
||||
/* exit: */
|
||||
MyLog(LOGA_INFO, "TEST4: test %s. %d tests run, %d failures.",
|
||||
(failures == 0) ? "passed" : "failed", tests, failures);
|
||||
write_test_result();
|
||||
return failures;
|
||||
}
|
||||
|
||||
|
||||
int test5(struct Options options)
|
||||
{
|
||||
int i = 0;
|
||||
int rc = 0;
|
||||
unsigned char buf[100];
|
||||
int buflen = sizeof(buf);
|
||||
#define TOPIC_COUNT 2
|
||||
|
||||
unsigned char dup = 0;
|
||||
unsigned short msgid = 23;
|
||||
int count = TOPIC_COUNT;
|
||||
MQTTString topicStrings[TOPIC_COUNT] = { MQTTString_initializer, MQTTString_initializer };
|
||||
|
||||
unsigned char dup2 = 1;
|
||||
unsigned short msgid2 = 2223;
|
||||
int count2 = 0;
|
||||
MQTTString topicStrings2[TOPIC_COUNT] = { MQTTString_initializer, MQTTString_initializer };
|
||||
|
||||
fprintf(xml, "<testcase classname=\"test1\" name=\"de/serialization\"");
|
||||
global_start_time = start_clock();
|
||||
failures = 0;
|
||||
MyLog(LOGA_INFO, "Starting test 2 - serialization of unsubscribe and back");
|
||||
|
||||
topicStrings[0].cstring = "mytopic";
|
||||
topicStrings[1].cstring = "mytopic2";
|
||||
rc = MQTTSerialize_unsubscribe(buf, buflen, dup, msgid, count, topicStrings);
|
||||
assert("good rc from serialize unsubscribe", rc > 0, "rc was %d\n", rc);
|
||||
|
||||
rc = MQTTDeserialize_unsubscribe(&dup2, &msgid2, 2, &count2, topicStrings2, buf, buflen);
|
||||
assert("good rc from deserialize unsubscribe", rc == 1, "rc was %d\n", rc);
|
||||
|
||||
/* data after should be the same as data before */
|
||||
assert("dups should be the same", dup == dup2, "dups were different %d\n", dup2);
|
||||
assert("msgids should be the same", msgid == msgid2, "msgids were different %d\n", msgid2);
|
||||
|
||||
assert("count should be the same", count == count2, "counts were different %d\n", count2);
|
||||
|
||||
for (i = 0; i < count2; ++i)
|
||||
assert("topics should be the same",
|
||||
checkMQTTStrings(topicStrings[i], topicStrings2[i]), "topics were different %s\n", "");
|
||||
|
||||
/* exit: */
|
||||
MyLog(LOGA_INFO, "TEST5: test %s. %d tests run, %d failures.",
|
||||
(failures == 0) ? "passed" : "failed", tests, failures);
|
||||
write_test_result();
|
||||
return failures;
|
||||
}
|
||||
|
||||
|
||||
int test6(struct Options options)
|
||||
{
|
||||
int rc = 0;
|
||||
unsigned char buf[100];
|
||||
int buflen = sizeof(buf);
|
||||
|
||||
unsigned char sessionPresent = 1;
|
||||
unsigned char connack_rc = 77;
|
||||
|
||||
unsigned char sessionPresent2 = 0;
|
||||
unsigned char connack_rc2 = 0;
|
||||
|
||||
fprintf(xml, "<testcase classname=\"test1\" name=\"de/serialization\"");
|
||||
global_start_time = start_clock();
|
||||
failures = 0;
|
||||
MyLog(LOGA_INFO, "Starting test 2 - serialization of connack and back");
|
||||
|
||||
rc = MQTTSerialize_connack(buf, buflen, connack_rc, sessionPresent);
|
||||
assert("good rc from serialize connack", rc > 0, "rc was %d\n", rc);
|
||||
|
||||
rc = MQTTDeserialize_connack(&sessionPresent2, &connack_rc2, buf, buflen);
|
||||
assert("good rc from deserialize connack", rc == 1, "rc was %d\n", rc);
|
||||
|
||||
/* data after should be the same as data before */
|
||||
assert("connack rcs should be the same", connack_rc == connack_rc2, "connack rcs were different %d\n", connack_rc2);
|
||||
assert("session present flags should be the same", sessionPresent == sessionPresent2,
|
||||
"session present flags were different %d\n", sessionPresent2);
|
||||
|
||||
/* exit: */
|
||||
MyLog(LOGA_INFO, "TEST6: test %s. %d tests run, %d failures.",
|
||||
(failures == 0) ? "passed" : "failed", tests, failures);
|
||||
write_test_result();
|
||||
return failures;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int rc = 0;
|
||||
int (*tests[])() = {NULL, test1, test2, test3, test4, test5, test6};
|
||||
|
||||
xml = fopen("TEST-test1.xml", "w");
|
||||
fprintf(xml, "<testsuite name=\"test1\" tests=\"%d\">\n", (int)(ARRAY_SIZE(tests) - 1));
|
||||
|
||||
getopts(argc, argv);
|
||||
|
||||
if (options.test_no == 0)
|
||||
{ /* run all the tests */
|
||||
for (options.test_no = 1; options.test_no < ARRAY_SIZE(tests); ++options.test_no)
|
||||
rc += tests[options.test_no](options); /* return number of failures. 0 = test succeeded */
|
||||
}
|
||||
else
|
||||
rc = tests[options.test_no](options); /* run just the selected test */
|
||||
|
||||
if (rc == 0)
|
||||
MyLog(LOGA_INFO, "verdict pass");
|
||||
else
|
||||
MyLog(LOGA_INFO, "verdict fail");
|
||||
|
||||
fprintf(xml, "</testsuite>\n");
|
||||
fclose(xml);
|
||||
return rc;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
# Eclipse Paho MQTT C/C++ client for Embedded platforms
|
||||
|
||||
This repository contains the source code for the [Eclipse Paho](http://eclipse.org/paho) MQTT C/C++ client library for Embedded platorms.
|
||||
|
||||
It is dual licensed under the EPL and EDL (see about.html and notice.html for more details). You can choose which of these licenses you want to use the code under. The EDL allows you to embed the code into your application, and distribute your application in binary or source form without contributing any of your code, or any changes you make back to Paho. See the EDL for the exact conditions.
|
||||
|
||||
The MQTTPacket directory contains the lowest level C library with the smallest requirements. This supplies simple serialization
|
||||
and deserialization routines. It is mainly up to you to write and read to and from the network.
|
||||
|
||||
The MQTTClient directory contains the next level C++ library. This still avoids most networking code so that you can plugin the
|
||||
network of your choice.
|
||||
|
||||
## Build requirements / compilation
|
||||
|
||||
There are helper scripts (build...) in various directories. The client library is a set of building blocks which you pick and choose from, so that the smallest MQTT application can be built.
|
||||
|
||||
## Usage and API
|
||||
|
||||
See the samples directory for examples of intended use.
|
||||
|
||||
|
||||
## Runtime tracing
|
||||
|
||||
As yet, there is no tracing. For the smallest client, should we have tracing?
|
||||
|
||||
|
||||
## Reporting bugs
|
||||
|
||||
Please report bugs under the "MQTT-Embedded-C" Component in [Eclipse Bugzilla](http://bugs.eclipse.org/bugs/) for the Paho Technology project.
|
||||
|
||||
## More information
|
||||
|
||||
Discussion of the Paho clients takes place on the [Eclipse paho-dev mailing list](https://dev.eclipse.org/mailman/listinfo/paho-dev).
|
||||
|
||||
General questions about the MQTT protocol are discussed in the [MQTT Google Group](https://groups.google.com/forum/?hl=en-US&fromgroups#!forum/mqtt).
|
||||
|
||||
There is much more information available via the [MQTT community site](http://mqtt.org).
|
|
@ -0,0 +1,22 @@
|
|||
# Eclipse Paho MQTT C/C++ client for Embedded platforms
|
||||
|
||||
This repository contains the source code for the [Eclipse Paho](http://eclipse.org/paho) MQTT C/C++ client library for Embedded platorms.
|
||||
|
||||
It is dual licensed under the EPL and EDL (see about.html and notice.html for more details). You can choose which of these licenses you want to use the code under. The EDL allows you to embed the code into your application, and distribute your application in binary or source form without contributing any of your code, or any changes you make back to Paho. See the EDL for the exact conditions.
|
||||
|
||||
The MQTTPacket directory contains the lowest level C library with the smallest requirements. This supplies simple serialization
|
||||
and deserialization routines. It is mainly up to you to write and read to and from the network.
|
||||
|
||||
|
||||
## 编译
|
||||
|
||||
* 在rtconfig.h中添加 `#define RT_USING_PAHOMQTT`
|
||||
* 开启LWIP组件
|
||||
|
||||
## 使用方法
|
||||
|
||||
参见 `rt-thread\examples\network\mqttclient.c`
|
||||
|
||||
## 关于MQTT
|
||||
|
||||
参见 [MQTT community site](http://mqtt.org).
|
|
@ -0,0 +1,13 @@
|
|||
import rtconfig
|
||||
Import('RTT_ROOT')
|
||||
from building import *
|
||||
|
||||
# get current directory
|
||||
cwd = GetCurrentDir()
|
||||
|
||||
# The set of source files associated with this SConscript file.
|
||||
src = Glob('MQTTPacket/src/*.c')
|
||||
path = [cwd + '/MQTTPacket/src']
|
||||
group = DefineGroup('paho-mqtt', src, depend = ['RT_USING_PAHOMQTT'], CPPPATH = path)
|
||||
|
||||
Return('group')
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"><head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<title>About</title>
|
||||
</head>
|
||||
<body lang="EN-US">
|
||||
<h2>About This Content</h2>
|
||||
|
||||
<p><em>December 9, 2013</em></p>
|
||||
<h3>License</h3>
|
||||
|
||||
<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
|
||||
indicated below, the Content is provided to you under the terms and conditions of the
|
||||
Eclipse Public License Version 1.0 ("EPL") and Eclipse Distribution License Version 1.0 ("EDL").
|
||||
A copy of the EPL is available at
|
||||
<a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>
|
||||
and a copy of the EDL is available at
|
||||
<a href="http://www.eclipse.org/org/documents/edl-v10.php">http://www.eclipse.org/org/documents/edl-v10.php</a>.
|
||||
For purposes of the EPL, "Program" will mean the Content.</p>
|
||||
|
||||
<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
|
||||
being redistributed by another party ("Redistributor") and different terms and conditions may
|
||||
apply to your use of any object code in the Content. Check the Redistributor's license that was
|
||||
provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
|
||||
indicated below, the terms and conditions of the EPL still apply to any source code in the Content
|
||||
and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
|
||||
|
||||
</body></html>
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
Eclipse Distribution License - v 1.0
|
||||
|
||||
Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
Eclipse Public License - v 1.0
|
||||
|
||||
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
|
||||
|
||||
1. DEFINITIONS
|
||||
|
||||
"Contribution" means:
|
||||
|
||||
a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and
|
||||
b) in the case of each subsequent Contributor:
|
||||
i) changes to the Program, and
|
||||
ii) additions to the Program;
|
||||
where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.
|
||||
"Contributor" means any person or entity that distributes the Program.
|
||||
|
||||
"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
|
||||
|
||||
"Program" means the Contributions distributed in accordance with this Agreement.
|
||||
|
||||
"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.
|
||||
|
||||
2. GRANT OF RIGHTS
|
||||
|
||||
a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
|
||||
b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.
|
||||
c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.
|
||||
d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
|
||||
3. REQUIREMENTS
|
||||
|
||||
A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
|
||||
|
||||
a) it complies with the terms and conditions of this Agreement; and
|
||||
b) its license agreement:
|
||||
i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
|
||||
ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;
|
||||
iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and
|
||||
iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.
|
||||
When the Program is made available in source code form:
|
||||
|
||||
a) it must be made available under this Agreement; and
|
||||
b) a copy of this Agreement must be included with each copy of the Program.
|
||||
Contributors may not remove or alter any copyright notices contained within the Program.
|
||||
|
||||
Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.
|
||||
|
||||
4. COMMERCIAL DISTRIBUTION
|
||||
|
||||
Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
|
||||
|
||||
For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.
|
||||
|
||||
5. NO WARRANTY
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
|
||||
|
||||
6. DISCLAIMER OF LIABILITY
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
7. GENERAL
|
||||
|
||||
If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
|
||||
|
||||
If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
|
||||
|
||||
All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
|
||||
|
||||
Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
|
||||
|
||||
This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.
|
|
@ -0,0 +1,108 @@
|
|||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
|
||||
<title>Eclipse Foundation Software User Agreement</title>
|
||||
</head>
|
||||
|
||||
<body lang="EN-US">
|
||||
<h2>Eclipse Foundation Software User Agreement</h2>
|
||||
<p>February 1, 2011</p>
|
||||
|
||||
<h3>Usage Of Content</h3>
|
||||
|
||||
<p>THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS
|
||||
(COLLECTIVELY "CONTENT"). USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND
|
||||
CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE
|
||||
OF THE CONTENT IS GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR
|
||||
NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND
|
||||
CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT USE THE CONTENT.</p>
|
||||
|
||||
<h3>Applicable Licenses</h3>
|
||||
|
||||
<p>Unless otherwise indicated, all Content made available by the Eclipse Foundation is provided to you under the terms and conditions of the Eclipse Public License Version 1.0
|
||||
("EPL"). A copy of the EPL is provided with this Content and is also available at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
|
||||
For purposes of the EPL, "Program" will mean the Content.</p>
|
||||
|
||||
<p>Content includes, but is not limited to, source code, object code, documentation and other files maintained in the Eclipse Foundation source code
|
||||
repository ("Repository") in software modules ("Modules") and made available as downloadable archives ("Downloads").</p>
|
||||
|
||||
<ul>
|
||||
<li>Content may be structured and packaged into modules to facilitate delivering, extending, and upgrading the Content. Typical modules may include plug-ins ("Plug-ins"), plug-in fragments ("Fragments"), and features ("Features").</li>
|
||||
<li>Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java™ ARchive) in a directory named "plugins".</li>
|
||||
<li>A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material. Each Feature may be packaged as a sub-directory in a directory named "features". Within a Feature, files named "feature.xml" may contain a list of the names and version numbers of the Plug-ins
|
||||
and/or Fragments associated with that Feature.</li>
|
||||
<li>Features may also include other Features ("Included Features"). Within a Feature, files named "feature.xml" may contain a list of the names and version numbers of Included Features.</li>
|
||||
</ul>
|
||||
|
||||
<p>The terms and conditions governing Plug-ins and Fragments should be contained in files named "about.html" ("Abouts"). The terms and conditions governing Features and
|
||||
Included Features should be contained in files named "license.html" ("Feature Licenses"). Abouts and Feature Licenses may be located in any directory of a Download or Module
|
||||
including, but not limited to the following locations:</p>
|
||||
|
||||
<ul>
|
||||
<li>The top-level (root) directory</li>
|
||||
<li>Plug-in and Fragment directories</li>
|
||||
<li>Inside Plug-ins and Fragments packaged as JARs</li>
|
||||
<li>Sub-directories of the directory named "src" of certain Plug-ins</li>
|
||||
<li>Feature directories</li>
|
||||
</ul>
|
||||
|
||||
<p>Note: if a Feature made available by the Eclipse Foundation is installed using the Provisioning Technology (as defined below), you must agree to a license ("Feature Update License") during the
|
||||
installation process. If the Feature contains Included Features, the Feature Update License should either provide you with the terms and conditions governing the Included Features or
|
||||
inform you where you can locate them. Feature Update Licenses may be found in the "license" property of files named "feature.properties" found within a Feature.
|
||||
Such Abouts, Feature Licenses, and Feature Update Licenses contain the terms and conditions (or references to such terms and conditions) that govern your use of the associated Content in
|
||||
that directory.</p>
|
||||
|
||||
<p>THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS. SOME OF THESE
|
||||
OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):</p>
|
||||
|
||||
<ul>
|
||||
<li>Eclipse Distribution License Version 1.0 (available at <a href="http://www.eclipse.org/licenses/edl-v10.html">http://www.eclipse.org/licenses/edl-v1.0.html</a>)</li>
|
||||
<li>Common Public License Version 1.0 (available at <a href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a>)</li>
|
||||
<li>Apache Software License 1.1 (available at <a href="http://www.apache.org/licenses/LICENSE">http://www.apache.org/licenses/LICENSE</a>)</li>
|
||||
<li>Apache Software License 2.0 (available at <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>)</li>
|
||||
<li>Metro Link Public License 1.00 (available at <a href="http://www.opengroup.org/openmotif/supporters/metrolink/license.html">http://www.opengroup.org/openmotif/supporters/metrolink/license.html</a>)</li>
|
||||
<li>Mozilla Public License Version 1.1 (available at <a href="http://www.mozilla.org/MPL/MPL-1.1.html">http://www.mozilla.org/MPL/MPL-1.1.html</a>)</li>
|
||||
</ul>
|
||||
|
||||
<p>IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR TO USE OF THE CONTENT. If no About, Feature License, or Feature Update License is provided, please
|
||||
contact the Eclipse Foundation to determine what terms and conditions govern that particular Content.</p>
|
||||
|
||||
|
||||
<h3>Use of Provisioning Technology</h3>
|
||||
|
||||
<p>The Eclipse Foundation makes available provisioning software, examples of which include, but are not limited to, p2 and the Eclipse
|
||||
Update Manager ("Provisioning Technology") for the purpose of allowing users to install software, documentation, information and/or
|
||||
other materials (collectively "Installable Software"). This capability is provided with the intent of allowing such users to
|
||||
install, extend and update Eclipse-based products. Information about packaging Installable Software is available at <a
|
||||
href="http://eclipse.org/equinox/p2/repository_packaging.html">http://eclipse.org/equinox/p2/repository_packaging.html</a>
|
||||
("Specification").</p>
|
||||
|
||||
<p>You may use Provisioning Technology to allow other parties to install Installable Software. You shall be responsible for enabling the
|
||||
applicable license agreements relating to the Installable Software to be presented to, and accepted by, the users of the Provisioning Technology
|
||||
in accordance with the Specification. By using Provisioning Technology in such a manner and making it available in accordance with the
|
||||
Specification, you further acknowledge your agreement to, and the acquisition of all necessary rights to permit the following:</p>
|
||||
|
||||
<ol>
|
||||
<li>A series of actions may occur ("Provisioning Process") in which a user may execute the Provisioning Technology
|
||||
on a machine ("Target Machine") with the intent of installing, extending or updating the functionality of an Eclipse-based
|
||||
product.</li>
|
||||
<li>During the Provisioning Process, the Provisioning Technology may cause third party Installable Software or a portion thereof to be
|
||||
accessed and copied to the Target Machine.</li>
|
||||
<li>Pursuant to the Specification, you will provide to the user the terms and conditions that govern the use of the Installable
|
||||
Software ("Installable Software Agreement") and such Installable Software Agreement shall be accessed from the Target
|
||||
Machine in accordance with the Specification. Such Installable Software Agreement must inform the user of the terms and conditions that govern
|
||||
the Installable Software and must solicit acceptance by the end user in the manner prescribed in such Installable Software Agreement. Upon such
|
||||
indication of agreement by the user, the provisioning Technology will complete installation of the Installable Software.</li>
|
||||
</ol>
|
||||
|
||||
<h3>Cryptography</h3>
|
||||
|
||||
<p>Content may contain encryption software. The country in which you are currently may have restrictions on the import, possession, and use, and/or re-export to
|
||||
another country, of encryption software. BEFORE using any encryption software, please check the country's laws, regulations and policies concerning the import,
|
||||
possession, or use, and re-export of encryption software, to see if this is permitted.</p>
|
||||
|
||||
<p><small>Java and all Java-based trademarks are trademarks of Oracle Corporation in the United States, other countries, or both.</small></p>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,560 @@
|
|||
|
||||
#define OS_RTTHREAD 1
|
||||
|
||||
#if (OS_RTTHREAD == 0)
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/select.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DEBUG printf
|
||||
#define SLEEP(x) sleep(x)
|
||||
#else
|
||||
#include <rtthread.h>
|
||||
#include <lwip/netdb.h>
|
||||
#include <lwip/sockets.h>
|
||||
#include <arch/sys_arch.h>
|
||||
#include <lwip/sys.h>
|
||||
|
||||
#define DEBUG rt_kprintf
|
||||
#define SLEEP(x) rt_thread_delay((x)*RT_TICK_PER_SECOND)
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include "MQTTPacket.h"
|
||||
|
||||
#define HOSTNAME "m2m.eclipse.org"
|
||||
#define HOSTPORT 1883
|
||||
#define USERNAME "testuser"
|
||||
#define PASSWORD "testpassword"
|
||||
#define TOPIC "test"
|
||||
|
||||
#define KEEPALIVE_INTERVAL 20
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int qos;
|
||||
unsigned char retained;
|
||||
unsigned char dup;
|
||||
unsigned short id;
|
||||
int payloadlen;
|
||||
unsigned char *payload;
|
||||
}mqtt_msg_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int sockfd;
|
||||
unsigned char *wbuf; //
|
||||
int wbuflen;
|
||||
unsigned char *rbuf;
|
||||
int rbuflen;
|
||||
int (*getfn)(unsigned char*, int);
|
||||
}mqtt_client_t;
|
||||
|
||||
static mqtt_client_t _cpub;
|
||||
static mqtt_client_t _csub;
|
||||
|
||||
static void *mqtt_ping_thread(void *param)
|
||||
{
|
||||
int *sockfd = (int*)param;
|
||||
unsigned char buf[2];
|
||||
int len;
|
||||
|
||||
DEBUG("ping start\n");
|
||||
|
||||
while (*sockfd >= 0)
|
||||
{
|
||||
SLEEP(KEEPALIVE_INTERVAL-1);
|
||||
len = MQTTSerialize_pingreq(buf, sizeof(buf));
|
||||
|
||||
send(*sockfd, buf, len, 0);
|
||||
}
|
||||
|
||||
DEBUG("ping exit\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mqtt_ping_start(int *sockfd)
|
||||
{
|
||||
#if (OS_RTTHREAD == 0)
|
||||
pthread_t tid;
|
||||
|
||||
pthread_create(&tid, NULL, mqtt_ping_thread, (void*)sockfd);
|
||||
#else
|
||||
sys_thread_new("ping",
|
||||
mqtt_ping_thread,
|
||||
(void*)sockfd,
|
||||
512,
|
||||
20);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int sub_read(unsigned char *buf, int len)
|
||||
{
|
||||
int bytes = 0;
|
||||
struct timeval interval;
|
||||
int rc;
|
||||
|
||||
interval.tv_sec = 3;
|
||||
interval.tv_usec = 0;
|
||||
|
||||
rc = setsockopt(_csub.sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&interval, sizeof(struct timeval));
|
||||
|
||||
while (bytes < len)
|
||||
{
|
||||
rc = recv(_csub.sockfd, &buf[bytes], (size_t)(len - bytes), 0);
|
||||
|
||||
if (rc == -1)
|
||||
{
|
||||
if (errno != ENOTCONN && errno != ECONNRESET)
|
||||
{
|
||||
bytes = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
bytes += rc;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static int pub_read(unsigned char *buf, int len)
|
||||
{
|
||||
int bytes = 0;
|
||||
struct timeval interval;
|
||||
int rc;
|
||||
|
||||
interval.tv_sec = 3;
|
||||
interval.tv_usec = 0;
|
||||
|
||||
rc = setsockopt(_cpub.sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&interval, sizeof(struct timeval));
|
||||
|
||||
while (bytes < len)
|
||||
{
|
||||
rc = recv(_cpub.sockfd, &buf[bytes], (size_t)(len - bytes), 0);
|
||||
|
||||
if (rc == -1)
|
||||
{
|
||||
if (errno != ENOTCONN && errno != ECONNRESET)
|
||||
{
|
||||
bytes = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
bytes += rc;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
int mqtt_write(int sockfd, unsigned char *buf, int len)
|
||||
{
|
||||
int rc;
|
||||
struct timeval tv;
|
||||
|
||||
tv.tv_sec = 2;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(struct timeval));
|
||||
rc = send(sockfd, buf, len, 0);
|
||||
if (rc == len)
|
||||
rc = 0;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int mqtt_subscribe(mqtt_client_t *c, char* topicstr, int qos)
|
||||
{
|
||||
MQTTString topic = MQTTString_initializer;
|
||||
int msgid = 1;
|
||||
int len;
|
||||
int rc = -1;
|
||||
|
||||
topic.cstring = topicstr;
|
||||
len = MQTTSerialize_subscribe(c->wbuf, c->wbuflen, 0, msgid, 1, &topic, &qos);
|
||||
if (len <= 0)
|
||||
goto exit;
|
||||
|
||||
rc = mqtt_write(c->sockfd, c->wbuf, len);
|
||||
if (rc < 0)
|
||||
goto exit;
|
||||
|
||||
if (MQTTPacket_read(c->rbuf, c->rbuflen, c->getfn) == SUBACK)
|
||||
{
|
||||
unsigned short submsgid;
|
||||
int subcount;
|
||||
int granted_qos;
|
||||
|
||||
rc = MQTTDeserialize_suback(&submsgid, 1, &subcount, &granted_qos, c->rbuf, c->rbuflen);
|
||||
if (granted_qos != 0)
|
||||
{
|
||||
DEBUG("granted qos != 0, %d\n", granted_qos);
|
||||
rc = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int mqtt_publish(mqtt_client_t *c, char* topicstr, mqtt_msg_t *msg)
|
||||
{
|
||||
int rc = -1;
|
||||
MQTTString topic = MQTTString_initializer;
|
||||
int len;
|
||||
int pktype;
|
||||
|
||||
topic.cstring = topicstr;
|
||||
|
||||
len = MQTTSerialize_publish(c->wbuf, c->wbuflen,
|
||||
msg->dup, msg->qos, msg->retained, msg->id,
|
||||
topic, msg->payload, msg->payloadlen);
|
||||
if (len <= 0)
|
||||
goto exit;
|
||||
|
||||
if ((rc = mqtt_write(c->sockfd, c->wbuf, len)) <= 0)
|
||||
goto exit;
|
||||
|
||||
pktype = MQTTPacket_read(c->rbuf, c->rbuflen, c->getfn);
|
||||
|
||||
if (msg->qos == 1)
|
||||
{
|
||||
if (pktype == PUBACK)
|
||||
{
|
||||
unsigned short mypacketid;
|
||||
unsigned char dup, type;
|
||||
if (MQTTDeserialize_ack(&type, &dup, &mypacketid, c->rbuf, c->rbuflen) != 1)
|
||||
rc = -1;
|
||||
}
|
||||
else
|
||||
rc = -1;
|
||||
}
|
||||
else if (msg->qos == 2)
|
||||
{
|
||||
if (pktype == PUBCOMP)
|
||||
{
|
||||
unsigned short mypacketid;
|
||||
unsigned char dup, type;
|
||||
if (MQTTDeserialize_ack(&type, &dup, &mypacketid, c->rbuf, c->rbuflen) != 1)
|
||||
rc = -1;
|
||||
}
|
||||
else
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int mqtt_netconnect(char *addr, int port)
|
||||
{
|
||||
struct hostent *host = 0;
|
||||
struct sockaddr_in sockaddr;
|
||||
int sock;
|
||||
|
||||
host = gethostbyname(addr);
|
||||
if (host == 0)
|
||||
return -1;
|
||||
|
||||
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
sockaddr.sin_family = AF_INET;
|
||||
sockaddr.sin_port = htons(port);
|
||||
sockaddr.sin_addr = *((struct in_addr *)host->h_addr);
|
||||
memset(&(sockaddr.sin_zero), 0, sizeof(sockaddr.sin_zero));
|
||||
|
||||
if (connect(sock, (struct sockaddr *)&sockaddr, sizeof(struct sockaddr)) == -1)
|
||||
{
|
||||
#if (OS_RTTHREAD == 0)
|
||||
close(sock);
|
||||
#else
|
||||
closesocket(sock);
|
||||
#endif
|
||||
return -2;
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
void mqtt_netdisconnect(int *sockfd)
|
||||
{
|
||||
#if (OS_RTTHREAD == 0)
|
||||
close(*sockfd);
|
||||
#else
|
||||
closesocket(*sockfd);
|
||||
#endif
|
||||
*sockfd = -1;
|
||||
}
|
||||
|
||||
int mqtt_connect(mqtt_client_t *c, MQTTPacket_connectData *data)
|
||||
{
|
||||
int rc = -1;
|
||||
int len;
|
||||
|
||||
len = MQTTSerialize_connect(c->wbuf, c->wbuflen, data);
|
||||
if (len <= 0)
|
||||
goto exit;
|
||||
|
||||
rc = mqtt_write(c->sockfd, c->wbuf, len);
|
||||
if (rc < 0)
|
||||
goto exit;
|
||||
|
||||
rc = MQTTPacket_read(c->rbuf, c->rbuflen, c->getfn);
|
||||
if (rc < 0)
|
||||
goto exit;
|
||||
|
||||
if (rc == CONNACK)
|
||||
{
|
||||
unsigned char sessionPresent, connack_rc;
|
||||
|
||||
if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, c->rbuf, c->rbuflen) == 1)
|
||||
{
|
||||
rc = connack_rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
rc = -1;
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
int mqtt_disconnect(mqtt_client_t *c)
|
||||
{
|
||||
int rc = -1;
|
||||
int len;
|
||||
|
||||
len = MQTTSerialize_disconnect(c->wbuf, c->wbuflen);
|
||||
if (len > 0)
|
||||
{
|
||||
rc = mqtt_write(c->sockfd, c->wbuf, len);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void *mqtt_pub_thread(void *param)
|
||||
{
|
||||
MQTTPacket_connectData condata = MQTTPacket_connectData_initializer;
|
||||
int rc;
|
||||
unsigned char rbuf[64];
|
||||
unsigned char wbuf[64];
|
||||
|
||||
DEBUG("pub thread start\n");
|
||||
/* */
|
||||
_cpub.rbuf = rbuf;
|
||||
_cpub.rbuflen = sizeof(rbuf);
|
||||
_cpub.wbuf = wbuf;
|
||||
_cpub.wbuflen = sizeof(wbuf);
|
||||
_cpub.getfn = pub_read;
|
||||
|
||||
if ((_cpub.sockfd = mqtt_netconnect(HOSTNAME, HOSTPORT)) < 0)
|
||||
{
|
||||
DEBUG("pub netconnet fail\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUG("pub connect to: %s %d\n", HOSTNAME, HOSTPORT);
|
||||
|
||||
condata.clientID.cstring = "mqttpub";
|
||||
condata.keepAliveInterval = KEEPALIVE_INTERVAL;
|
||||
condata.cleansession = 1;
|
||||
condata.username.cstring = USERNAME;
|
||||
condata.password.cstring = PASSWORD;
|
||||
|
||||
rc = mqtt_connect(&_cpub, &condata);
|
||||
if (rc < 0)
|
||||
goto exit;
|
||||
|
||||
DEBUG("pub connect ok\n");
|
||||
|
||||
mqtt_ping_start(&_cpub.sockfd);
|
||||
|
||||
while (rc == 0)
|
||||
{
|
||||
mqtt_msg_t msg;
|
||||
|
||||
SLEEP(5);
|
||||
msg.dup = 0;
|
||||
msg.id = 0;
|
||||
msg.qos = 0;
|
||||
msg.retained = 0;
|
||||
msg.payload = (unsigned char*)"RT-Thread";
|
||||
msg.payloadlen = strlen((const char*)msg.payload);
|
||||
|
||||
rc = mqtt_publish(&_cpub, TOPIC, &msg);
|
||||
}
|
||||
|
||||
exit:
|
||||
mqtt_netdisconnect(&_cpub.sockfd);
|
||||
DEBUG("pub thread exit\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void msgprocess(MQTTString *topic, mqtt_msg_t *msg)
|
||||
{
|
||||
msg->payload[msg->payloadlen] = 0;
|
||||
DEBUG("recv: size = %d, msg = %s\n", msg->payloadlen, msg->payload);
|
||||
}
|
||||
|
||||
static void *mqtt_sub_thread(void *param)
|
||||
{
|
||||
MQTTPacket_connectData condata = MQTTPacket_connectData_initializer;
|
||||
int pktype, rc, len;
|
||||
int failcnt = 0;
|
||||
unsigned char wbuf[64];
|
||||
unsigned char rbuf[64];
|
||||
|
||||
DEBUG("sub thread start\n");
|
||||
|
||||
_csub.wbuf = wbuf;
|
||||
_csub.wbuflen = sizeof(wbuf);
|
||||
_csub.rbuf = rbuf;
|
||||
_csub.rbuflen = sizeof(rbuf);
|
||||
_csub.getfn = sub_read;
|
||||
|
||||
if ((_csub.sockfd = mqtt_netconnect(HOSTNAME, HOSTPORT)) < 0)
|
||||
{
|
||||
DEBUG("sub netconnect fail\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUG("sub connect to: %s %d\n", HOSTNAME, HOSTPORT);
|
||||
|
||||
condata.clientID.cstring = "mqttsub";
|
||||
condata.keepAliveInterval = KEEPALIVE_INTERVAL;
|
||||
condata.cleansession = 1;
|
||||
condata.username.cstring = USERNAME;
|
||||
condata.password.cstring = PASSWORD;
|
||||
|
||||
rc = mqtt_connect(&_csub, &condata);
|
||||
if (rc < 0)
|
||||
goto exit;
|
||||
|
||||
DEBUG("sub connect ok\n");
|
||||
|
||||
rc = mqtt_subscribe(&_csub, TOPIC, 0);
|
||||
if (rc < 0)
|
||||
goto exit;
|
||||
|
||||
DEBUG("sub topic: %s\n", TOPIC);
|
||||
|
||||
mqtt_ping_start(&_csub.sockfd);
|
||||
|
||||
while (1)
|
||||
{
|
||||
pktype = MQTTPacket_read(_csub.rbuf, _csub.rbuflen, sub_read);
|
||||
|
||||
switch (pktype)
|
||||
{
|
||||
case CONNACK:
|
||||
case PUBACK:
|
||||
case SUBACK:
|
||||
break;
|
||||
case PUBLISH:
|
||||
{
|
||||
MQTTString topic;
|
||||
mqtt_msg_t msg;
|
||||
|
||||
if (MQTTDeserialize_publish(&msg.dup, &msg.qos, &msg.retained, &msg.id, &topic,
|
||||
&msg.payload, &msg.payloadlen, _csub.rbuf, _csub.rbuflen) != 1)
|
||||
goto exit;
|
||||
|
||||
msgprocess(&topic, &msg);
|
||||
|
||||
if (msg.qos != 0)
|
||||
{
|
||||
if (msg.qos == 1)
|
||||
len = MQTTSerialize_ack(_csub.wbuf, _csub.wbuflen, PUBACK, 0, msg.id);
|
||||
else if (msg.qos == 2)
|
||||
len = MQTTSerialize_ack(_csub.wbuf, _csub.wbuflen, PUBREC, 0, msg.id);
|
||||
|
||||
if (len <= 0)
|
||||
rc = -1;
|
||||
else
|
||||
rc = mqtt_write(_csub.sockfd, _csub.wbuf, len);
|
||||
|
||||
if (rc == -1)
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PUBCOMP:
|
||||
break;
|
||||
case PINGRESP:
|
||||
failcnt = 0;
|
||||
break;
|
||||
case -1:
|
||||
if (++failcnt > KEEPALIVE_INTERVAL)
|
||||
{
|
||||
/* */
|
||||
goto exit;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* */
|
||||
mqtt_disconnect(&_csub);
|
||||
|
||||
exit:
|
||||
mqtt_netdisconnect(&_csub.sockfd);
|
||||
DEBUG("sub thread exit\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mqtt_client_init(void)
|
||||
{
|
||||
#if (OS_RTTHREAD == 0)
|
||||
pthread_t tid;
|
||||
|
||||
pthread_create(&tid, NULL, mqtt_sub_thread, NULL);
|
||||
pthread_create(&tid, NULL, mqtt_pub_thread, NULL);
|
||||
#else
|
||||
sys_thread_new("sub",
|
||||
mqtt_sub_thread,
|
||||
NULL,
|
||||
1024,
|
||||
20);
|
||||
sys_thread_new("pub",
|
||||
mqtt_pub_thread,
|
||||
NULL,
|
||||
1024,
|
||||
20);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef RT_USING_FINSH
|
||||
#include <finsh.h>
|
||||
FINSH_FUNCTION_EXPORT(mqtt_client_init, MQTT Pub/Sub Test);
|
||||
#endif
|
Loading…
Reference in New Issue