Why mysql_init(nullptr) leads to segmentation fault in the following code? - c++

My code is as follow:
main.cc
int main(void){
int port, sqlPort, ConnectionNum, threadNum;
string sqlUser, sqlPwd, DBName;
try{
Config Settings("./config.ini");
Settings.Read("port",port);
Settings.Read("sqlPort",sqlPort);
Settings.Read("sqlUser",sqlUser);
Settings.Read("sqlPwd",sqlPwd);
Settings.Read("DBName",DBName);
Settings.Read("ConnectionNum",ConnectionNum);
Settings.Read("threadNum",threadNum);
}catch(File_Not_Found& e){
cerr << "Cannot Find the Configuration File:" << e.what() << endl;
exit(1);
}catch(Key_Not_Found& e){
cerr << "Cannot Find the Keyword:" << e.what() << endl;
exit(1);
}
WebServer server(port, 5000, sqlPort, sqlUser, sqlPwd,DBName, ConnectionNum, threadNum );
//server.Start();
return 0;
}
WebServer.cc
WebServer::WebServer(int port, int timeoutMS, int sqlPort,
const std::string& sqlUser, const std::string& sqlPwd,
const std::string& dbName, int connPoolNum, int threadNum)
:m_port(port), m_timeoutMS(timeoutMS),
m_epoller(new Epoller()),m_timer(new TimerHeap()){
assert(port < 65535 && port > 1024);
m_srcDir = get_current_dir_name();
assert(m_srcDir);
strncat(m_srcDir, "/resources/", 16);
HttpConn::userCnt = 0;
HttpConn::srcDir = m_srcDir;
connPool->init("localhost", sqlUser, sqlPwd, dbName, sqlPort, connPoolNum);
threadPool->start(threadNum);
m_isRunning = InitSocket();
if(m_isRunning){
LOG_INFO << "===================== Server Init Success =====================";
LOG_INFO << "Port : " << m_port << ", srcDir :" << m_srcDir;
LOG_INFO << "threadNum : " << threadNum << ", ConnectionNum : " << connPoolNum;
}else{
LOG_FATAL << "Socket Init Failure";
}
}
SqlConnectionPool.cc
ConnectionPool *ConnectionPool::GetInstance()
{
static ConnectionPool connPool;
return &connPool;
}
void ConnectionPool::init(string url, string User, string PassWord, string DBName, int Port, int MaxConn)
{
m_url = url;
m_Port = Port;
m_User = User;
m_PassWord = PassWord;
m_DatabaseName = DBName;
for (int i = 0; i < MaxConn; i++)
{
MYSQL *con = NULL;
con = mysql_init(con); //segmentation fault happened!!
if (con == NULL)
{
LOG_FATAL << "MySQL Init Error";
}
con = mysql_real_connect(con, url.c_str(), User.c_str(), PassWord.c_str(), DBName.c_str(), Port, NULL, 0);
if (con == NULL)
{
LOG_FATAL << "MySQL Get Real Connection Error";
}
connList.push_back(con);
++m_FreeConn;
}
m_MaxConn = m_FreeConn;
}
As is shown in the picture above, the pointer con is always nullptr and it leads to segmentation fault. And I write another code to test SqlConnectionPool.cc file, but this time it works perfectly.
SqlConnectionPool_test.cc(part of code)
ConnectionPool* connPool = ConnectionPool::GetInstance();
unordered_map<string, string> users;
vector<thread> thread_pool;
int main(void){
connPool->init("localhost", "root", "123123", "zjwdb", 3306, 8);
{
MYSQL *mysql = NULL;
connectionRAII mysqlcon(&mysql, connPool);
if(mysql_query(mysql, "CREATE TABLE user_test(username VARCHAR(100) NOT NULL,passwd VARCHAR(60) NOT NULL,PRIMARY KEY ( username ));")){
LOG_ERROR << "CREATE TABLE user_test Failed : " << mysql_error(mysql);
}
if(!Register("test1", "test1")){
LOG_ERROR << "INSERT test user Failed";
}
if(!Register("test2", "test2")){
LOG_ERROR << "INSERT test user Failed";
}
if(!Register("test3", "test3")){
LOG_ERROR << "INSERT test user Failed";
}
showUsers(mysql);
}
CountDownLatch latch(4);
thread_pool.emplace_back(login, "test1", "test1");
thread_pool.emplace_back(login, "test5", "test5");
thread_pool.emplace_back(login, "test1", "test");
thread_pool.emplace_back([](CountDownLatch& latch, string const& name, string const& passwd)
{
latch.countDown();
latch.wait();
Register(name, passwd);
login(name, passwd);
}
, std::ref(latch), "test_user1", "123123");
thread_pool.emplace_back([](CountDownLatch& latch, string const& name, string const& passwd)
{
latch.countDown();
latch.wait();
Register(name, passwd);
login(name, passwd);
}
, std::ref(latch), "test_user2", "123123");
thread_pool.emplace_back([](CountDownLatch& latch, string const& name, string const& passwd)
{
latch.countDown();
latch.wait();
Register(name, passwd);
login(name, passwd);
}
, std::ref(latch), "test_user3", "123123");
thread_pool.emplace_back([](CountDownLatch& latch, string const& name, string const& passwd)
{
latch.countDown();
latch.wait();
Register(name, passwd);
login(name, passwd);
}
, std::ref(latch), "test3", "test3");
for(auto& th : thread_pool){
if(th.joinable())
th.join();
}
MYSQL* conn_temp = NULL;
connectionRAII mysqlconn_temp(&conn_temp, connPool);
showUsers(conn_temp);
dropTheTable();
return 0;
}
That really confuse me. Is it a bug of libmysql.lib?? If it is, how should I slove it?
PS:
My Ubuntu version is Ubuntu-16.04 and the lib version is libmysqlclient.so.20.
The SqlConnectionPool_test.cc is running in a multithread envirnment. Reason has it that this test file is more likely to crash becuase mysql_init is not thread-safe. But it can work perfectly.
Please let me know if you need more information, thanks!

