I'm trying to use mqtt on my raspberrpi4.
I installed mqtt and the c++ headers.
I start my broker with :
mosquitto -d
Then for a validation I start a client via the command line:
mosquitto_sub -d -t "test"
Then I also start the c++ client (following code):
#include <string.h>
#include <stdio.h>
#include <mosquitto.h>
char mqMsg[30];
void my_message_callback(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *message)
{
if(message->payloadlen){
printf("BANANA %s %s\n", message->topic, message->payload);
}else{
printf("BONONO %s (null)\n", message->topic);
}
fflush(stdout);
}
void my_connect_callback(struct mosquitto *mosq, void *userdata, int result)
{
int i;
if(!result){
mosquitto_publish(mosq, NULL, "hello/world", strlen(mqMsg), mqMsg, 2, 0);
}else{
fprintf(stderr, "Connect failed\n");
}
}
void my_log_callback(struct mosquitto *mosq, void *userdata, int level, const char *str)
{
/* Pring all log messages regardless of level. */
printf("%s\n", str);
}
void my_publish_callback(struct mosquitto *mosq, void *userdata, int usernumber)
{
/* We've published so lets exit nicely */
mosquitto_disconnect(mosq);
}
int main(int argc, char *argv[])
{
int i;
char *host = "localhost";
int port = 1883;
int keepalive = 60;
bool clean_session = true;
struct mosquitto *mosq = NULL;
sprintf(mqMsg,"%s",argv[1]);
printf("And the word is >> %s <<\n", mqMsg);
mosquitto_lib_init();
mosq = mosquitto_new(NULL, clean_session, NULL);
if(!mosq){
fprintf(stderr, "Error: Out of memory.\n");
return 1;
}
mosquitto_log_callback_set(mosq, my_log_callback);
mosquitto_connect_callback_set(mosq, my_connect_callback);
mosquitto_message_callback_set(mosq, my_message_callback);
mosquitto_publish_callback_set(mosq, my_publish_callback);
if(mosquitto_connect(mosq, host, port, keepalive)){
fprintf(stderr, "Unable to connect.\n");
return 1;
}
mosquitto_loop_forever(mosq, -1, 1);
mosquitto_subscribe(mosq, NULL, "test", 2);
while(1){
}
mosquitto_destroy(mosq);
mosquitto_lib_cleanup();
return 0;
}
After that I publish something on the topic
mosquitto_pub -d -t "test" -m "test message"
I can see the message on the command line client but not on the c++ client?
Any idea why?
B.t.w. publishing via the c++ client works.
Thanks!
You call mosquitto_subscribe(mosq, NULL, "test", 2); after mosquitto_loop_forever(), therefore when your client actually runs, it's not subscribed to your topic. This also means that you don't need the while(1) in your code. See https://mosquitto.org/api/files/mosquitto-h.html#mosquitto_loop_forever for the description of the `mosquitto_loop_forever()' function.
Related
I'm struggling using the mosquitto lib on my RaspberryPI 4 in an non-blocking way.
This is may main method:
#include <stdio.h>
#include <mosquitto.h>
#include "mqtt.h"
#include <string>
int main(int argc, char **argv)
{
printf("Start\n");
MqttConnector * mqtt = new MqttConnector("piClient", "send", "rc", 1883, "localhost", 60);
mqtt->startClient();
printf("MQTT is started\n");
while(1)
{
}
return 0;
}
Important parts of mqtt.cpp
#include "mqtt.h"
#include <stdio.h>
#include <string>
#include <string.h>
#include <mosquitto.h>
MqttConnector::MqttConnector(std::string id, std::string sendTopic, std::string receiveTopic, int port, std::string host, int keepalive)
{
mosquitto_lib_init();
mosqClient = mosquitto_new(id.c_str(), true, this);
if(!mosqClient){
fprintf(stderr, "Error: Out of memory.\n");
}
this->keepalive = keepalive;
this->id = id;
this->host = host;
this->port = port;
this->sendTopic = sendTopic;
this->receiveTopic = receiveTopic;
}
MqttConnector::~MqttConnector()
{
mosquitto_destroy(mosqClient);
mosquitto_lib_cleanup();
}
void MqttConnector::messageCallback(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *message)
{
//MqttConnector * mqttInstance = (MqttConnector *) userdata;
if(message->payloadlen){
std::string payloadString = reinterpret_cast<char*>(message->payload);
printf("Message arriving in %s : %s\n", message->topic, payloadString.c_str());
std::string rp = "rp";
if(payloadString == rp)
{
mosquitto_publish(mosq, NULL, "send", strlen("test"), "test", 2, false);
}
}else{
printf("Empty message arriving in %s\n", message->topic);
}
}
void MqttConnector::connectCallback(struct mosquitto *mosq, void *userdata, int result)
{
if(!result){
printf("Connection established\n");
}else{
fprintf(stderr, "Connect failed\n");
}
}
void MqttConnector::logCallback(struct mosquitto *mosq, void *userdata, int level, const char *str)
{
/* Pring all log messages regardless of level. */
printf("%s\n", str);
}
void MqttConnector::publishCallback(struct mosquitto *mosq, void *userdata, int usernumber)
{
printf("Published a message\n");
}
void MqttConnector::startClient()
{
mosquitto_message_callback_set(mosqClient, messageCallback);
mosquitto_log_callback_set(mosqClient, logCallback);
mosquitto_connect_callback_set(mosqClient, connectCallback);
mosquitto_publish_callback_set(mosqClient, publishCallback);
/*
//Connecting without async works! Publish and receiving messages work!
// loop starts by calling loop_forever
if(mosquitto_connect(mosqClient, host.c_str(), port, keepalive)){
fprintf(stderr, "Unable to connect.\n");
}
mosquitto_subscribe(mosqClient, NULL, receiveTopic.c_str(), 2);
mosquitto_loop_forever(mosqClient, 10, 1);
*/
// According to docu we need to call loop_start instead of forever
// not able to publish/receive message with this!
if(mosquitto_connect_async(mosqClient, host.c_str(), port, keepalive)){
fprintf(stderr, "Unable to connect.\n");
}
mosquitto_subscribe(mosqClient, NULL, receiveTopic.c_str(), 2);
mosquitto_loop_start(mosqClient);
}
Connecting to the server with mosquitto_connect and starting the loop with mosquitto_loop_forever is the blocking way. Therefore I only see the first print statement in the main.cpp ('Start'). But with this way, I'm able to publish and receive message with that client.
As soon as I connect with mosquitto_connect_async, I need to start the loop with mosquitto_loop_start as it is mentioned in the documentation. The client connects to the server without an error. And I also see the second print statement from the main.cpp now. But I'm not able to publish oder receive any messages with the mqtt client.
My first guess was that because of the threading, the messages are not printed on the console, but even other clients startet with mosquitto_sub don't receive a message from that c++ client.
Do I need to install something for threading support on the RPI4?
I don't understand why it is not working, because the RPI4 supports multithreading.
Thanks for helping
The following code is an application supposed to be communicating between two applications. In one exe (A) user type a message and the message is printed in the other exe(B).
The flow of the program:
Both exe calls connectTo so they are ready to send and receive messages between.
User type a message to be send in A console window, which calls sendMsg in A exe. When message is received in B, the message is printed in B console window.
The problem is that the message received is sometimes empty. When I enter 1234 ten times in A, ten messages are printed in B with only 5-6 of them are 1234 and the rest are empty. The situation is the same from B to A.
++++++++++++++++++++++++++
whole program
++++++++++++++++++++++++++
Header.h
#pragma once
#include <WinSock2.h>
#include <Windows.h>
#include <mutex>
#include <thread>
class CommuWin
{
private:
std::mutex m_accessMutexSend;
std::mutex m_accessMutexReceive;
std::thread m_sendThread;
std::thread m_receiveThread;
bool m_IsSendReady = false;
bool m_IsRecvReady = false;
SOCKET m_outSocket;
SOCKADDR_IN m_outAddr;
SOCKET m_inSocket;
SOCKADDR_IN m_inAddr;
public:
CommuWin(int InPort, int OutPort);
~CommuWin();
int connectTo();
int sendMsg(const char* message);
int StartReceiveMsg();
bool GetRecvStatus();
bool GetSendStatus();
private:
void SetRecvStatus(bool ready);
void SetSendStatus(bool ready);
int SetupRecvEnd();
int SetupSendEnd();
int sendMsgTo(const char* message);
int ReceiveMsgFrom();
};
Source.cpp
#pragma comment(lib, "Ws2_32.lib")
#include "Header.h"
#define OKAY (1)
#define ERROR (-1)
#define MAX_MSG_SIZE (200)
class NetworkServices
{
public:
static int sendMessage(SOCKET curSocket, const char* message, int messageSize);
static int receiveMessage(SOCKET curSocket, char * buffer, int bufSize);
};
int NetworkServices::sendMessage(SOCKET curSocket, const char* message, int messageSize)
{
return send(curSocket, message, messageSize, 0);
}
int NetworkServices::receiveMessage(SOCKET curSocket, char * buffer, int bufSize)
{
return recv(curSocket, buffer, bufSize, 0);
}
CommuWin::CommuWin(int InPort, int OutPort)
{
WSAData wsaData;
WORD DLLVersion;
DLLVersion = MAKEWORD(2, 1);
int r = WSAStartup(DLLVersion, &wsaData);
///////////////////////////////////////////////////////////////////////
m_outSocket = socket(AF_INET, SOCK_STREAM, NULL);
m_outAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
m_outAddr.sin_family = AF_INET;
m_outAddr.sin_port = htons(OutPort);
m_inSocket = socket(AF_INET, SOCK_STREAM, NULL);
m_inAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
m_inAddr.sin_family = AF_INET;
m_inAddr.sin_port = htons(InPort);
}
CommuWin::~CommuWin()
{
}
int CommuWin::connectTo()
{
printf("connect to");
printf("\n");
m_sendThread = std::thread(
&CommuWin::SetupSendEnd,
this);
m_receiveThread = std::thread(
&CommuWin::SetupRecvEnd,
this);
return OKAY;
}
int CommuWin::SetupSendEnd()
{
SOCKET sListen;
sListen = socket(AF_INET, SOCK_STREAM, NULL);
bind(sListen, (SOCKADDR*)&m_outAddr, sizeof(m_outAddr));
listen(sListen, SOMAXCONN);
m_outSocket = accept(sListen, NULL, NULL);
if (m_outSocket != INVALID_SOCKET)
{
SetSendStatus(true);
printf("accepted\n");
}
return OKAY;
}
int CommuWin::SetupRecvEnd()
{
int connectSucceed = 0;
do
{
Sleep(1000);
connectSucceed = connect(m_inSocket, (SOCKADDR*)&m_inAddr, sizeof(m_inAddr));
} while (connectSucceed == SOCKET_ERROR);
SetRecvStatus(true);
printf("connected\n");
return OKAY;
}
int CommuWin::sendMsg(const char* message)
{
if (GetSendStatus())
{
m_sendThread.detach();
m_sendThread = std::thread(
&CommuWin::sendMsgTo,
this,
message);
}
return OKAY;
}
int CommuWin::sendMsgTo(const char* message)
{
NetworkServices::sendMessage(m_outSocket, message, (int)strlen(message));
return OKAY;
}
int CommuWin::StartReceiveMsg()
{
if (GetRecvStatus())
{
m_receiveThread.detach();
m_receiveThread = std::thread(
&CommuWin::ReceiveMsgFrom,
this);
}
return OKAY;
}
int CommuWin::ReceiveMsgFrom()
{
while (true)
{
char message[MAX_MSG_SIZE];
ZeroMemory(message, MAX_MSG_SIZE);
NetworkServices::receiveMessage(m_inSocket, message, sizeof(message));
printf(message);
printf("\n");
}
return OKAY;
}
void CommuWin::SetRecvStatus(bool ready)
{
std::lock_guard<std::mutex> lock(m_accessMutexReceive);
m_IsRecvReady = ready;
}
void CommuWin::SetSendStatus(bool ready)
{
std::lock_guard<std::mutex> lock(m_accessMutexSend);
m_IsSendReady = ready;
}
bool CommuWin::GetRecvStatus()
{
std::lock_guard<std::mutex> lock(m_accessMutexReceive);
return m_IsRecvReady;
}
bool CommuWin::GetSendStatus()
{
std::lock_guard<std::mutex> lock(m_accessMutexSend);
return m_IsSendReady;
}
main.cpp
#include "stdafx.h"
#include "Header.h"
#include <iostream>
#include <string>
int main(int argc, char *argv[])
{
std::cout << argc <<std::endl;
int Inport = std::stoi(argv[1]);
int Outport = std::stoi(argv[2]);
//std::cout << "inport = " << argv[1] << " outport = " << argv[2] << std::endl;
std::cout << "inport = " << Inport << " outport = " << Outport << std::endl;
CommuWin com(Inport, Outport);
com.connectTo();
while (true)
{
if (com.GetSendStatus() && com.GetRecvStatus())
{
com.StartReceiveMsg();
break;
}
}
while (true)
{
std::cout << "Enter Send Message" << std::endl;
std::string msg;
std::cin >> msg;
com.sendMsg(msg.c_str());
}
return 0;
}
There are multiple issues with your code. First of all, you need to check results of all the functions, including, but not limited to, to sListen, bind, listen, recv.
NetworkServices::receiveMessage(m_inSocket, message, sizeof(message));
// Without checking recv result there is no way to guess how much
// bytes are actually stored in `message`, if any. Also boldly assuming
// that `message` is null terminated and represents a proper format string
// is dangerous.
printf(message);
You also need to carefully initialize all the stuff, especially sockaddr structures, which may get potentially partially initialized in this case. You are using multiple threads but perform insufficient synchronization. Method sendMsgTo(const char* message) executed by (potentially detached) background thread receives a pointer to a string buffer that may get invalidated at any time.
If you want to send and receive messages, you have to write some code to do that. Nowhere is there any code to send or receive messages. If you think there is, point specifically to the code that figures out whether or not the data you received is one or more messages. You cannot do it.
TCP is not a message protocol. If you need a message protocol on top of TCP, you have to implement one. Have a look at protocols that do this such as HTTP, IRC, or FTP so see how it's done.
If you log the number of bytes received, you will see that all the data you sent was received. It's your job to split that data into messages if you need to -- it won't happen by itself.
I´m trying to use the TeamSpeak SDK for a personal project, but the code I wrote gives me weird errors.
I read the documentation many times to find an error but I can´t see why my program is not able to connect to the TeamSpeak Server.
Here is the output from the program:
Client library version: 3.0.3.2 [Build: 1433933257] SDK
Connection Status changed to: 1, errorNumber: 0
Connection Status changed to: 0, errorNumber: 1797
failed connection initialization
Here is the program code
#include <iostream>
#include <fstream>
#include <cstring>
#include <teamspeak/clientlib.h>
#include <teamspeak/public_errors.h>
uint64 connectionHandler;
void destroy();
void event_connectStatusChanged(uint64 serverConnectionIDHandler, int newStatus, unsigned int errorNumber);
void event_serverError(uint64 serverConnectionHandlerID, const char* errorMessage, unsigned int error, const char* returnCode, const char* extraMessage);
int main()
{
ClientUIFunctions uiFunctions;
memset(&uiFunctions, 0, sizeof(struct ClientUIFunctions));
uiFunctions.onConnectStatusChangeEvent = event_connectStatusChanged;
uiFunctions.onServerErrorEvent = event_serverError;
unsigned int error = ts3client_initClientLib(&uiFunctions, NULL, LogType_FILE, NULL, "./");
if (error != ERROR_ok)
printf("Error initializing clientlib: %i\n", error);
char* version;
error = ts3client_getClientLibVersion(&version);
if (error != ERROR_ok) {
printf("Error querying clientlib version: %d\n", error);
return 0;
}
printf("Client library version: %s\n", version); /* Print version */
ts3client_freeMemory(version); /* Release string */
if (ts3client_spawnNewServerConnectionHandler(0, &connectionHandler) != ERROR_ok)
{
destroy();
return 0;
}
char* identity;
ts3client_createIdentity(&identity);
error = ts3client_startConnection(connectionHandler, identity, "127.0.0.1", 9987, "test", NULL, "", "");
ts3client_freeMemory(identity);
if (error != ERROR_ok)
std::cout << "Connection failed!" << std::endl;
getchar();
ts3client_stopConnection(connectionHandler, "...");
destroy();
return 0;
}
void event_connectStatusChanged(uint64 serverConnectionIDHandler, int newStatus, unsigned int errorNumber)
{
printf("Connection Status changed to: %i, errorNumber: %i\n", newStatus, errorNumber);
if (errorNumber != ERROR_ok)
{
char* error;
ts3client_getErrorMessage(errorNumber, &error);
std::cout << error << std::endl;
ts3client_freeMemory(error);
}
}
void event_serverError(uint64 serverConnectionHandlerID, const char* errorMessage, unsigned int error, const char* returnCode, const char* extraMessage)
{
std::cout << "ERROR: " << errorMessage << std::endl << "Extra Message: " << extraMessage << std::endl;
}
void destroy()
{
ts3client_destroyServerConnectionHandler(connectionHandler);
ts3client_destroyClientLib();
}
It appears your client crashes immediately on connecting.
Here are some common causes to check:
Server doesn't exist at that address.
Server password is wrong.
Default channel doesn't exist.
Client has been banned.
Server is an illegal installation.
Security level of your identity is too low.
Also, this error can be triggered when a component such as client, server, etc need to be updated:
I updated to the latest version because the bot wouldn't launch and
figured I'd give the install on my other server another try. It's
working flawlessly now.
Ref: https://forum.sinusbot.com/threads/new-connection-status-0-error-1797.2508/
I'd like to store my serialized data on redis and publish to the channel that i've defined. But it seems that there is a problem on SETting the key value at redis. What do i miss for the solution ?
Thanks in advance.
#include <stdio.h>
#include <assert.h>
#include <signal.h>
#include <stdlib.h>
#include "hiredis.h"
#include "async.h"
#include "macosx.h"
#define PACKETSIZE sizeof(cloudRANMessage)
#define COMMANDSIZE 256
typedef struct cloudRANMessage
{
unsigned int station_id;
unsigned int location_area;
char command[COMMANDSIZE];
}cloudRANMessage;
void printMyMessage(cloudRANMessage *message)
{
printf("%d\n", message->location_area);
printf("%d\n", message->station_id);
printf("%s\n", message->command);
}
void serialize(cloudRANMessage *message, char *data)
{
assert(data != NULL);
memcpy(data, message, sizeof *message);
}
void deserialize(char *data)
{
cloudRANMessage *tempMessage = malloc(sizeof(cloudRANMessage)); // To store deserialized message.
memset(tempMessage, 0, sizeof(cloudRANMessage));
memcpy(tempMessage, data, sizeof(cloudRANMessage));
printMyMessage(tempMessage);
}
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisReply *reply = r;
if (reply == NULL) return;
printf("%s\n", reply->str); // Call deserializaton function for the data retrieval.;
/* Disconnect after receiving the reply to GET */
redisAsyncDisconnect(c);
}
void callbackDeserialize(redisAsyncContext *c, void *r, void *privdata) {
redisReply *reply = r;
if (reply == NULL) return;
printf("%s\n", reply->str); // Call deserializaton function for the data retrieval.
char *stringReply = reply->element[0]->str;
deserialize(stringReply);
/* Disconnect after receiving the reply to GET */
redisAsyncDisconnect(c);
}
void connectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
return;
}
printf("Connected...\n");
}
void disconnectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
return;
}
CFRunLoopStop(CFRunLoopGetCurrent());
printf("Disconnected...\n");
}
int main (int argc, char **argv) {
cloudRANMessage *newcloudRANMessage = malloc(sizeof(cloudRANMessage));
newcloudRANMessage->location_area = 7214;
newcloudRANMessage->station_id = 45632;
strcpy(newcloudRANMessage->command, "HANDOVER\0");
char data[PACKETSIZE];
serialize(newcloudRANMessage, data);
signal(SIGPIPE, SIG_IGN);
CFRunLoopRef loop = CFRunLoopGetCurrent();
if( !loop ) {
printf("Error: Cannot get current run loop\n");
return 1;
}
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
/* Let *c leak for now... */
printf("Error: %s\n", c->errstr);
return 1;
}
redisMacOSAttach(c, loop);
redisAsyncSetConnectCallback(c,connectCallback);
redisAsyncSetDisconnectCallback(c,disconnectCallback);
redisAsyncCommand(c,getCallback,NULL,"SUBSCRIBE cloudRAN");
// Serialize Data then send to Redis
//redisAsyncCommand(c, getCallback, (char*) "SET", "SET LTEdata %s", data, strlen(data)); // key for our data in this case is LTEdata
redisAsyncCommand(c,NULL, NULL, "SET LTEdata %s", data);
//redisAsyncCommand(c, getCallback,(char*) "GET", "GET LTEdata");
redisAsyncCommand(c, callbackDeserialize,NULL, "GET LTEdata");
// Publish the information to the all subscribers.
redisAsyncCommand(c,NULL, NULL, "PUB cloudRAN %b",data,strlen(data));
CFRunLoopRun();
return 0;
}
In this call to redisAsyncCommand:
redisAsyncCommand(c,NULL, NULL, "SET LTEdata %s", data);
any null bytes appearing in data will terminate the hiredis string interpolation. Since you are binary encoding, this is likely to be truncating your string. Try specifying the length to make it binary-safe:
redisAsyncCommand(c,NULL, NULL, "SET LTEdata %b", data, sizeof(data));
I am using HiRedis with a c/c++ program and have written some tests to verify that subscriptions work (I based my solution on this comment).
However, currently I can only publish by manually typing something like publish foo "abcd" into the redis-cli terminal. This works as per the linked comment, but I'd like to publish from my c++ program. How can I do this?
I have tried this command:
redisAsyncCommand(c, SubCallback, (char*)"command", "publish foo \"abcd\"");
But that results in this runtime error:
Error: ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context
How can I publish data from within HiRedis?
Once a connection context has subscribed, it can't be used to PUBLISH. You must create a new connection.
from https://github.com/redis/hiredis in hiredis/examples/example-libevent.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <hiredis.h>
#include <async.h>
#include <adapters/libevent.h>
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisReply *reply = r;
if (reply == NULL) return;
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
/* Disconnect after receiving the reply to GET */
redisAsyncDisconnect(c);
}
void connectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
return;
}
printf("Connected...\n");
}
void disconnectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
return;
}
printf("Disconnected...\n");
}
int main (int argc, char **argv) {
signal(SIGPIPE, SIG_IGN);
struct event_base *base = event_base_new();
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
/* Let *c leak for now... */
printf("Error: %s\n", c->errstr);
return 1;
}
redisLibeventAttach(c,base);
redisAsyncSetConnectCallback(c,connectCallback);
redisAsyncSetDisconnectCallback(c,disconnectCallback);
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
event_base_dispatch(base);
return 0;
}