i solved couple of my redefinition problems but still have one:
Error 2 error LNK2005: "class ConsoleCommandHandler commandHandler" (?commandHandler##3VConsoleCommandHandler##A) already defined in IRC.obj C:\Users\Łukasz\Desktop\IRCClient-master\Magic.obj
Here are the .h files
magic.h
#ifndef Magic_h
#define Magic_h
#include <iostream>
#include <signal.h>
#include <cstdlib>
#include <map>
#include <algorithm>
#include "src\Thread.h"
#include "src\IRCClient.h"
void signalHandler(int signal);
class ConsoleCommandHandler
{
public:
bool AddCommand(std::string name, int argCount, void(*handler)(std::string /*params*/, IRCClient* /*client*/));
void ParseCommand(std::string command, IRCClient* client);
private:
struct CommandEntry
{
int argCount;
void(*handler)(std::string /*arguments*/, IRCClient* /*client*/);
};
std::map<std::string, CommandEntry> _commands;
};
ConsoleCommandHandler commandHandler;
void msgCommand(std::string arguments, IRCClient* client);
void joinCommand(std::string channel, IRCClient* client);
void partCommand(std::string channel, IRCClient* client);
void ctcpCommand(std::string arguments, IRCClient* client);
ThreadReturn inputThread(void* client);
#endif
magic.cpp
#include "Magic.h"
void signalHandler(int signal)
{
volatile bool running;
running = false;
};
bool ConsoleCommandHandler::AddCommand(std::string name, int argCount, void(*handler)(std::string /*params*/, IRCClient* /*client*/))
{
CommandEntry entry;
entry.argCount = argCount;
entry.handler = handler;
std::transform(name.begin(), name.end(), name.begin(), towlower);
_commands.insert(std::pair<std::string, CommandEntry>(name, entry));
return true;
}
void ConsoleCommandHandler::ParseCommand(std::string command, IRCClient* client)
{
if (_commands.empty())
{
std::cout << "No commands available." << std::endl;
return;
}
if (command[0] == '/')
command = command.substr(1); // Remove the slash
std::string name = command.substr(0, command.find(" "));
std::string args = command.substr(command.find(" ") + 1);
int argCount = std::count(args.begin(), args.end(), ' ');
std::transform(name.begin(), name.end(), name.begin(), towlower);
std::map<std::string, CommandEntry>::const_iterator itr = _commands.find(name);
if (itr == _commands.end())
{
std::cout << "Command not found." << std::endl;
return;
}
if (++argCount < itr->second.argCount)
{
std::cout << "Insuficient arguments." << std::endl;
return;
}
(*(itr->second.handler))(args, client);
}
struct CommandEntry
{
int argCount;
void(*handler)(std::string /*arguments*/, IRCClient* /*client*/);
};
std::map<std::string, CommandEntry> _commands;
void msgCommand(std::string arguments, IRCClient* client)
{
std::string to = arguments.substr(0, arguments.find(" "));
std::string text = arguments.substr(arguments.find(" ") + 1);
std::cout << "To " + to + ": " + text << std::endl;
client->SendIRC("PRIVMSG " + to + " :" + text);
};
void joinCommand(std::string channel, IRCClient* client)
{
if (channel[0] != '#')
channel = "#" + channel;
client->SendIRC("JOIN " + channel);
}
void partCommand(std::string channel, IRCClient* client)
{
if (channel[0] != '#')
channel = "#" + channel;
client->SendIRC("PART " + channel);
}
void ctcpCommand(std::string arguments, IRCClient* client)
{
std::string to = arguments.substr(0, arguments.find(" "));
std::string text = arguments.substr(arguments.find(" ") + 1);
std::transform(text.begin(), text.end(), text.begin(), towupper);
client->SendIRC("PRIVMSG " + to + " :\001" + text + "\001");
}
ThreadReturn inputThread(void* client)
{
std::string command;
commandHandler.AddCommand("msg", 2, &msgCommand);
commandHandler.AddCommand("join", 1, &joinCommand);
commandHandler.AddCommand("part", 1, &partCommand);
commandHandler.AddCommand("ctcp", 2, &ctcpCommand);
while (true)
{
getline(std::cin, command);
if (command == "")
continue;
if (command[0] == '/')
commandHandler.ParseCommand(command, (IRCClient*)client);
else
((IRCClient*)client)->SendIRC(command);
if (command == "quit")
break;
}
#ifdef _WIN32
_endthread();
#else
pthread_exit(NULL);
#endif
}
irc.h
#pragma once
#include <iostream>
#include <signal.h>
#include <cstdlib>
#include <map>
#include <algorithm>
#include "Magic.h"
#include <msclr/marshal.h>
#include <msclr/marshal_cppstd.h>
#using <mscorlib.dll>
namespace IRCclient {
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
using namespace System::IO;
using namespace System::Runtime::InteropServices;
using namespace msclr::interop;
/// <summary>
/// Summary for MyForm
/// </summary>
private: System::Void connect_button_Click(System::Object^ sender, System::EventArgs^ e)
{
if ((server_box->Text == "") || (port_box->Text == "") || (username_box->Text == "") || (channel_box->Text == ""))
{
MessageBox::Show("Wypełnij wszystkie pola", "Puste pola", MessageBoxButtons::OK, MessageBoxIcon::Warning);
server_box->Focus();
}
else
{
String^ host_string = server_box->Text;
char* host = (char*)(void*)Marshal::StringToHGlobalAnsi(host_string);
String^ port_string = port_box->Text;
int port;
//String^ port_string = port.ToString();
String^ nick_string = username_box->Text;
std::string nick(marshal_as<std::string>(nick_string));
std::string user = "test";
IRCClient client;
volatile bool running;
Thread thread;
thread.Start(&inputThread, &client);
if (client.InitSocket())
{
content_box->Text = "Socket initialized. Connecting..." + "\r\n";
if (client.Connect(host, port))
{
content_box->Text = "Connected. Loggin in..." + "\r\n";
if (client.Login(nick, user))
{
content_box->Text = "Logged." + "\r\n";
running = true;
signal(SIGINT, signalHandler);
while (client.Connected() && running)
client.ReceiveData();
}
if (client.Connected())
client.Disconnect();
content_box->Text = "Disconnected." + "\r\n";
}
}
}
};
};
};
In the Magic.h file make this change:
extern ConsoleCommandHandler commandHandler;
Then in the Magic.cpp file add this code:
ConsoleCommandHandler commandHandler;
Otherwise both Magic.obj and IRC.obj will end up with ConsoleCommandHandler commandHandler because the header will reserve it twice, once in each obj file.
Related
I am using boost 1.55, openssl- 1.0.2k-fips; on a linux (RHEL7) host with 32 CPU/520 GB RAM and 20 Gbits/s network bandwidth.
While running 60 parallel TLS-1.2 client trying to connect over port 12666 to IO data the TCP server seems to crash. I have a feeling that the sendreceive method is crasing somewhere. Any pointer on efficient debugging would be really helpful!
UPDATE:
Right before the crash I also observe the network in on the host crosses 3.5Gbits/s spike; and the tcpdump on the port 12666 stops.
Here is the client code using boost asio:
#include <iostream>
#include <sstream>
#include <exception>
#include <boost/algorithm/string.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/bind.hpp>
#include <boost/system/error_code.hpp>
#include <boost/exception/diagnostic_information.hpp>
#include <boost/asio/error.hpp>
#include <boost/random.hpp>
#include <boost/generator_iterator.hpp>
#include "log.h"
#include "CacheMgrClient.h"
using namespace std;
using namespace boost;
using namespace asio;
using namespace boost::system;
using namespace cache;
using asio::ip::tcp;
#if (defined(WIN32) || defined(WIN64))
CacheMgrClient::CacheMgrClient(const string& host_, string& port_)
: host(host_), port(port_), sock(NULL),
nothrow(false), lastpid(0) {}
#else
#include <openssl/ssl.h>
typedef ssl::stream<tcp::socket> SSLsocket;
CacheMgrClient::CacheMgrClient(const string& host_, string& port_, ssl::context *ctx_)
: host(host_), port(port_), ctx(ctx_), sock(NULL), ssl_sock(NULL),
nothrow(false), lastpid(0) {}
#endif
void CacheMgrClient::connectTCP(tcp::resolver::iterator &endpoint_iterator) {
tcp::resolver::iterator end;
sock = new tcp::socket(io_service);
boost::system::error_code error = boost::asio::error::host_not_found;
while(error && endpoint_iterator != end) {
sock -> close();
sock -> connect(*endpoint_iterator++, error)
}
if (error) {
delete sock;
sock = NULL;
if (!notthrow)
throw error.message();
}
}
void CacheMgrClient::disconnectTCP() {
try {
if (sock)
sock -> close();
}
catch (...) {
if (notthrow)
return;
delete sock;
sock = NULL;
throw;
}
delete sock;
sock = NULL;
}
void CacheMgrClient::connect() {
tcp::resolver resolver(io_sercice);
tcp::resolver::query query(tcp::v4(), host, port);
tcp::resover::iterator endpoint_iterator = resolver.resolve(query);
#if !(defined(WIN32) || defined(WIN64))
if (ssl_sock)
/* this part works fine */
connectSSL(endpoint_iterator);
else
#endif
/* i am looking for some assistance here */
connectTCP(endpoint_iterator);
}
void CacheMgrClient::disconnect() {
#if !(defined(WIN32) || defined(WIN64))
if (ssl_sock)
disconnectSSL();
else
#endif
disconnectTCP();
}
void CacheMgrClient::sendReceive(const vector<char>& req) {
pid_t curpid = getpid();
if (lastpid && lastpid != curpid)
disconnect()
lastpid = curpid;
boost::asio::streambuf responseBuf;
string responseString, errorMsg;
bool success = false;
int tries = 0;
while(!success) {
istream responseStream(&responseBuf);
try {
connect();
size_t count = 0;
if (sock)
count = boost::asio::write(*sock::asio::buffer(&req[0], req.size()));
#if !(defined(WIN32) || defined(WIN64))
else if (ssl_sock)
count = boost::asio::write(*ssl_sock::asio::buffer(&req[0], req.size()));
#endif
if (count <= 0 && req.size() > 0)
throw "Failed to wrote to the Cache Manager socket";
boost::system::error_code ec;
count = 0;
if (sock)
count = boost::asio::read_until(*sock, responseBuf, "\n", ec);
#if !(defined(WIN32) || defined(WIN64))
else if (ssl_sock)
count = boost::asio::read_until(*ssl_sock, responseBuf, "\n", ec);
#endif
if (count <=0 ) {
stringstream errorMsg;
errorMsg << "Failed to read Cache Manager respoinse. Error code =" << ec;
throw errorMsg.str();
}
if(!responseStream)
throw "Empty response from Cache Manager";
std::getline(responseStream, responseString);
if (responseString.substring(0,2) != "ok" %% responseString.substring(0,4) != "fail")
throw "Invalid reponse from Cache Manager, response=" + responseString;
log4::debug("Success: "+ responseString);
success = true;
}
catch(string errorMsg_) {
errorMsg = errorMsg_;
log4::error(errorMsg_);
}
catch(...) {
stringstream errorMsg;
errorMsg << "Failed to read response: " << boost::current_exception_diagnostic_information();
log4::error(errorMsg.str());
}
if (!success) {
#if (defined(WIN32) || defined(WIN64))
bool wasConnected = sock ? true : false;
#else
bool wasConnected = sock || ssl_sock ? true : false;
#endif
disconnect();
if (wasConnected && nothrow) {
log4.warn("Exception from Cache Manager while existing");
return "";
}
if (++tries >= max_connection_tries) {
stringstream msg
msg << "Giving up connecting ot Cahce Manager after " << tries << " tries";
log4::error(msg.str());
throw msg.str();
}
stringstream errorMsg;
int retry_delay = chooseRetryDelay(tries); /* usually 10 seconds */
/* This is the error that i am getting in my run*/
errorMsg << "Unable to connect to cache manager at " << host << ":" << port
<< ". Sleeping for " << retry_delay << " seconds and retrying."
<< " Will try " << max_connect_tries - tries -1 << " more times.";
log4::warn(errorMsg.str());
if (tries) {
#if (defined(WIN32) || defined(WIN64))
Sleep(retry_delay);
#else
sleep(retry_delay);
#endif
}
}
}
if (responseString.substring(0,2) != "ok") {
string errorMsg = "Exception from Cache Manager. See Cache Manager log in parent task "
"for traceback. \nResponse=" + responseString;
log4::error(errorMsg);
throw errorMsg;
}
return trim_left_copy(responseString.substring(2m resonseString.size() -2));
}
I've downloaded mails with Poco/Net/POP3ClientSession, I wanted to convert e-mail subject into human readable, so I tried to use neagoegab's solution from here:
https://stackoverflow.com/a/8104496/1350091
unfortunately it doesn't work:
#include <Poco/Net/POP3ClientSession.h>
#include <Poco/Net/MailMessage.h>
#include <iostream>
#include <string>
using namespace std;
using namespace Poco::Net;
#include <iconv.h>
const size_t BUF_SIZE=1024;
class IConv {
iconv_t ic_;
public:
IConv(const char* to, const char* from)
: ic_(iconv_open(to,from)) { }
~IConv() { iconv_close(ic_); }
bool convert(char* input, char* output, size_t& out_size) {
size_t inbufsize = strlen(input)+1;
return iconv(ic_, &input, &inbufsize, &output, &out_size);
}
};
int main()
{
POP3ClientSession session("poczta.o2.pl");
session.login("my mail", "my password");
POP3ClientSession::MessageInfoVec messages;
session.listMessages(messages);
cout << "id: " << messages[0].id << " size: " << messages[0].size << endl;
MailMessage message;
session.retrieveMessage(messages[0].id, message);
const string subject = message.getSubject();
cout << "Original subject: " << subject << endl;
IConv iconv_("UTF8","ISO-8859-2");
char from[BUF_SIZE];// "=?ISO-8859-2?Q?Re: M=F3j sen o JP II?=";
subject.copy(from, sizeof(from));
char to[BUF_SIZE] = "bye";
size_t outsize = BUF_SIZE;//you will need it
iconv_.convert(from, to, outsize);
cout << "converted: " << to << endl;
}
The output is:
id: 1 size: 2792
Original subject: =?ISO-8859-2?Q?Re: M=F3j sen o JP II?=
converted: =?ISO-8859-2?Q?Re: M=F3j sen o JP II?=
The interesting thing is that when I try to convert the subject with POCO it fails:
cout << "Encoded with POCO: " << MailMessage::encodeWord("Re: Mój sen o JP II", "ISO-8859-2") << endl; // output: Encoded with POCO: =?ISO-8859-2?q?Re=3A_M=C3=B3j_sen_o_JP_II?=
But the subject I want to receive is:
"Re: Mój sen o JP II"
The only succesfull way I found to convert the subject is:
https://docs.python.org/2/library/email.header.html#email.header.decode_header
So my question is -how to convert e-mail's subject in C++ into some format like UTF-8?
The relevant RFC to your situation is RFC 2047. That RFC specifies how non-ASCII data should be encoded in mail messages. The basic gist is that all bytes besides printable ASCII characters are escaped as an '=' character followed by two hexadecimal digits. Since "ó" is represented by the byte 0xF3 in ISO-8859-2, and 0xF3 is not a printable ASCII character, it is encoded as "=F3". You'll need to decode all of the encoded characters in your message.
I found out how to solve the problem (I'm not sure that it is 100% correct solution), but it looks like it is enough to use:
Poco::UTF8Encoding::convert to convert from characterCode to utf8:
#include <Poco/Net/POP3ClientSession.h>
#include <Poco/Net/MessageHeader.h>
#include <Poco/Net/MailMessage.h>
#include <Poco/UTF8Encoding.h>
#include <iostream>
#include <string>
using namespace std;
using namespace Poco::Net;
class EncoderLatin2
{
public:
EncoderLatin2(const string& encodedSubject)
{
/// encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
int charsetBeginPosition = strlen("=?");
int charsetEndPosition = encodedSubject.find("?", charsetBeginPosition);
charset = encodedSubject.substr(charsetBeginPosition, charsetEndPosition-charsetBeginPosition);
int encodingPosition = charsetEndPosition + strlen("?");
encoding = encodedSubject[encodingPosition];
if ("ISO-8859-2" != charset)
throw std::invalid_argument("Invalid encoding!");
const int lenghtOfEncodedText = encodedSubject.length() - encodingPosition-strlen("?=")-2;
extractedEncodedSubjectToConvert = encodedSubject.substr(encodingPosition+2, lenghtOfEncodedText);
}
string convert()
{
size_t positionOfAssignment = -1;
while (true)
{
positionOfAssignment = extractedEncodedSubjectToConvert.find('=', positionOfAssignment+1);
if (string::npos != positionOfAssignment)
{
const string& charHexCode = extractedEncodedSubjectToConvert.substr(positionOfAssignment + 1, 2);
replaceAllSubstringsWithUnicode(extractedEncodedSubjectToConvert, charHexCode);
}
else
break;
}
return extractedEncodedSubjectToConvert;
}
void replaceAllSubstringsWithUnicode(string& s, const string& charHexCode)
{
const int charCode = stoi(charHexCode, nullptr, 16);
char buffer[10] = {};
encodingConverter.convert(charCode, (unsigned char*)buffer, sizeof(buffer));
replaceAll(s, '=' + charHexCode, buffer);
}
void replaceAll(string& s, const string& replaceFrom, const string& replaceTo)
{
size_t needlePosition = -1;
while (true)
{
needlePosition = s.find(replaceFrom, needlePosition + 1);
if (string::npos == needlePosition)
break;
s.replace(needlePosition, replaceFrom.length(), replaceTo);
}
}
private:
string charset;
char encoding;
Poco::UTF8Encoding encodingConverter;
string extractedEncodedSubjectToConvert;
};
int main()
{
POP3ClientSession session("poczta.o2.pl");
session.login("my mail", "my password");
POP3ClientSession::MessageInfoVec messages;
session.listMessages(messages);
MessageHeader header;
MailMessage message;
auto currentMessage = messages[0];
session.retrieveHeader(currentMessage.id, header);
session.retrieveMessage(currentMessage.id, message);
const string subject = message.getSubject();
EncoderLatin2 encoder(subject);
cout << "Original subject: " << subject << endl;
cout << "Encoded: " << encoder.convert() << endl;
}
I found another solution, better than before.
Some e-mails subjects has different encodings, I noticed:
Latin2, encoded like: =?ISO-8859-2?Q?...?=
UTF-8 Base64 like:
=?utf-8?B?Wm9iYWN6Y2llIGNvIGRsYSBXYXMgcHJ6eWdvdG93YWxpxZtteSAvIHN0eWN6ZcWEIHcgTGFzZXJwYXJrdQ==?=
UTF-8 quoted printable like:
=?utf-8?Q?...?=
No encoding (if only ASCII characters) like: ...
So with POCO (Base64Decoder, Latin2Encoding, UTF8Encoding, QuotedPrintableDecoder) I managed to convert all the cases:
#include <iostream>
#include <string>
#include <sstream>
#include <Poco/Net/POP3ClientSession.h>
#include <Poco/Net/MessageHeader.h>
#include <Poco/Net/MailMessage.h>
#include <Poco/Base64Decoder.h>
#include <Poco/Latin2Encoding.h>
#include <Poco/UTF8Encoding.h>
#include <Poco/Net/QuotedPrintableDecoder.h>
using namespace std;
class Encoder
{
public:
Encoder(const string& encodedText)
{
isStringEncoded = isEncoded(encodedText);
if (!isStringEncoded)
{
extractedEncodedSubjectToConvert = encodedText;
return;
}
splitEncodedText(encodedText);
}
string convert()
{
if (isStringEncoded)
{
if (Poco::Latin2Encoding().isA(charset))
return decodeFromLatin2();
if (Poco::UTF8Encoding().isA(charset))
return decodeFromUtf8();
}
return extractedEncodedSubjectToConvert;
}
private:
void splitEncodedText(const string& encodedText)
{
/// encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
const int charsetBeginPosition = strlen(sequenceBeginEncodedText);
const int charsetEndPosition = encodedText.find("?", charsetBeginPosition);
charset = encodedText.substr(charsetBeginPosition, charsetEndPosition-charsetBeginPosition);
const int encodingPosition = charsetEndPosition + strlen("?");
encoding = encodedText[encodingPosition];
const int lenghtOfEncodedText = encodedText.length() - encodingPosition-strlen(sequenceBeginEncodedText)-strlen(sequenceEndEncodedText);
extractedEncodedSubjectToConvert = encodedText.substr(encodingPosition+2, lenghtOfEncodedText);
}
bool isEncoded(const string& encodedSubject)
{
if (encodedSubject.size() < 4)
return false;
if (0 != encodedSubject.find(sequenceBeginEncodedText))
return false;
const unsigned positionOfLastTwoCharacters = encodedSubject.size() - strlen(sequenceEndEncodedText);
return positionOfLastTwoCharacters == encodedSubject.rfind(sequenceEndEncodedText);
}
string decodeFromLatin2()
{
size_t positionOfAssignment = -1;
while (true)
{
positionOfAssignment = extractedEncodedSubjectToConvert.find('=', positionOfAssignment+1);
if (string::npos != positionOfAssignment)
{
const string& charHexCode = extractedEncodedSubjectToConvert.substr(positionOfAssignment + 1, 2);
replaceAllSubstringsWithUnicode(extractedEncodedSubjectToConvert, charHexCode);
}
else
break;
}
return extractedEncodedSubjectToConvert;
}
void replaceAllSubstringsWithUnicode(string& s, const string& charHexCode)
{
static Poco::UTF8Encoding encodingConverter;
const int charCode = stoi(charHexCode, nullptr, 16);
char buffer[10] = {};
encodingConverter.convert(charCode, (unsigned char*)buffer, sizeof(buffer));
replaceAll(s, '=' + charHexCode, buffer);
}
void replaceAll(string& s, const string& replaceFrom, const string& replaceTo)
{
size_t needlePosition = -1;
while (true)
{
needlePosition = s.find(replaceFrom, needlePosition + 1);
if (string::npos == needlePosition)
break;
s.replace(needlePosition, replaceFrom.length(), replaceTo);
}
}
string decodeFromUtf8()
{
if('B' == toupper(encoding))
{
return decodeFromBase64();
}
else // if Q:
{
return decodeFromQuatedPrintable();
}
}
string decodeFromBase64()
{
istringstream is(extractedEncodedSubjectToConvert);
Poco::Base64Decoder e64(is);
extractedEncodedSubjectToConvert.clear();
string buffer;
while(getline(e64, buffer))
extractedEncodedSubjectToConvert += buffer;
return extractedEncodedSubjectToConvert;
}
string decodeFromQuatedPrintable()
{
replaceAll(extractedEncodedSubjectToConvert, "_", " ");
istringstream is(extractedEncodedSubjectToConvert);
Poco::Net::QuotedPrintableDecoder qp(is);
extractedEncodedSubjectToConvert.clear();
string buffer;
while(getline(qp, buffer))
extractedEncodedSubjectToConvert += buffer;
return extractedEncodedSubjectToConvert;
}
private:
string charset;
char encoding;
string extractedEncodedSubjectToConvert;
bool isStringEncoded;
static constexpr const char* sequenceBeginEncodedText = "=?";
static constexpr const char* sequenceEndEncodedText = "?=";
};
int main()
{
Poco::Net::POP3ClientSession session("poczta.o2.pl");
session.login("my mail", "my password");
Poco::Net::POP3ClientSession::MessageInfoVec messages;
session.listMessages(messages);
Poco::Net::MessageHeader header;
Poco::Net::MailMessage message;
auto currentMessage = messages[0];
session.retrieveHeader(currentMessage.id, header);
session.retrieveMessage(currentMessage.id, message);
const string subject = message.getSubject();
Encoder encoder(subject);
cout << "Original subject: " << subject << endl;
cout << "Encoded: " << encoder.convert() << endl;
}
I am writing mosquitto code for consuming the message after subscribing to a particular topic. Now I want to set different configuration for mosquitto like
autosave_interval 100
persistence true
persistence_location /var/lib/mosquitto/
persistence_file mosquitto.db
But I have no idea how to set this configuration in c++. I tried to google it but could not found any result. Plz, help. Below is c++ code for mosquito
myMosq.h
/*
* myMosq.h
*
* Created on: Jul 28, 2016
* Author: nilav
*/
#include <iostream>
#ifndef MYMOSQ_H_
#define MYMOSQ_H_
#include <mosquittopp.h>
#include <mosquitto.h>
using namespace std;
class myMosq : public mosqpp::mosquittopp
{
private:
const char * host;
const char * id;
const char * topic;
int port;
int keepalive;
void on_connect(int rc);
void on_message(const struct mosquitto_message *message);
void on_disconnect(int rc);
void on_subscribe(int mid, int qos_count, const int *granted_qos);
void on_publish(int mid);
void on_unsubscribe(int mid);
public:
myMosq(const char *id, const char * _topic, const char *host, int port);
~myMosq();
bool send_message(string responseMessage);
bool receive_message();
void writeToDatabase(string query);
};
#endif
myMosq.cpp
#include <cstdio>
#include <cstring>
#include <iostream>
#include "myMosq.h"
#include <mosquittopp.h>
#include "Configuration.h"
#include "Databases.h"
using namespace std;
Configuration configuration;
myMosq::myMosq(const char * _id,const char * _topic, const char * _host, int _port) : mosquittopp(_id)
{
mosqpp::lib_init(); // Mandatory initialization for mosquitto library
this->keepalive = 60; // Basic configuration setup for myMosq class
this->id = _id;
this->port = _port;
this->host = _host;
this->topic = _topic;
connect_async(host, // non blocking connection to broker request
port,
keepalive);
loop_start(); // Start thread managing connection / publish / subscribe
};
myMosq::~myMosq() {
loop_stop(); // Kill the thread
mosqpp::lib_cleanup(); // Mosquitto library cleanup
}
bool myMosq::receive_message()
{
int set = subscribe(NULL, configuration.subscriptionTopic.c_str(),2);
return set;
}
bool myMosq::send_message(string responseMessage) {
int ret = publish(NULL,configuration.producerTopic.c_str(),strlen(responseMessage.c_str()),responseMessage.c_str(),1,false);
return (ret = MOSQ_ERR_SUCCESS);
}
void myMosq::on_disconnect(int rc) {
std::cout << ">> myMosq - disconnection(" << rc << ")" << std::endl;
}
void myMosq::on_connect(int rc)
{
if ( rc == 0 ) {
std::cout << ">> myMosq - connected with server" << std::endl;
} else {
std::cout << ">> myMosq - Impossible to connect with server(" << rc << ")" << std::endl;
}
}
void myMosq::on_message(const struct mosquitto_message *message) {
char * pchar = (char*)(message->payload);
string str(pchar);
writeToDatabase(str);
}
void myMosq::on_subscribe(int mid, int qos_count, const int *granted_qos)
{
std::cout << ">> subscription succeeded (" << mid << ") " << std::endl;
}
void myMosq::on_publish(int mid) {
std::cout << ">> myMosq - Message (" << mid << ") succeed to be published " << std::endl;
}
void myMosq::writeToDatabase(string query) {
Databases* database = new Databases(configuration.db,
configuration.dbPort, configuration.username, configuration.password,
configuration.schema);
database->writeDatabase(query);
if(database->responseMessage == "") {
database->responseMessage = "SUCCESS";
}
this->send_message(database->responseMessage);
}
void myMosq::on_unsubscribe(int mid) {
cout<<"unscubscribed";
};
The options you are seeing are for mosquitto broker which acts almost like a server.
Mosquitto C++ library is a client library and those options(e.g. autosave_interval) are not valid for a client. Regarding persistance, mosquitto C/C++ client library doesn't offer file persistance currently.
I want to broadcast OpenCV images (coming from a camera) to a distant computer in real time, it has to be done via Ethernet. The images are continuously received in standard OpenCV Mat objects. The final code has to be integrated into a C++ (Qt) application.
I have found this Python script that does the job very well.
Now I'm trying to get a C++ equivalent of that code, I managed to create an HTTP server using Boost Asio and the Simple-Web-Server project. I am able to display a static blue image/webcam camera image (not refreshed).
I have written a code but it's not working. My guess is that the data are sent only at the return of the function (which never returns).
How can I force sending the data after each iteration of the while loop?
#include "server_http.hpp"
#include <thread>
#include <boost/chrono.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/posix_time/posix_time_io.hpp>
#include <opencv2/opencv.hpp>
//#include <opencv/cv.h>
using namespace boost::posix_time;
typedef SimpleWeb::Server<SimpleWeb::HTTP> HttpServer;
cv::Mat image;
cv::VideoCapture cap;
int main()
{
cap.open(0);
if (!cap.isOpened ())
{
std::cerr << "Could not initialize capturing" << std::endl;
return (-1);
}
cap >> image;
HttpServer server(8080, 2);
// Image resource is requested
server.resource["^/cam.mjpg"]["GET"] =
[=](HttpServer::Response& response, std::shared_ptr<HttpServer::Request> request)
{
time_facet *facet = new time_facet("%d-%b-%Y %H:%M:%S");
std::cout.imbue(std::locale(std::cout.getloc(), facet));
std::cout << second_clock::local_time() << " | " << "Camera image requested!" << std::endl;
response <<
"HTTP/1.1 200 OK\r\n"
"Content-type: multipart/x-mixed-replace; boundary=--jpgboundary";
//TODO: Send header
while (1) // TODO: Allow exiting this
{
std::cout << "Send image" << std::endl;
cap >> image;
// Encode mat to jpg and copy it to content
std::vector<uchar> buf;
cv::imencode(".jpg", image, buf, std::vector<int>());
std::string img_content(buf.begin(), buf.end());
response << "--jpgboundary\r\n" << // Signal we start a new image
"Content-type: image/jpeg" <<
"Content-Length: " << img_content.length() << "\r\n" <<
"\r\n" << img_content << "\r\n";
std::this_thread::sleep_for(std::chrono::milliseconds(400));
}
};
// Anything else is requested
server.default_resource["GET"] = [](HttpServer::Response& response, std::shared_ptr<HttpServer::Request> request)
{
time_facet *facet = new time_facet("%d-%b-%Y %H:%M:%S");
std::cout.imbue(std::locale(std::cout.getloc(), facet));
std::cout << second_clock::local_time() << " | " << request->path << std::endl;
std::string content =
"<html><head></head><body>"
"<img src=\"cam.mjpg\"/>"
"</body></html>";
response <<
"HTTP/1.1 200 OK\r\n"
"Content-Length: " << content.length() << "\r\n"
"\r\n" << content;
};
std::thread server_thread([&server]()
{
server.start();
});
std::this_thread::sleep_for(std::chrono::seconds(1));
server_thread.join();
return 0;
}
EDIT 1
Based on Technik Empire comment I went back to boost examples;
In the HTTP server example the response in sent when the callback returns so I modified the callback to allow do_write() operations on the socket.
The HTML page is correctly displayed but the image is not displayed (the broken image icon is shown instead), I tried to see what happens with Wireshark but I don't know what is wrong.
Here is my handle_request function: (request_handler.cpp):
void request_handler::handle_request(const request& req, reply& rep, connection &con)
{
// Decode url to path.
std::string request_path;
if (!url_decode(req.uri, request_path))
{
rep = reply::stock_reply(reply::bad_request);
return;
}
// Request path must be absolute and not contain "..".
if (request_path.empty() || request_path[0] != '/'
|| request_path.find("..") != std::string::npos)
{
rep = reply::stock_reply(reply::bad_request);
return;
}
// Determine the file extension.
std::size_t last_slash_pos = request_path.find_last_of("/");
std::string filename;
if (last_slash_pos != std::string::npos)
filename = request_path.substr(last_slash_pos + 1);
if (filename == "cam.mjpg") // Image is requested
{
rep.status = reply::ok;
rep.headers.resize(1);
rep.headers[0].name = "Content-Type";
rep.headers[0].value = "multipart/x-mixed-replace; boundary=--jpgboundary";
rep.content.empty();
con.do_write();
rep.status = reply::none;
while (true) // FIXME: How do I handle disconnection from the client?
{
cv::Mat image(200, 300, CV_8UC3);
int random = rand() % 255 + 1;
image = cv::Scalar(random, 0, 0); // Fill image with blue
std::vector<uchar> buf;
cv::imencode(".jpg", image, buf, std::vector<int>());
std::string img_content(buf.begin(), buf.end());
rep.headers.clear();
rep.content.clear();
rep.content.append("--jpgboundary\r\n");
con.do_write();
rep.content.clear();
rep.headers.resize(2);
rep.headers[0].name = "Content-Type";
rep.headers[0].value = mime_types::extension_to_type("jpg");
rep.headers[1].name = "Content-length";
rep.headers[1].value = img_content.size();
rep.content.append(img_content);
rep.content.append("\r\n");
con.do_write();
boost::this_thread::sleep(boost::posix_time::milliseconds(500));
}
}
else // Anything but the image is requested
{
std::string content =
"<html><head></head><body>"
"Hello :)<br>"
"<img src=\"cam.mjpg\"/>"
"</body></html>";
rep.status = reply::ok;
rep.headers.resize(2);
rep.headers[0].name = "Content-Length";
rep.headers[0].value = content.length();
rep.headers[1].name = "Content-Type";
rep.headers[1].value = mime_types::extension_to_type("html");
rep.content.append(content);
con.do_write();
return;
}
}
I got it working by analyzing the packets thanks to the Firefox network analyzer, I replicated the Python headers/content answer and it works fine:
I added a reply::none reply type and made sure that if this reply was provided, no HTTP status is being sent. So in the reply::to_buffers() function I added this:
if (status != none) // Don't add status to buffer if status is "none"
buffers.push_back(status_strings::to_buffer(status));
The request_handler.cpp code looks like this:
if (filename == "cam.mjpg") // Image is requested
{
rep.status = reply::ok;
rep.headers.resize(1);
rep.headers[0].name = "Content-Type";
rep.headers[0].value = "multipart/x-mixed-replace; boundary=--jpgboundary\r\n";
con.do_write();
while (true) // FIXME: How do I handle disconnection from the client?
{
cv::Mat image(200, 300, CV_8UC3);
int random = rand() % 255 + 1;
image = cv::Scalar(random, 0, 0); // Fill image with blue
std::vector<uchar> buf;
cv::imencode(".jpg", image, buf, std::vector<int>());
std::string img_content(buf.begin(), buf.end());
rep.status = reply::none;
rep.headers.resize(0);
rep.content.clear();
rep.content.append("--jpgboundary");
rep.content.append("\r\n");
rep.content.append("Content-Type: image/jpeg");
rep.content.append("\r\n");
rep.content.append("Content-length: "+boost::lexical_cast<std::string>(img_content.length()));
rep.content.append("\r\n");
rep.content.append("\r\n");
rep.content.append(img_content);
rep.content.append("\r\n");
con.do_write();
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
}
}
else // Anything but the image is requested
{
std::string content =
"<html><head></head><body>"
"<img src=\"cam.mjpg\"/>"
"</body></html>";
rep.status = reply::ok;
rep.headers.resize(2);
rep.headers[0].name = "Content-Length";
rep.headers[0].value = content.length();
rep.headers[1].name = "Content-Type";
rep.headers[1].value = mime_types::extension_to_type("html");
rep.content.append(content);
con.do_write();
return;
}
Improvements suggestions are welcome in the comments. I don't know how to handle a disconnection from the client (exiting the while loop) so at the moment the server will only work with 1 request; after that it's stucked in the while loop.
Http_server.hpp
#ifndef HTTPSERVER_HPP_INCLUDED
#define HTTPSERVER_HPP_INCLUDED
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <opencv2/core/core.hpp>
#include <boost/thread.hpp>
using boost::asio::ip::tcp;
typedef boost::shared_ptr<tcp::socket> socket_ptr;
class HttpServer
{
public:
std::map<std::string, std::string> winnames;
std::map<std::string,int> requestcounts;
std::map<std::string,std::vector<unsigned char> > jpegbuffers;
short port;
HttpServer();
void run(int portno);
boost::shared_mutex mut;
boost::condition_variable_any cond;
int httpdelay;
void IMSHOW(std::string win, cv::Mat mat);
int compression;
bool is_debug;
private:
int it;
void server(int port);
void session(socket_ptr sock);
void handleinfo(socket_ptr sock);
void handlewindows(socket_ptr sock);
void handlemjpeg(socket_ptr sock,std::string winname);
void handlejpg(socket_ptr sock,std::string winname);
void handle404(socket_ptr sock);
};
#endif
Http_server.cpp
#include "http_server.hpp"
#include <fstream>
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <opencv2/opencv.hpp>
#include <boost/lexical_cast.hpp>
namespace bfs= boost::filesystem;
using namespace std;
using boost::lexical_cast;
// Helper functions
#if defined(unix) || defined(__unix) || defined(__unix__) \
|| defined(linux) || defined(__linux) || defined(__linux__) \
|| defined(sun) || defined(__sun) \
|| defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) \
|| defined(__FreeBSD__) || defined __DragonFly__ \
|| defined(sgi) || defined(__sgi) \
|| defined(__MACOSX__) || defined(__APPLE__) \
|| defined(__CYGWIN__)
#define is_nix
#endif
#if defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) \
|| defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
#define is_win
#endif
#ifdef is_win
#include <windows.h>
#define SLEEP(ms) Sleep(ms)
#endif
#ifdef is_nix
#define SLEEP(ms) usleep(ms*1000)
#endif
std::vector<std::string> &dssplit(const std::string &s, char delim, std::vector<std::string> &elems) {
std::stringstream ss(s);
std::string item;
while (getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
std::vector<std::string> dssplit(const std::string &s, char delim) {
std::vector<std::string> elems;
return dssplit(s, delim, elems);
}
void removeEmptyStrings(std::vector<std::string>& strings)
{
std::vector<std::string>::iterator it = remove_if(strings.begin(), strings.end(), mem_fun_ref(&std::string::empty));
strings.erase(it, strings.end());
}
bool hasEnding(std::string const &fullString, std::string const &ending)
{
if (fullString.length() >= ending.length()) {
return (0 == fullString.compare(fullString.length() - ending.length(), ending.length(), ending));
}
else {
return false;
}
}
bool startswith(std::string const &src, std::string const &start)
{
if (src.compare(0, start.length(), start) == 0)
{
return true;
}
return false;
}
std::string urldecode(std::string &src) {
std::string ret;
char ch;
int ii;
for (size_t i = 0; i<src.length(); i++) {
if (int(src[i]) == 37) {
sscanf(src.substr(i + 1, 2).c_str(), "%x", &ii);
ch = static_cast<char>(ii);
ret += ch;
i = i + 2;
}
else {
ret += src[i];
}
}
return (ret);
}
// Server implementation
HttpServer::HttpServer() :compression(70), is_debug(true), httpdelay(100)
{
}
void HttpServer::IMSHOW(std::string win, cv::Mat mat)
{
winnames[win] = lexical_cast<string>(mat.cols) + "," + lexical_cast<string>(mat.rows);
if (is_debug)
{
cv::imshow(win, mat);
}
else
{
//cvDestroyWindow(win.c_str());
}
if (requestcounts[win] > 0)
{
cv::Mat towrite;
if (mat.type() == CV_8UC1)
{
cvtColor(mat, towrite, CV_GRAY2BGR);
}
else if (mat.type() == CV_32FC3)
{
double minVal, maxVal;
minMaxLoc(mat, &minVal, &maxVal);
mat.convertTo(towrite, CV_8U, 255.0 / (maxVal - minVal), -minVal * 255.0 / (maxVal - minVal));
}
else{
towrite = mat;
}
std::vector<uchar> buffer;
std::vector<int> param(2);
param[0] = CV_IMWRITE_JPEG_QUALITY;
param[1] = compression;
imencode(".jpg", towrite, buffer, param);
jpegbuffers[win].swap(buffer);
}
}
void HttpServer::run(int portno)
{
port=portno;
boost::thread t(boost::bind(&HttpServer::server,this,port));
}
void HttpServer::server(int port)
{
try
{
boost::asio::io_service io_service;
io_service.run();
tcp::acceptor a(io_service, tcp::endpoint(tcp::v4(), port));
for (;;)
{
socket_ptr sock(new tcp::socket(io_service));
a.accept(*sock);
boost::thread t(boost::bind(&HttpServer::session, this, sock));
}
}
catch (boost::exception & e)
{
std::cout << "OMG!" << boost::diagnostic_information(e)<<endl;
}
}
void HttpServer::session(socket_ptr sock)
{
try
{
boost::system::error_code ec;
boost::asio::streambuf sbuffer;
boost::asio::read_until(* sock, sbuffer, "\0", ec );
const char* header=boost::asio::buffer_cast<const char*>(sbuffer.data());
std::string reqStr(header,header+sbuffer.size());
sbuffer.consume(sbuffer.size());
std::vector<std::string> strs;
strs = dssplit(reqStr,' ');
if(strs.size()>1)
{
std::string requesturl = urldecode(strs[1]);
std::vector<std::string> splited=dssplit(requesturl,'/');
removeEmptyStrings(splited);
if(splited.size()==1)
{
if(startswith(splited[0],"windows"))
{
handlewindows(sock);
}else if(startswith(splited[0],"info"))
{
handleinfo(sock);
}else if(hasEnding(splited[0],".mjpg"))
{
handlemjpeg(sock,splited[0].substr(0,splited[0].size()-5));
}else if(hasEnding(splited[0],".jpg") || splited[0].find(".jpg?")!=string::npos)
{
handlejpg(sock,splited[0]);
}else
{
handle404(sock);
}
}else
{
handle404(sock);
}
sock->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
}
}catch(const std::exception& ex)
{
boost::system::error_code ec;
boost::asio::ip::tcp::endpoint endpoint = sock->remote_endpoint(ec);
if(!ec)
{
sock->shutdown(boost::asio::ip::tcp::socket::shutdown_both);
}
//DPRINTERR(ex.what());
}catch(const std::string& ex)
{
boost::system::error_code ec;
boost::asio::ip::tcp::endpoint endpoint = sock->remote_endpoint(ec);
if(!ec)
{
sock->shutdown(boost::asio::ip::tcp::socket::shutdown_both);
}
}
}
void HttpServer::handleinfo(socket_ptr sock)
{
boost::system::error_code error;
boost::asio::streambuf sbuffer;
std::ostream response_stream(&sbuffer);
string retstr;
for (std::map<std::string,std::string>::iterator it=winnames.begin(); it!=winnames.end(); ++it)
{
string wname =it->first;
int rcnt = 0;
if(requestcounts.find(wname)!=requestcounts.end())
{
rcnt=requestcounts[wname];
}
retstr+=boost::str(boost::format("{"
"\"name\":\"%s\","
"\"reqCnt\":%d,"
"\"size\":\"%s\""
"},"
)
%wname
%rcnt
%it->second
);
}
if(retstr.size()>0) retstr.resize(retstr.size()-1);
retstr=boost::str(boost::format("{"
"\"windows\":[%s],"
"\"version\":\"%s\","
"\"fps\":%s"
"}"
)
%retstr
%"0.0"
% to_string(0.)
);
response_stream << "HTTP/1.1 200 OK\r\n";
response_stream << "Access-Control-Allow-Origin: *\r\n";
response_stream << "Content-Type: text/plain\r\n\r\n";
response_stream << retstr << "\r\n\r\n";
boost::asio::write(*sock, sbuffer);
}
void HttpServer::handlewindows(socket_ptr sock)
{
boost::system::error_code error;
boost::asio::streambuf sbuffer;
std::ostream response_stream(&sbuffer);
string retstr;
for (std::map<std::string,std::string>::iterator it=winnames.begin(); it!=winnames.end(); ++it)
{
string wname =it->first;
int rcnt = 0;
if(requestcounts.find(wname)!=requestcounts.end())
{
rcnt=requestcounts[wname];
}
retstr+=boost::str(boost::format("{"
"\"name\":\"%s\","
"\"reqCnt\":%d,"
"\"size\":\"%s\""
"},"
)
%wname
%rcnt
%it->second
);
}
if(retstr.size()>0) retstr.resize(retstr.size()-1);
retstr="{\"windows\":["+retstr+"]}";
response_stream<<"HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n\r\n"<<
retstr<<"\r\n\r\n";
boost::asio::write(*sock, sbuffer);
}
void HttpServer::handlemjpeg(socket_ptr sock,std::string winname)
{
if(requestcounts.find(winname)==requestcounts.end())
{
handle404(sock);
return;
}
std::string frame=winname;
//boost::shared_lock<boost::shared_mutex> lock(mut);
//lock.lock();
requestcounts[frame]++;
//lock.unlock();
boost::system::error_code error;
boost::asio::streambuf sbuffer;
std::ostream response_stream(&sbuffer);
response_stream<<"HTTP/1.1 200 OK\r\n";
response_stream<<"Content-Type: multipart/mixed;boundary=b\r\n";
response_stream<<"Cache-Control: no-store\r\n";
response_stream<<"Pragma: no-cache\r\n";
response_stream<<"Audio Mode : None\r\n";
response_stream<<"Connection: close\r\n";
response_stream<<"\r\n";
boost::asio::write(*sock, sbuffer);
for(;;)
{
try
{
if( (jpegbuffers.count(frame)<0 ||
jpegbuffers[frame].size()<4) ||
(jpegbuffers[frame][0]!=0xff && jpegbuffers[frame][1]!=0xd8 &&
jpegbuffers[frame][jpegbuffers[frame].size()-2]!=0xff && jpegbuffers[frame][jpegbuffers[frame]. size()-1]!=0xd9))
{
SLEEP(10);
continue;
}
//boost::shared_lock<boost::shared_mutex> lock(mut);
response_stream<<"--b\r\n";
response_stream<<"Content-Type: image/jpeg\r\n";
response_stream<<"Content-length: "<<jpegbuffers[frame].size()<<"\r\n";
response_stream<<"\r\n";
boost::asio::write(*sock, sbuffer);
boost::asio::write(*sock,boost::asio::buffer(jpegbuffers[frame], jpegbuffers[frame].size()));
//lock.unlock();
SLEEP(httpdelay);
}
catch (std::exception& e)
{
SLEEP(50);
//lock.lock();
requestcounts[frame]--;
//lock.unlock();
return;
}
}
//lock.lock();
requestcounts[frame]--;
//lock.unlock();
}
void HttpServer::handlejpg(socket_ptr sock,std::string winname)
{
if(winname.find("?")!=string::npos)
{
winname = winname.substr(0,winname.find("?"));
}
winname =winname.substr(0,winname.size()-4);
std::string frame=winname;
requestcounts[frame]++;
boost::system::error_code error;
boost::asio::streambuf sbuffer;
std::ostream response_stream(&sbuffer);
jpegbuffers[frame].clear();
for(;;)
{
try
{
if( (jpegbuffers.count(frame)<0 ||
jpegbuffers[frame].size()<4) ||
(jpegbuffers[frame][0]!=0xff && jpegbuffers[frame][1]!=0xd8 &&
jpegbuffers[frame][jpegbuffers[frame].size()-2]!=0xff && jpegbuffers[frame][jpegbuffers[frame]. size()-1]!=0xd9))
{
SLEEP(10);
continue;
}
response_stream<<"HTTP/1.1 200 OK\r\n";
response_stream<<"Content-Type: image/jpeg\r\n";
response_stream<<"Cache-Control: no-store\r\n";
response_stream<<"Access-Control-Allow-Origin: *\r\n";
response_stream<<"Pragma: no-cache\r\n";
response_stream<<"Content-length: "<<jpegbuffers[frame].size()<<"\r\n";
response_stream<<"Connection: close\r\n";
response_stream<<"\r\n";
boost::asio::write(*sock, sbuffer);
boost::asio::write(*sock,boost::asio::buffer(jpegbuffers[frame], jpegbuffers[frame].size()));
break;
}
catch (std::exception& e)
{
//DPRINTERR( "net exceptoin:"+std::string(e.what()));
SLEEP(50);
requestcounts[frame]--;
return;
}
}
requestcounts[frame]--;
}
void HttpServer::handle404(socket_ptr sock)
{
boost::system::error_code error;
boost::asio::streambuf sbuffer;
std::ostream response_stream(&sbuffer);
response_stream<<"HTTP/1.1 404 Not Found\r\n"
"Content-Type: text/html\r\n"
"Connection: close\r\n"
"Content-Length: 132\r\n\r\n"
"<html>\r\n"
"<head><title>404 Not Found</title></head>\r\n"
"<body bgcolor=\"white\">\r\n"
"<center><h1>404 Not Found</h1></center>\r\n"
"</body>\r\n"
"</html>\r\n";
boost::asio::write(*sock, sbuffer);
}
Main.cpp
#include <opencv2/opencv.hpp>
#include "http_server.hpp"
#include <iostream>
#include <fstream>
using namespace cv;
#define MJPGFILE_BUFFER_SIZE 10240
class MjpgFileCapture{
public:
static double lastframeseen;
MjpgFileCapture() {};
MjpgFileCapture(std::string filepath)
{
filepath_ = filepath;
is_inited_ = false;
skip_ = true;
imgready_ = false;
ff_ = false;
readbytes_ = -2;
i_ = 0;
};
void init();
MjpgFileCapture& operator >>(cv::Mat& out);
private:
std::string filepath_;
bool is_inited_;
std::ifstream ifstream_;
std::vector<char> data_;
bool skip_;
bool imgready_;
bool ff_;//have we seen ff byte?
long long readbytes_;
char ca_[MJPGFILE_BUFFER_SIZE];
int i_;//loop index
};
void MjpgFileCapture::init()
{
is_inited_ = true;
ifstream_ = std::ifstream(filepath_.c_str(), std::ios::binary);
}
MjpgFileCapture& MjpgFileCapture::operator >> (cv::Mat& out)
{
out = Mat();
if (!is_inited_)
{
init();
}
while (1)
{
uchar c;
if (readbytes_ != 0 && readbytes_ != -1)
{
if (i_ >= readbytes_)
{
ifstream_.read(ca_, MJPGFILE_BUFFER_SIZE);
readbytes_ = ifstream_.gcount();
i_ = 0;
}
for (; i_ < readbytes_; i_++)
{
c = ca_[i_];
if (ff_ && c == 0xd8)
{
skip_ = false;
data_.push_back((uchar)0xff);
}
if (ff_ && c == 0xd9)
{
imgready_ = true;
data_.push_back((uchar)0xd9);
skip_ = true;
}
ff_ = c == 0xff;
if (!skip_)
{
data_.push_back(c);
}
if (imgready_)
{
if (data_.size() != 0)
{
cv::Mat data_mat(data_);
cv::Mat frame(imdecode(data_mat, 1));
out = frame;
}
else
{
printf("warning:image is ready and data is empty. Likely bug.");
}
imgready_ = false;
skip_ = true;
data_.clear();
return *this;
}
}
}
else
{
//own exception class
throw std::string("zero byte read:probably end of file.");
}
}
return *this;
}
HttpServer* server = 0;
void file_loop()
{
MjpgFileCapture cap("C:/v/frame.mjpg");
while (true)
{
Mat im;
cap >> im;
server->IMSHOW("im", im);
imshow("im", im);
if (waitKey(1) == 27)
exit(0);
}
}
int main(int argc, char** argv)
{
server = new HttpServer;
//server->port = 8080;
server->run(8080);
while (true)
{
try{
file_loop();
}
catch (...)
{
}
}
return 0;
}
Usage
server->IMSHOW("im",mat);//register mat as window name im in server, accessible from browser as http://localhost:8080/im.jpg or http://localhost:8080/im.mjpg
http://localhost:8080/windows (show all registered windows)
I am trying to perform XMPP Handshake Flow mentioned at https://developers.google.com/cloud-print/docs/rawxmpp.
This allows the device to subscribe and receive notifications.
As of now, I have explored the following options:
1) libcurl
2) Gloox C/C++
3) TXMPP
4) Libjingle
Which option would be a good choice to start with? I would like to consider support for and maintenance of the library as a major factor.
Following is my solution using Gloox C/C++ library to perform XMPP Handshake Flow:
#include <cassert>
#include <iostream>
#include <boost/make_shared.hpp>
#include <iq.h>
#include <parser.h>
#include <base64.h>
#include <connectiontcpclient.h>
#include <connectiontls.h>
#include <connectiondatahandler.h>
#include <connectionhttpproxy.h>
#include <logsink.h>
#include <client.h>
#include <connectionsocks5proxy.h>
using namespace gloox;
using namespace std;
const string proxyHost = ""; //specify proxy server name
const int proxyPort = 0; //specify proxy port number
const string xmppHost = "talk.google.com";
const int xmppPort = 5222;
Client *c;
ConnectionBase *client_ ;
ConnectionTCPClient* conn0;
ConnectionHTTPProxy* conn2;
class Bot: public ConnectionDataHandler, TagHandler, TLSHandler{
public:
Bot(): parser_(this)
{
conn0 = new ConnectionTCPClient(this, log_, proxyHost, proxyPort);
ConnectionHTTPProxy* conn2 = new ConnectionHTTPProxy( this, conn0, log_, xmppHost, xmppPort);
client_ = conn0;
ConnectionError ce = ConnNoError;
ce = conn2->connect();
assert(ce == ConnNoError);
conn2->receive();
}
virtual void handleConnect(const ConnectionBase* con) {
send("<stream:stream to=\"gmail.com\" xml:lang=\"en\" version=\"1.0\" xmlns:stream=\"http://etherx.jabber.org/streams\" xmlns=\"jabber:client\">\r\n");
}
virtual void handleReceivedData(const ConnectionBase* con, const string& data) {
cerr << "[recv] " << data << endl;
string copied = data;
int pos = parser_.feed(copied);
assert(pos < 0);
}
virtual void handleTag(Tag* tag) {
if (tag->name() == "stream" && tag->xmlns() == "http://etherx.jabber.org/streams") {
sid_ = tag->findAttribute("id");
} else{
if (tag->name() == "features") {
if (tag->hasChild("starttls", "xmlns", "urn:ietf:params:xml:ns:xmpp-tls")) {
send(Tag("starttls", "xmlns", "urn:ietf:params:xml:ns:xmpp-tls").xml());
}
else if (tag->hasChild("mechanisms", "xmlns", "urn:ietf:params:xml:ns:xmpp-sasl") && tag->findChild("mechanisms")->hasChildWithCData(
"mechanism", "X-OAUTH2"))
{
Tag a("auth", "xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
a.addAttribute("mechanism", "X-OAUTH2");
a.addAttribute("service", "chromiumsync");
a.addAttribute("allow-generated-jid", "true");
a.addAttribute("client-uses-full-bind-result", "true");
a.addAttribute("auth", "http://www.google.com/talk/protocol/auth");
string credential;
credential.append("\0", 1);
credential.append(""); //Specify Bare JID
credential.append("\0", 1);
credential.append(""); //Specify Access Token
a.setCData(Base64::encode64(credential));
send(a.xml());
}
else if (tag->hasChild("bind", "xmlns", "urn:ietf:params:xml:ns:xmpp-bind")) {
Tag iq("iq", "xmlns", "jabber:client");
iq.addAttribute("type", "set");
iq.addAttribute("id", "0");
Tag *bind = new Tag("bind", "xmlns", "urn:ietf:params:xml:ns:xmpp-bind");
Tag *resource = new Tag("resource");
resource->setCData("GCPResource");
bind->addChild(resource);
iq.addChild(bind);
send(iq.xml());
}
}
else if (tag->name() == "proceed" && tag->xmlns() == "urn:ietf:params:xml:ns:xmpp-tls") {
ConnectionTLS* encryption_client = new ConnectionTLS(this, conn0, log_);
encryption_client->registerTLSHandler(this);
client_ = encryption_client;
ConnectionError ret = encryption_client->connect();
assert(ret == ConnNoError);
}
else if (tag->name() == "success" && tag->xmlns() == "urn:ietf:params:xml:ns:xmpp-sasl") {
send("<stream:stream to=\"gmail.com\" xml:lang=\"en\" version=\"1.0\" xmlns:stream=\"http://etherx.jabber.org/streams\" xmlns=\"jabber:client\">\r\n");
}
else if (tag->name() == "iq") {
if (tag->hasChild("bind", "xmlns", "urn:ietf:params:xml:ns:xmpp-bind")) {
resource_ = tag->findChild("bind")->findChild("jid")->cdata();
Tag iq("iq");
iq.addAttribute("type", "set");
iq.addAttribute("id", "1");
iq.addChild(new Tag("session", "xmlns", "urn:ietf:params:xml:ns:xmpp-session"));
send(iq.xml());
//Step 2: Subscribing for notifications
if (tag->hasAttribute("type", "result")) {
Tag iq("iq");
iq.addAttribute("type", "set");
iq.addAttribute("to", ""); //Specify Bare JID
iq.addAttribute("id", "3");
Tag *bind = new Tag("subscribe", "xmlns", "google:push");
Tag *resource = new Tag("item");
resource->addAttribute("channel", "cloudprint.google.com");
resource->addAttribute("from", "cloudprint.google.com");
bind->addChild(resource);
iq.addChild(bind);
send(iq.xml());
}
}
}
}
}
virtual void handleEncryptedData(const TLSBase* tls,
const string& data) {
cout << "handleEncryptedData" << endl;
}
virtual void handleDecryptedData(const TLSBase* tls,
const string& data) {
cout << "handleDecryptedData" << endl;
}
virtual void handleHandshakeResult(const TLSBase* tls, bool,
CertInfo& cert) {
cout << "handleHandshakeResult" << endl;
}
virtual void handleDisconnect(const ConnectionBase* con, ConnectionError) {
cout << "handleDisconnect" << endl;
}
private:
LogSink log_;
string sid_;
string resource_;
Parser parser_;
ConnectionBase *client_;
void send(const string &data) {
cerr << "[send] " << data << endl;
client_->send(data);
}
};
int main() {
Bot bot;
}