Related

How to manipulate libevent bufferevents outside of callbacks

I use libevent to write client-server application, on client side i want to continiusly wait for intput from console. I tried to run event_base_dispatch in thread, and in main thread ask for input string and add it to bufferevent.
std::thread libevthr(libev_start, base);
std::string s;
do
{
cin >> s;
bufferevent_write(bev, "Hello, world!", 13);
} while(s != "xxx");
libevthr.join();
For some reason this doesn't work, but if i put bufferevent_write inside one of callbacks, it works fine
void event_cb(struct bufferevent *bev, short events, void *ptr)
{
if (events & BEV_EVENT_CONNECTED) {
/* We're connected to 127.0.0.1 Ordinarily we'd do
something here, like start reading or writing. */
bufferevent_write(bev, "Hello, world!", 13);
std::cout << "Connected" << std::endl;
}
if (events & BEV_EVENT_ERROR) {
if (EVUTIL_SOCKET_ERROR() == 10054)
cout << "Server stopped working!" << endl;
else
cout << "Error has happened" << endl;
}
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR))
{
bufferevent_free(bev);
}
}
Can you explain how should i write this correctly?
Sorry if i have any mistakes in english.
Full code here:
#include "UClient.h"
#include <iostream>
#include "event2/event.h"
#include "event2/listener.h"
#include "event2/bufferevent.h"
#include "event2/buffer.h"
#include <thread>
using std::cout, std::cin, std::endl;
void event_cb(struct bufferevent *bev, short events, void *ptr)
{
if (events & BEV_EVENT_CONNECTED) {
/* We're connected to 127.0.0.1 Ordinarily we'd do
something here, like start reading or writing. */
bufferevent_write(bev, "Hello, world!", 13);
std::cout << "Connected" << std::endl;
}
if (events & BEV_EVENT_ERROR) {
if (EVUTIL_SOCKET_ERROR() == 10054)
cout << "Server stopped working!" << endl;
else
cout << "Error has happened" << endl;
}
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR))
{
bufferevent_free(bev);
}
}
void write_cb(struct bufferevent *bev, void *ctx)
{
cout << 'Data was written' << endl;
}
void libev_start(event_base *base)
{
event_base_dispatch(base);
}
int main()
{
int port = 9554;
struct event_base *base;
struct bufferevent *bev;
struct sockaddr_in cl_inf;
if (!initWinsock()) {
perror("Failed to initialize Winsock");
return 1;
}
base = event_base_new();
ZeroMemory(&cl_inf, sizeof(cl_inf));
in_addr serv_ip;
inet_pton(AF_INET, "127.0.0.1", &serv_ip);
cl_inf.sin_family = AF_INET;
cl_inf.sin_addr = serv_ip;
cl_inf.sin_port = htons(port);
bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, NULL, write_cb, event_cb, NULL);
if (bufferevent_socket_connect(bev,
(struct sockaddr *)&cl_inf, sizeof(cl_inf)) < 0) {
/* Error starting connection */
std::cout << "Can't connect to server!" << std::endl;
bufferevent_free(bev);
return -1;
}
bufferevent_enable(bev, EV_READ | EV_WRITE);
std::thread libevthr(libev_start, base);
std::string s;
do
{
cin >> s;
} while(s != "xxx");
libevthr.join();
std::cout << "client finished working";
return 0;
}

crypto++ aes GCM-AEAD decrypt throw exception hash or MAC not valid' in cpp

First of all, I am a beginner to C ++
i try to encrypt and decrypt data in c++ with crypto++ library
and i create custom class for this
class:
#ifndef CRYPTODATA
#define CRYPTODATA
typedef unsigned char byte;
#include <iostream>
#include "stdafx.h"
#include "assert.h"
using std::cerr;
using std::cout;
#include "cryptopp/hex.h"
using CryptoPP::HexDecoder;
using CryptoPP::HexEncoder;
#include "cryptopp/cryptlib.h"
using CryptoPP::AuthenticatedSymmetricCipher;
using CryptoPP::BufferedTransformation;
#include <string>
using std::string;
#include "cryptopp/filters.h"
using CryptoPP::AuthenticatedDecryptionFilter;
using CryptoPP::AuthenticatedEncryptionFilter;
using CryptoPP::StringSink;
using CryptoPP::StringSource;
#include "cryptopp/aes.h"
using CryptoPP::AES;
#include "cryptopp/gcm.h"
using CryptoPP::GCM;
using CryptoPP::GCM_TablesOption;
#include <string>
namespace CryptoData{
class CryptoData{
public:
CryptoData(){
std::string _key = "mohsensalamkojayikhoobikhosshhaa";
std::string _iv = "mohsensalamk";
std::copy(_key.begin(), _key.end(), this->key);
std::copy(_iv.begin(), _iv.end(), this->iv);
pad = (16, (char)0x00);
}
CryptoData(std::string _key, std::string _iv, std::string _pad):pad(_pad){
std::copy(_key.begin(), _key.end(), this->key);
std::copy(_iv.begin(), _iv.end(), this->iv);
std::copy(_pad.begin(), _pad.end(), this->pad);
}
~CryptoData() {
}
std::string _encrypting(std::string _data){
return this->encryptData(_data);
}
std::string _decrypting(std::string _data) {
return this->decryptData(_data);
}
protected:
std::string decryptData(std::string _data) {
std::string radata, rpdata;
try
{
GCM<AES>::Decryption d;
d.SetKeyWithIV(key, sizeof(key), iv, sizeof(iv));
string enc = cipher.substr(0, cipher.length() - TAG_SIZE);
string mac = cipher.substr(cipher.length() - TAG_SIZE);
assert(cipher.size() == enc.size() + mac.size());
assert(enc.size() == pad.size());
assert(TAG_SIZE == mac.size());
radata = _data;
AuthenticatedDecryptionFilter df(d, NULL,
AuthenticatedDecryptionFilter::MAC_AT_BEGIN |
AuthenticatedDecryptionFilter::THROW_EXCEPTION,
TAG_SIZE);
df.ChannelPut("", (const byte*)mac.data(), mac.size());
df.ChannelPut("AAD", (const byte*)_data.data(), _data.size());
df.ChannelPut("", (const byte*)enc.data(), enc.size());
df.ChannelMessageEnd("AAD");
df.ChannelMessageEnd("");
bool b = false;
b = df.GetLastResult();
assert(true == b);
string retrieved;
size_t n = (size_t)-1;
df.SetRetrievalChannel("");
n = (size_t)df.MaxRetrievable();
retrieved.resize(n);
if (n > 0)
{
df.Get((byte*)retrieved.data(), n);
}
rpdata = retrieved;
printf("done6 \n");
assert(rpdata == pad);
cout << "its a decrypted data " << radata << " \n";
}
catch (CryptoPP::InvalidArgument& e)
{
cerr << "Caught InvalidArgument..." << endl;
}
catch (CryptoPP::AuthenticatedSymmetricCipher::BadState& e)
{
cerr << "Caught BadState..." << endl;
}
catch (CryptoPP::HashVerificationFilter::HashVerificationFailed& e)
{
cerr << "Caught HashVerificationFailed..." << endl;
cerr << e.what() << endl;
}
return radata;
}
std::string encryptData(std::string _data){
std::string encoded;
try
{
GCM<AES>::Encryption e;
e.SetKeyWithIV(key, sizeof(key), iv, sizeof(iv));
AuthenticatedEncryptionFilter ef(e,
new StringSink(cipher), false, TAG_SIZE); // AuthenticatedEncryptionFilter
ef.ChannelPut("AAD", (const byte*)_data.data(), _data.size());
ef.ChannelMessageEnd("AAD");
ef.ChannelPut("", (const byte*)pad.data(), pad.size());
ef.ChannelMessageEnd("");
// Pretty print
StringSource(cipher, true,
new HexEncoder(new StringSink(encoded), true, 16, " "));
}
catch (CryptoPP::BufferedTransformation::NoChannelSupport& e)
{
cerr << "Caught NoChannelSupport..." << endl;
}
catch (CryptoPP::AuthenticatedSymmetricCipher::BadState& e)
{
cerr << "Caught BadState..." << endl;
}
catch (CryptoPP::InvalidArgument& e)
{
cerr << "Caught InvalidArgument..." << endl;
}
return encoded;
}
private:
byte key[32];
byte iv[12];
const int TAG_SIZE = 16;
std::string cipher;
std::string pad;
};
}
#endif
I try to encrypt and decrypt the String with the following code:
int __cdecl main(void)
{
CryptoData::CryptoData crypt;
std::string _myData = "mohsenjooooon:X";
std::string _encrypted = crypt._encrypting(_myData);
printf("its encrypted data: %p \n", _encrypted);
std::string _decData = crypt._decrypting(_encrypted);
printf("its decrypted data: %p \n", _decData);
return 0;
}
and finally i get this Exception:
Caught HashVerificationFailed...
HashVerificationFilter: message hash or MAC not valid
Thanks for the help, where is my code problem?
i put wrong data to wrong channel
fixed:
#ifndef CRYPTODATA
#define CRYPTODATA
typedef unsigned char byte;
#include <iostream>
#include "stdafx.h"
#include "assert.h"
using std::cerr;
using std::cout;
#include "cryptopp/hex.h"
using CryptoPP::HexDecoder;
using CryptoPP::HexEncoder;
#include "cryptopp/cryptlib.h"
using CryptoPP::AuthenticatedSymmetricCipher;
using CryptoPP::BufferedTransformation;
#include <string>
using std::string;
#include "cryptopp/filters.h"
using CryptoPP::AuthenticatedDecryptionFilter;
using CryptoPP::AuthenticatedEncryptionFilter;
using CryptoPP::StringSink;
using CryptoPP::StringSource;
#include "cryptopp/aes.h"
using CryptoPP::AES;
#include "cryptopp/gcm.h"
using CryptoPP::GCM;
using CryptoPP::GCM_TablesOption;
#include <string>
namespace CryptoData{
class CryptoData{
public:
CryptoData(){
std::string _key = ":D:D:D:D:D:D:D:D::D:D:D:";
std::string _iv = ":D:D::D:D:D:D:";
std::copy(_key.begin(), _key.end(), this->key);
std::copy(_iv.begin(), _iv.end(), this->iv);
string pad(16, (char)0x00);
authCode = pad;
printf("default constractor call \n");
}
CryptoData(std::string _key, std::string _iv, std::string _pad):authCode(_pad){
std::copy(_key.begin(), _key.end(), this->key);
std::copy(_iv.begin(), _iv.end(), this->iv);
printf("constractor call with args \n");
}
~CryptoData() {
printf("default destractur call \n");
}
std::string _encrypting(std::string _data){
return this->encryptData(_data);
}
std::string _decrypting(std::string _data) {
return this->decryptData(_data);
}
protected:
std::string decryptData(std::string _data) {
std::string radata, rpdata;
try
{
GCM<AES>::Decryption d;
d.SetKeyWithIV(key, sizeof(key), iv, sizeof(iv));
string enc = cipher.substr(0, cipher.length() - TAG_SIZE);
string mac = cipher.substr(cipher.length() - TAG_SIZE);
assert(cipher.size() == enc.size() + mac.size());
// assert(enc.size() == _data.size());
assert(TAG_SIZE == mac.size());
radata = authCode;
AuthenticatedDecryptionFilter df(d, NULL,
AuthenticatedDecryptionFilter::MAC_AT_BEGIN |
AuthenticatedDecryptionFilter::THROW_EXCEPTION,
TAG_SIZE);
df.ChannelPut("", (const byte*)mac.data(), mac.size());
df.ChannelPut("AAD", (const byte*)authCode.data(), authCode.size());
df.ChannelPut("", (const byte*)enc.data(), enc.size());
df.ChannelMessageEnd("AAD");
df.ChannelMessageEnd("");
bool b = false;
b = df.GetLastResult();
assert(true == b);
string retrieved;
size_t n = (size_t)-1;
df.SetRetrievalChannel("");
n = (size_t)df.MaxRetrievable();
retrieved.resize(n);
if (n > 0)
{
df.Get((byte*)retrieved.data(), n);
}
rpdata = retrieved;
printf("done6 \n");
//assert(rpdata == pad);
cout << "its a decrypted data " << radata << " \n";
}
catch (CryptoPP::InvalidArgument& e)
{
cerr << "Caught InvalidArgument..." << endl;
}
catch (CryptoPP::AuthenticatedSymmetricCipher::BadState& e)
{
cerr << "Caught BadState..." << endl;
}
catch (CryptoPP::HashVerificationFilter::HashVerificationFailed& e)
{
cerr << "Caught HashVerificationFailed..." << endl;
cerr << e.what() << endl;
}
return rpdata;
}
std::string encryptData(std::string _data){
std::string encoded;
try
{
GCM<AES>::Encryption e;
e.SetKeyWithIV(key, sizeof(key), iv, sizeof(iv));
AuthenticatedEncryptionFilter ef(e,
new StringSink(cipher), false, TAG_SIZE); // AuthenticatedEncryptionFilter
ef.ChannelPut("AAD", (const byte*)authCode.data(), authCode.size());
ef.ChannelMessageEnd("AAD");
ef.ChannelPut("", (const byte*)_data.data(), _data.size());
ef.ChannelMessageEnd("");
// Pretty print
StringSource(cipher, true,
new HexEncoder(new StringSink(encoded), true, 16, " "));
}
catch (CryptoPP::BufferedTransformation::NoChannelSupport& e)
{
cerr << "Caught NoChannelSupport..." << endl;
}
catch (CryptoPP::AuthenticatedSymmetricCipher::BadState& e)
{
cerr << "Caught BadState..." << endl;
}
catch (CryptoPP::InvalidArgument& e)
{
cerr << "Caught InvalidArgument..." << endl;
}
return encoded;
}
private:
byte key[32];
byte iv[12];
const int TAG_SIZE = 16;
std::string cipher;
std::string authCode;
};
}
#endif

is there an example of protobuf with text output?

I want to use protobuf and to create the serialization output file in text format for testing and for a replacement of json. I can't figure out how to write it on my own and am looking for examples.
Here is the one on binary output :
#include <iostream>
#include <fstream>
#include <string>
#include "addressbook.pb.h"
using namespace std;
// This function fills in a Person message based on user input.
void PromptForAddress(tutorial::Person* person) {
cout << "Enter person ID number: ";
int id;
cin >> id;
person->set_id(id);
cin.ignore(256, '\n');
cout << "Enter name: ";
getline(cin, *person->mutable_name());
cout << "Enter email address (blank for none): ";
string email;
getline(cin, email);
if (!email.empty()) {
person->set_email(email);
}
while (true) {
cout << "Enter a phone number (or leave blank to finish): ";
string number;
getline(cin, number);
if (number.empty()) {
break;
}
tutorial::Person::PhoneNumber* phone_number = person->add_phones();
phone_number->set_number(number);
cout << "Is this a mobile, home, or work phone? ";
string type;
getline(cin, type);
if (type == "mobile") {
phone_number->set_type(tutorial::Person::MOBILE);
} else if (type == "home") {
phone_number->set_type(tutorial::Person::HOME);
} else if (type == "work") {
phone_number->set_type(tutorial::Person::WORK);
} else {
cout << "Unknown phone type. Using default." << endl;
}
}
}
// Main function: Reads the entire address book from a file,
// adds one person based on user input, then writes it back out to the same
// file.
int main(int argc, char* argv[]) {
// Verify that the version of the library that we linked against is
// compatible with the version of the headers we compiled against.
GOOGLE_PROTOBUF_VERIFY_VERSION;
if (argc != 2) {
cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
return -1;
}
tutorial::AddressBook address_book;
{
// Read the existing address book.
fstream input(argv[1], ios::in | ios::binary);
if (!input) {
cout << argv[1] << ": File not found. Creating a new file." << endl;
} else if (!address_book.ParseFromIstream(&input)) {
cerr << "Failed to parse address book." << endl;
return -1;
}
}
// Add an address.
PromptForAddress(address_book.add_people());
{
// Write the new address book back to disk.
fstream output(argv[1], ios::out | ios::trunc | ios::binary);
if (!address_book.SerializeToOstream(&output)) {
cerr << "Failed to write address book." << endl;
return -1;
}
}
// Optional: Delete all global objects allocated by libprotobuf.
google::protobuf::ShutdownProtobufLibrary();
return 0;
}
Can I just do some minor changes in this one to output in text format or something else needs to be done? Please either suggest the changes required or any link where code exists (in any language).
The debug string output is guaranteed to be valid text-serialized format, but does not care about whether the protocol message is actually valid:
std::string s = msg.DebugString(); // or ShortDebugString
If you want to validate, use TextFormat::PrintToString:
#include <google/protobuf/text_format.h>
if (std::string s; google::protobuf::TextFormat::PrintToString(msg, &s)) {
std::cout << "Your message: " << s;
} else {
std::cerr << "Message not valid (partial content: "
<< msg.ShortDebugString() << ")\n";
}
Tools for JSON interop are available in json_util.h.
This code will serialise protobuf messages to JSON and deserialise JSON to protobuf messages.
This is lifted straight out of production code (which I own and hereby grant you licence to use, but please credit me).
This is linked against protobuf 3.
Header:
struct pretty_json_type {
void operator()(google::protobuf::util::JsonOptions& opts) const {
opts.add_whitespace = true;
}
};
static constexpr pretty_json_type pretty_json{};
struct compact_json_type {
void operator()(google::protobuf::util::JsonOptions& opts) const {
opts.add_whitespace = false;
}
};
static constexpr compact_json_type compact_json{};
struct include_defaults_type {
void operator()(google::protobuf::util::JsonOptions& opts) const {
opts.always_print_primitive_fields = true;
}
};
static constexpr include_defaults_type include_defaults{};
template<class...Options>
auto json_options(Options&&...options)
{
google::protobuf::util::JsonOptions opts;
using expand = int [];
void(expand{
0,
((options(opts)),0)...
});
return opts;
}
std::string as_json(const google::protobuf::Message& msg,
google::protobuf::util::JsonOptions opts = json_options(pretty_json,
include_defaults));
std::string as_json(const google::protobuf::Message* msg,
google::protobuf::util::JsonOptions opts = json_options(pretty_json,
include_defaults));
google::protobuf::Message& from_json(google::protobuf::Message& msg,
const char* first,
std::size_t size);
inline
decltype(auto) from_json(google::protobuf::Message& msg,
const std::string& json)
{
return from_json(msg, json.data(), json.length());
}
Implementation
std::string as_json(const google::protobuf::Message& msg,
google::protobuf::util::JsonOptions opts)
{
namespace pb = google::protobuf;
namespace pbu = google::protobuf::util;
auto buffer = msg.SerializeAsString();
std::string result;
pb::io::ArrayInputStream zistream(buffer.data(), buffer.size());
auto resolver = std::unique_ptr<pbu::TypeResolver> {
pbu::NewTypeResolverForDescriptorPool("",
pb::DescriptorPool::generated_pool())
};
auto status = google::protobuf::util::BinaryToJsonString(resolver.get(),
"/" + msg.GetDescriptor()->full_name(),
buffer,
std::addressof(result),
opts);
if (!status.ok())
{
std::ostringstream ss;
ss << status;
throw std::runtime_error(ss.str());
}
return result;
}
std::string as_json(const google::protobuf::Message* msg,
google::protobuf::util::JsonOptions opts)
{
return as_json(*msg, opts);
}
google::protobuf::Message& from_json(google::protobuf::Message& msg,
const char* first,
std::size_t size)
{
namespace pb = google::protobuf;
namespace pbu = google::protobuf::util;
auto resolver = std::unique_ptr<pbu::TypeResolver> {
pbu::NewTypeResolverForDescriptorPool("", pb::DescriptorPool::generated_pool())
};
auto zistream = std::make_unique<pb::io::ArrayInputStream>(first,
size);
auto binary_buffer = std::string {};
binary_buffer.reserve(size);
auto zostream = std::make_unique<pb::io::StringOutputStream>(std::addressof(binary_buffer));
auto status = pbu::JsonToBinaryStream(resolver.get(),
"/" + msg.GetDescriptor()->full_name(),
zistream.get(), zostream.get());
zistream.reset();
zostream.reset();
if (msg.ParseFromString(binary_buffer))
{
return msg;
}
throw std::runtime_error("invalid message");
}
To convert a message to JSON in three lines of code, do this -
#include <google/protobuf/util/json_util.h>
static std::string ProtoToJson(const google::protobuf::Message& proto)
{
std::string json;
google::protobuf::util::MessageToJsonString(proto, &json);
return json;
}

deserialization of pod struct stored in redis fails if key is defined as std::string

Storing of POD struct in redis works fine with const char * but doesn't if std::string is involved.
const char * example
#include <hiredis/hiredis.h>
#include <string.h>
#include <string>
#include <iostream>
using namespace std;
struct Test
{
const uint32_t id;
const char *name;
};
int main() {
redisContext *context = redisConnect("127.0.0.1", 6379);
if (context == NULL || context->err) {
if (context) {
printf("Error: %s\n", context->errstr);
} else {
printf("Can't allocate redis context\n");
}
exit(EXIT_FAILURE);
}
Test obj = {(uint32_t) 123, "foo bar"};
const size_t size = 2 * sizeof(uint32_t) + (strlen(obj.name) + 1);
cout << "object{id: " << obj.id << ", name:' " << obj.name << "'}; size: " << size << endl;
redisReply *reply = 0;
const char *key = strdup("some-key");
reply = (redisReply *) redisCommand(context, "SET %b %b", key, strlen(key), &obj, size);
if (!reply)
return REDIS_ERR;
freeReplyObject(reply);
reply = (redisReply *) redisCommand(context, "GET %s", key);
if (!reply)
return REDIS_ERR;
Test *res = (struct Test*) reply->str;
cout << "result{id: " << res->id << ", name:' " << res->name << "'}; size: " << size << endl;
freeReplyObject(reply);
redisFree(context);
}
If I replace the lines:
const char *key = strdup("some-key");
reply = (redisReply *) redisCommand(context, "SET %b %b", key, strlen(key), &obj, size);
with
std::string key("some-key");
reply = (redisReply *) redisCommand(context, "SET %b %b", key.c_str(), key.size(), &obj, size);
the execution always ends in Segmentation fault.
I can't solve the issue by myself and I would really appreciate any help.
OK, I got it:
replacement of second redisCommand statement
reply = (redisReply *) redisCommand(context, "GET %b", key.c_str(), key.size());
solved the issue.

Use of a WebSocket++ server and client in the same function/program

My question is how to set up a WebSocket++ server and create a WebSocket++ client that connects to this server in the same program or function? (for test purpose)
Details:
I would like to use library WebSocket++ in my C++ program to stream data on a websocket. I have a websocket client that sends data to an extern websocket server.
As a good programmer, I try to write some tests to check everything is fine. Therefore I want to setup a WebSocket++ server to test the data I send from the WebSocket++ client.
From the examples, I have managed to create a server in a program and a client in another program. It works like a charm. Problem arises when I try to put the server and the client code in the same program (code is given below): The client can not connect to server, and leads to a timeout handshake.
I guess it is an ASIO problem or a thread problem, but I have no idea how to deal with it.
From the classical example I met, I had to replace echo_server.start() with echo_server.poll(), to have a non stop blocking process. It is not blocking but it prevents the client from connecting to server.
Any advise on how to solve this would be of great help!!
Should I use thread or anything else?
Below is the program I try to get running, where I want the client to connect to the server.
It is based on the merge of tutorials found here and here
#include <websocketpp/config/asio_no_tls_client.hpp>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/client.hpp>
#include <websocketpp/server.hpp>
#include <websocketpp/common/thread.hpp>
#include <websocketpp/common/memory.hpp>
#include <cstdlib>
#include <iostream>
#include <map>
#include <string>
#include <sstream>
typedef websocketpp::server<websocketpp::config::asio> server;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
// pull out the type of messages sent by our config
typedef server::message_ptr message_ptr;
// Define a callback to handle incoming messages
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg);
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg)
{
std::cout << "on_message called with hdl: " << hdl.lock().get()
<< " and message: " << msg->get_payload()
<< std::endl;
try {
s->send(hdl, msg->get_payload(), msg->get_opcode());
} catch (const websocketpp::lib::error_code& e) {
std::cout << "Echo failed because: " << e
<< "(" << e.message() << ")" << std::endl;
}
}
typedef websocketpp::client<websocketpp::config::asio_client> client;
class connection_metadata {
public:
typedef websocketpp::lib::shared_ptr<connection_metadata> ptr;
connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri)
: m_id(id)
, m_hdl(hdl)
, m_status("Connecting")
, m_uri(uri)
, m_server("N/A")
, m_error_reason("")
,m_messages()
{}
void on_open(client * c, websocketpp::connection_hdl hdl) {
m_status = "Open";
client::connection_ptr con = c->get_con_from_hdl(hdl);
m_server = con->get_response_header("Server");
}
void on_fail(client * c, websocketpp::connection_hdl hdl) {
m_status = "Failed";
client::connection_ptr con = c->get_con_from_hdl(hdl);
m_server = con->get_response_header("Server");
m_error_reason = con->get_ec().message();
}
void on_close(client * c, websocketpp::connection_hdl hdl) {
m_status = "Closed";
client::connection_ptr con = c->get_con_from_hdl(hdl);
std::stringstream s;
s << "close code: " << con->get_remote_close_code() << " ("
<< websocketpp::close::status::get_string(con->get_remote_close_code())
<< "), close reason: " << con->get_remote_close_reason();
m_error_reason = s.str();
}
void on_message(websocketpp::connection_hdl, client::message_ptr msg) {
if (msg->get_opcode() == websocketpp::frame::opcode::text) {
m_messages.push_back("<< " + msg->get_payload());
} else {
m_messages.push_back("<< " + websocketpp::utility::to_hex(msg->get_payload()));
}
}
websocketpp::connection_hdl get_hdl() const {
return m_hdl;
}
int get_id() const {
return m_id;
}
std::string get_status() const {
return m_status;
}
void record_sent_message(std::string message) {
m_messages.push_back(">> " + message);
}
friend std::ostream & operator<< (std::ostream & out, connection_metadata const & data);
private:
int m_id;
websocketpp::connection_hdl m_hdl;
std::string m_status;
std::string m_uri;
std::string m_server;
std::string m_error_reason;
std::vector<std::string> m_messages;
};
std::ostream & operator<< (std::ostream & out, connection_metadata const & data) {
out << "> URI: " << data.m_uri << "\n"
<< "> Status: " << data.m_status << "\n"
<< "> Remote Server: " << (data.m_server.empty() ? "None Specified" : data.m_server) << "\n"
<< "> Error/close reason: " << (data.m_error_reason.empty() ? "N/A" : data.m_error_reason) << "\n";
out << "> Messages Processed: (" << data.m_messages.size() << ") \n";
std::vector<std::string>::const_iterator it;
for (it = data.m_messages.begin(); it != data.m_messages.end(); ++it) {
out << *it << "\n";
}
return out;
}
class websocket_endpoint {
public:
websocket_endpoint () : m_endpoint(), m_thread(), m_connection_list(), m_next_id(0)
{
m_endpoint.clear_access_channels(websocketpp::log::alevel::all);
m_endpoint.clear_error_channels(websocketpp::log::elevel::all);
m_endpoint.init_asio();
m_endpoint.start_perpetual();
m_thread = websocketpp::lib::make_shared<websocketpp::lib::thread>(&client::run, &m_endpoint);
}
~websocket_endpoint() {
m_endpoint.stop_perpetual();
for (con_list::const_iterator it = m_connection_list.begin(); it != m_connection_list.end(); ++it) {
if (it->second->get_status() != "Open") {
// Only close open connections
continue;
}
std::cout << "> Closing connection " << it->second->get_id() << std::endl;
websocketpp::lib::error_code ec;
m_endpoint.close(it->second->get_hdl(), websocketpp::close::status::going_away, "", ec);
if (ec) {
std::cout << "> Error closing connection " << it->second->get_id() << ": "
<< ec.message() << std::endl;
}
}
m_thread->join();
}
int connect(std::string const & uri) {
websocketpp::lib::error_code ec;
client::connection_ptr con = m_endpoint.get_connection(uri, ec);
if (ec) {
std::cout << "> Connect initialization error: " << ec.message() << std::endl;
return -1;
}
int new_id = m_next_id++;
connection_metadata::ptr metadata_ptr = websocketpp::lib::make_shared<connection_metadata>(new_id, con->get_handle(), uri);
m_connection_list[new_id] = metadata_ptr;
con->set_open_handler(websocketpp::lib::bind(
&connection_metadata::on_open,
metadata_ptr,
&m_endpoint,
websocketpp::lib::placeholders::_1
));
con->set_fail_handler(websocketpp::lib::bind(
&connection_metadata::on_fail,
metadata_ptr,
&m_endpoint,
websocketpp::lib::placeholders::_1
));
con->set_close_handler(websocketpp::lib::bind(
&connection_metadata::on_close,
metadata_ptr,
&m_endpoint,
websocketpp::lib::placeholders::_1
));
con->set_message_handler(websocketpp::lib::bind(
&connection_metadata::on_message,
metadata_ptr,
websocketpp::lib::placeholders::_1,
websocketpp::lib::placeholders::_2
));
m_endpoint.connect(con);
return new_id;
}
void close(int id, websocketpp::close::status::value code, std::string reason) {
websocketpp::lib::error_code ec;
con_list::iterator metadata_it = m_connection_list.find(id);
if (metadata_it == m_connection_list.end()) {
std::cout << "> No connection found with id " << id << std::endl;
return;
}
m_endpoint.close(metadata_it->second->get_hdl(), code, reason, ec);
if (ec) {
std::cout << "> Error initiating close: " << ec.message() << std::endl;
}
}
void send(int id, std::string message) {
websocketpp::lib::error_code ec;
con_list::iterator metadata_it = m_connection_list.find(id);
if (metadata_it == m_connection_list.end()) {
std::cout << "> No connection found with id " << id << std::endl;
return;
}
m_endpoint.send(metadata_it->second->get_hdl(), message, websocketpp::frame::opcode::text, ec);
if (ec) {
std::cout << "> Error sending message: " << ec.message() << std::endl;
return;
}
metadata_it->second->record_sent_message(message);
}
connection_metadata::ptr get_metadata(int id) const {
con_list::const_iterator metadata_it = m_connection_list.find(id);
if (metadata_it == m_connection_list.end()) {
return connection_metadata::ptr();
} else {
return metadata_it->second;
}
}
private:
typedef std::map<int,connection_metadata::ptr> con_list;
client m_endpoint;
websocketpp::lib::shared_ptr<websocketpp::lib::thread> m_thread;
con_list m_connection_list;
int m_next_id;
};
int main() {
bool done = false;
std::string input;
websocket_endpoint endpoint;
server echo_server;
// Set logging settings
echo_server.set_access_channels(websocketpp::log::alevel::all);
echo_server.clear_access_channels(websocketpp::log::alevel::frame_payload);
// Initialize ASIO
echo_server.init_asio();
// Register our message handler
echo_server.set_message_handler(bind(&on_message,&echo_server,::_1,::_2));
// Listen on port 9002
echo_server.listen(9002);
// Start the server accept loop
echo_server.start_accept();
// Start the ASIO io_service run loop
echo_server.poll();
// echo_server.run();
//thread t(bind(&WSServer::poll,echo_server));
//t.detach();
while (!done) {
std::cout << "Enter Command: ";
std::getline(std::cin, input);
if (input == "quit") {
done = true;
} else if (input == "help") {
std::cout
<< "\nCommand List:\n"
<< "connect <ws uri>\n"
<< "send <connection id> <message>\n"
<< "close <connection id> [<close code:default=1000>] [<close reason>]\n"
<< "show <connection id>\n"
<< "help: Display this help text\n"
<< "quit: Exit the program\n"
<< std::endl;
} else if (input.substr(0,7) == "connect") {
int id = endpoint.connect(input.substr(8));
if (id != -1) {
std::cout << "> Created connection with id " << id << std::endl;
}
} else if (input.substr(0,4) == "send") {
std::stringstream ss(input);
std::string cmd;
int id;
std::string message = "";
ss >> cmd >> id;
std::getline(ss,message);
endpoint.send(id, message);
} else if (input.substr(0,5) == "close") {
std::stringstream ss(input);
std::string cmd;
int id;
int close_code = websocketpp::close::status::normal;
std::string reason = "";
ss >> cmd >> id >> close_code;
std::getline(ss,reason);
endpoint.close(id, (websocketpp::close::status::value)close_code, reason);
} else if (input.substr(0,4) == "show") {
int id = atoi(input.substr(5).c_str());
connection_metadata::ptr metadata = endpoint.get_metadata(id);
if (metadata) {
std::cout << *metadata << std::endl;
} else {
std::cout << "> Unknown connection id " << id << std::endl;
}
} else {
std::cout << "> Unrecognized Command" << std::endl;
}
}
return 0;
}
The CMakeLists.txt needed to compile this program looks like this
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.8)
FIND_PACKAGE(Boost 1.53 COMPONENTS random system thread REQUIRED)
IF(Boost_FOUND)
MESSAGE(STATUS "Boost_INCLUDE_DIRS : ${Boost_INCLUDE_DIRS}")
MESSAGE(STATUS "Boost_LIBRARIES : ${Boost_LIBRARIES}")
ENDIF()
INCLUDE_DIRECTORIES(SYSTEM ${Boost_INCLUDE_DIRS})
INCLUDE_DIRECTORIES(SYSTEM websocketpp)
ADD_EXECUTABLE(DemoWebSocket DemoWebSocket.cpp)
TARGET_LINK_LIBRARIES(DemoWebSocket
${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_RANDOM_LIBRARY})
IF(WIN32)
TARGET_LINK_LIBRARIES(DemoWebSocket wsock32 ws2_32)
ELSE()
TARGET_LINK_LIBRARIES(DemoWebSocket pthread rt)
ENDIF()
The solutions consists in creating a thread that creates a WebSocket server and launches its runnning. Then the client code can be used in the same function.
Below is the code that allows to use a WebSocket++ server and a a WebSocket++ client in the same function/program
void createServerEcho();
void createServerEcho()
{
server echo_server;
// Set logging settings
echo_server.set_access_channels(websocketpp::log::alevel::all);
echo_server.clear_access_channels(websocketpp::log::alevel::frame_payload);
// Initialize ASIO
echo_server.init_asio();
// Register our message handler
echo_server.set_message_handler(bind(&on_message,&echo_server,::_1,::_2));
// Listen on port 9002
echo_server.listen(9002);
// Start the server accept loop
echo_server.start_accept();
// Start the ASIO io_service run loop
echo_server.run();
}
int main()
{
websocket_endpoint endpoint;
std::thread serverThread (createServerEcho);
/*
* Client code part with variable endpoint, with creation, connection, ...
*/
serverThread.join();
}