Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I have written an application in c++ with OpenCV and boost and it is running well and correct, but before exiting it crashes (if I debug step by step, it crashes when return 0; at the end of main) saying:
in kdevelop:
*** Error in `/home/xxx/git_repos/my_proj/build/my_proj': free(): invalid pointer: 0x00000000008939c0 ***
*** Crashed with return code: 0 ***
in command line:
*** Error in `./my_proj': free(): invalid pointer: 0x00000000008939c0 ***
Aborted (core dumped)
if I use gdb:
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
*** Error in `/home/xxx/git_repos/my_proj/build/plate_info_extractor': free(): invalid pointer: 0x00000000008939c0 ***
Program received signal SIGABRT, Aborted.
0x00007ffff5b83cc9 in __GI_raise (sig=sig#entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
56 ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.
Any help please? I cannot figure out what is wrong.
I have commented some code parts and it seems that the problem is in the code that I use to log the errors/info... So:
#include "CLogger.hpp"
static void help(const std::string& appNameIn)
{
std::cerr << "Usage: " << appNameIn << " <option(s)> SOURCES" << std::endl
<< "Options:" << std::endl
<< " -h,--help Show this help message" << std::endl
<< " -c,--config CONFIG FILE Specify the name and path to the config file" << std::endl
<< std::endl
<< std::endl;
}
enum RetVals
{
NO_ERRORS,
NOT_ENOUGH_PARAMETERS,
UNKNOWN_OPTION,
SHOW_USAGE,
UNKNOWN_EXCEPTION,
OTHER_EXCEPTION,
};
int main(int argc, char **argv)
{
Logger::initFilesList();
Logger::initLogging();
BoostLogger lg = setClassNameAttribute("main");
if (argc == 3)
{
std::string opt = argv[1];
if (opt == "-c" || opt == "--config")
{
try
{
std::cout << "running" << std::endl;
}
catch (std::exception& e)
{
BOOST_LOG_SEV(lg, Logger::SeverityLevels::error) << "Other exception: " << e.what();
return OTHER_EXCEPTION;
}
catch (...)
{
BOOST_LOG_SEV(lg, Logger::SeverityLevels::error) << "Unkown exception ...";
return UNKNOWN_EXCEPTION;
}
}
else
{
help(argv[0]);
BOOST_LOG_SEV(lg, Logger::SeverityLevels::debug) << "The options are -c or --config, -h or --help";
return UNKNOWN_OPTION;
}
}
else if (argc == 2 && (std::string(argv[1]) == "-h" || std::string(argv[1]) == "--help"))
{
help(argv[0]);
BOOST_LOG_SEV(lg, Logger::SeverityLevels::debug) << "Call help function";
return SHOW_USAGE;
}
else
{
help(argv[0]);
BOOST_LOG_SEV(lg, Logger::SeverityLevels::debug) << "The number of input parameters is wrong";
return NOT_ENOUGH_PARAMETERS;
}
return NO_ERRORS;
}
and the CLogger.hpp:
#pragma once
#include <list>
#include <string>
#include <boost/log/common.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/concept_check.hpp>
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
class Logger
{
private:
static std::list< std::string > sm_logFilesList;
static const std::string sm_folderOfLogFiles;
/** the function adds the input log file to the list
*
* #param logFilePathIn : input log file path
*/
static void addNewLogFileToList(const std::string& logFilePathIn, bool sorting = false);
/** The function adds new logs to new log file and remove the oltest ones to have always 2 log files */
static void addNewRemoveOldLogs(bool sorting = false);
/** #returns : if the input path is valid or not */
static bool checkPath(const boost::filesystem::path& pathIn);
/** #returns if the file with the first input path was more recent accesed than the file with the second input path
*/
static bool compareAccessTime(const std::string& path1In, const std::string& path2In);
/** The function remove the old log files and keeps just the most recent two */
static void removeOldLogFiles();
/** The function is calles at the end of each file and removes the old files keeping just the last two */
static void openingHandler(boost::log::sinks::text_file_backend::stream_type& file);
public:
enum SeverityLevels
{
debug,
info,
warning,
error
};
/** The function sets the sm_logFilesList to contain the files if there are log files from earlier runs */
static void initFilesList();
/** The fuction is initializing the sinks and the formatter of the logger */
static void initLogging();
};
typedef boost::log::sources::severity_logger< Logger::SeverityLevels > BoostLogger;
/** the overloaded << operator of the BoostLogger */
std::ostream& operator<< (std::ostream& strm, Logger::SeverityLevels level);
/** The function sets the logger attribute ClassName to the specified string and returns the initialized logger
*
* #param classNameIn : input string name of the class
*
* #returns : initialized BoostLogger
*/
BoostLogger setClassNameAttribute(const std::string& classNameIn);
and the implementation:
#include "CLogger.hpp"
#include <boost/log/attributes.hpp>
#include <boost/log/attributes/current_process_id.hpp>
#include <boost/log/attributes/current_process_name.hpp>
#include <boost/log/core/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/utility/setup/file.hpp>
typedef boost::log::sinks::synchronous_sink< boost::log::sinks::text_file_backend > SinkTextFileBakend;
BOOST_LOG_ATTRIBUTE_KEYWORD(correlid, "CorrelationID", std::string)
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", Logger::SeverityLevels)
BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp, "TimeStamp", boost::posix_time::ptime)
BOOST_LOG_ATTRIBUTE_KEYWORD(threadid, "ThreadID", boost::log::attributes::current_thread_id)
BOOST_LOG_ATTRIBUTE_KEYWORD(classname, "ClassName", std::string)
std::list< std::string > Logger::sm_logFilesList;
const std::string Logger::sm_folderOfLogFiles = "../logs/";
std::ostream& operator<<(std::ostream& strm, Logger::SeverityLevels level)
{
static const char* strings[] =
{
"DEBUG",
"INFO",
"WARN",
"ERROR"
};
if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
strm << strings[level];
else
strm << static_cast< int >(level);
return strm;
}
BoostLogger setClassNameAttribute(const std::string& classNameIn)
{
BoostLogger lg;
lg.add_attribute("ClassName", boost::log::attributes::constant<std::string>(classNameIn));
return lg;
}
void Logger::removeOldLogFiles()
{
while (sm_logFilesList.size() > 2)
{
std::string fileToDelete = sm_logFilesList.front();
if (fs::exists(fs::path(fileToDelete)))
{
fs::remove(fs::path(fileToDelete));
sm_logFilesList.pop_front();
}
}
}
bool Logger::compareAccessTime(const std::string& path1In, const std::string& path2In)
{
return (fs::last_write_time(fs::path(path1In)) < fs::last_write_time(fs::path(path2In)));
}
void Logger::addNewLogFileToList(const std::string& logFilePathIn, bool sorting)
{
if (std::find(sm_logFilesList.begin(), sm_logFilesList.end(), logFilePathIn) == sm_logFilesList.end())
{
sm_logFilesList.push_back(logFilePathIn);
if (sorting)
{
sm_logFilesList.sort(compareAccessTime);
}
removeOldLogFiles();
}
}
void Logger::addNewRemoveOldLogs(bool sorting)
{
fs::path path(sm_folderOfLogFiles);
fs::directory_iterator endDir;
if (checkPath(path))
{
for (fs::directory_iterator it(path); it != endDir; it++)
{
if (fs::is_regular_file(it->status()))
{
if (fs::extension(*it) == ".log")
{
std::string fileToPush = it->path().string();
addNewLogFileToList(fileToPush, sorting);
}
}
}
}
}
bool Logger::checkPath(const boost::filesystem::path& pathIn)
{
if (!fs::exists(pathIn))
{
return false;
}
if (!fs::is_directory(pathIn))
{
return false;
}
return true;
}
void Logger::openingHandler(boost::log::sinks::text_file_backend::stream_type& file)
{
addNewRemoveOldLogs();
}
void Logger::initFilesList()
{
addNewRemoveOldLogs(true);
}
void Logger::initLogging()
{
// Create a backend
boost::shared_ptr< SinkTextFileBakend > sink = boost::log::add_file_log(
sm_folderOfLogFiles + "plate_info_extractor_%Y-%m-%d_%H-%M-%S.%N.log",
boost::log::keywords::format = boost::log::expressions::stream
<< boost::log::expressions::attr< boost::log::attributes::current_process_name::value_type >("Executable") << "("
<< boost::log::expressions::attr< boost::log::attributes::current_process_id::value_type >("ExeUID") << ") CID("
<< correlid << ") " << severity << "["
<< boost::log::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S.%f")
<< "] [" << boost::log::expressions::attr< boost::log::attributes::current_thread_id::value_type >("ThreadID")
<< "] " << classname << " - " << boost::log::expressions::smessage,
boost::log::keywords::open_mode = (std::ios::out | std::ios::app),
boost::log::keywords::rotation_size = 2 * 1024 * 1024,
boost::log::keywords::auto_flush = true
);
sink->locked_backend()->set_open_handler(&openingHandler);
boost::log::core::get()->set_filter(severity >= SeverityLevels::info);
boost::log::core::get()->add_sink(sink);
#ifdef DEBUGGING
boost::shared_ptr< boost::log::sinks::text_ostream_backend > backend =
boost::make_shared< boost::log::sinks::text_ostream_backend >();
backend->add_stream(
boost::shared_ptr< std::ostream >(&std::cout));
// Enable auto-flushing after each log record written
backend->auto_flush(true);
// Wrap it into the frontend and register in the core.
// The backend requires synchronization in the frontend.
// for testing and printing log in sysout
boost::shared_ptr< boost::log::sinks::synchronous_sink< boost::log::sinks::text_ostream_backend > > backend_sink(
new boost::log::sinks::synchronous_sink< boost::log::sinks::text_ostream_backend >(backend));
backend_sink->set_formatter(
boost::log::expressions::stream
// line id will be written in hex, 8-digits, zero-filled
<< boost::log::expressions::attr< boost::log::attributes::current_process_name::value_type >("Executable")
<< "(" << boost::log::expressions::attr< boost::log::attributes::current_process_name::value_type >("ExeUID")
<< ") CID(" << correlid << ") " << severity << " ["
<< boost::log::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S.%f")
<< "] [" << boost::log::expressions::attr< boost::log::attributes::current_thread_id::value_type >("ThreadID")
<< "] " << classname << " - " << boost::log::expressions::smessage);
boost::log::core::get()->add_sink(backend_sink); // for testing and printing log in sysout
#endif
}
I hope that now there is enough information :)
This line:
backend->add_stream(boost::shared_ptr< std::ostream >(&std::cout));
looks pretty disastrous me. Your program will attempt to delete std::cout when exiting. I'm not familiar with Boost Log, so I don't know how to properly set up a sink for std::cout.
As Arpegius pointed out you should use the null_deleter to avoid deletion. Something along the lines of this:
backend->add_stream(boost::shared_ptr< std::ostream>(&std::cout, boost::null_deleter()));
Related
I'm trying to make my own programming language and compiler for it. While creating lexical analyzer i have troubles with this function. Also i included stdlib.h and stdio.h. Platform what i use is m1 mac and vscode with gcc
There is code:
#define _CRT_SECURE_NO_WARNINGS
#include "Log.h"
namespace Log
{
LOG getlog(wchar_t logfile[])
{
try
{
LOG struc;
struc.stream = new (std::nothrow) std::ofstream;
if (!struc.stream)
throw ERROR_THROW(112);
struc.stream->open(logfile);
if (!(*(struc.stream)).is_open())
throw ERROR_THROW(112);
wcscpy(struc.logfile, logfile);
return struc;
}
catch (Error::ERROR e)
{
Error::ErrorDisplay(e);
}
}
void WriteLine(LOG log, char *c, ...)
{
for (char **c_ptr = &c; **c_ptr; c_ptr++)
{
*log.stream << *c_ptr;
}
}
void WriteLine(LOG log, wchar_t *c, ...)
{
unsigned lineLength = 0;
char *line = nullptr;
unsigned int n = 0;
for (wchar_t **c_ptr = &c; **c_ptr; c_ptr++)
{
lineLength = wcslen(*c_ptr);
line = new char[lineLength + 1];
wcstombs_s(&n, line, lineLength + 1u, *c_ptr, lineLength + 1u);
*log.stream << line;
delete[] line;
}
}
void WriteLog(LOG log)
{
std::string line = "----Протокол------ ";
time_t result = time(NULL);
char str[26];
ctime_s(str, sizeof str, &result);
for (int i = 0; i < 26; i++)
line += str[i];
*log.stream << line;
}
void WriteParm(LOG log, Parm::PARM parm)
{
char in_text[PARM_MAX_SIZE];
char out_text[PARM_MAX_SIZE];
char log_text[PARM_MAX_SIZE];
wcstombs(in_text, parm.in, PARM_MAX_SIZE);
wcstombs(out_text, parm.out, PARM_MAX_SIZE);
wcstombs(log_text, parm.log, PARM_MAX_SIZE);
*log.stream << "---- Параметры ---- \n-in: " << in_text
<< "\n-out: " << out_text
<< "\n-log: " << log_text;
}
void WriteIn(LOG log, In::IN in)
{
*log.stream << "\n----Исходные данные------\nКол-во символов: " << (in.size < 0 ? 0 : in.size) << std::endl
<< "Проигнорировано: " << (in.ignor < 0 ? 0 : in.ignor) << std::endl
<< "Кол-во строк: " << (in.lines < 0 ? 0 : in.lines) << std::endl;
}
void WriteError(LOG log, Error::ERROR error)
{
*log.stream << "Ошибка " << error.id << ": " << error.message << ", Строка " << error.inext.line << ", Позиция " << error.inext.col << std::endl;
}
void Close(LOG log)
{
(*log.stream).close();
delete log.stream;
}
}
And header file:
#pragma once
#include <fstream>
#include <iostream>
#include <cwchar>
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include "In.h"
#include "Parm.h"
#include "Error.h"
namespace Log
{
struct LOG
{
wchar_t logfile[PARM_MAX_SIZE];
std::ofstream *stream;
};
static const LOG INITLOG{L"", NULL};
LOG getlog(wchar_t logfile[]);
void WriteLine(LOG log, char *c, ...);
void WriteLine(LOG log, wchar_t *c, ...);
void WriteLog(LOG log);
void WriteParm(LOG log, Parm::PARM parm);
void WriteIn(LOG log, In::IN in);
void WriteError(LOG log, Error::ERROR error);
void Close(LOG log);
}
I tried to change include path and change versions of c++, std and compilers
I try to set dynamic date for my boost multi-file logger to separate log files for everyday in different files like this :'log___2018-07-10__172.17.18.199.log'.
I don't want to set the date in each functions in my code a BOOST_LOG_SCOPED_THREAD_TAG!
I want the boost core logger to do that and I just want to set local clock in global attributes, but I do NOT know how to do that.
Here is me code:
#include <boost/shared_ptr.hpp>
#include <boost/thread/thread.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/common.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_multifile_backend.hpp>
#include <boost/log/support/date_time.hpp>
enum SeverityLevel {
TRACE,
DEBUG,
WARNING,
INFO,
ERROR,
FATAL
};
enum
{
THREAD_COUNT = 5,
LOG_RECORDS_TO_WRITE = 10
};
boost::log::sources::severity_logger_mt< SeverityLevel > myLogger;
#define LOG_ERROR BOOST_LOG_SEV(myLogger, ERROR)
#define LOG_FATAL BOOST_LOG_SEV(myLogger, FATAL)
#define LOG_INFO BOOST_LOG_SEV(myLogger, INFO)
#define LOG_WARNING BOOST_LOG_SEV(myLogger, WARNING)
#define LOG_DEBUG BOOST_LOG_SEV(myLogger, DEBUG)
#define LOG_TRACE BOOST_LOG_SEV(myLogger, TRACE)
// This function is executed in a separate thread
void thread_fooMain()
{
BOOST_LOG_NAMED_SCOPE("thread_fooMain");
for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i)
{
LOG_ERROR << "Log record " << i;
}
}
void thread_foo2()
{
BOOST_LOG_SCOPED_THREAD_TAG("TerminalIP", "172.17.18.102");
BOOST_LOG_NAMED_SCOPE("thread_fooMain");
for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i)
{
LOG_TRACE << "Log record " << i;
}
}
void thread_foo3()
{
BOOST_LOG_SCOPED_THREAD_TAG("TerminalIP", "172.17.18.103");
BOOST_LOG_NAMED_SCOPE("thread_fooMain");
for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i)
{
LOG_INFO << "Log record " << i;
}
}
void thread_foo4()
{
BOOST_LOG_SCOPED_THREAD_TAG("TerminalIP", "172.17.18.104");
BOOST_LOG_NAMED_SCOPE("thread_fooMain");
for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i)
{
LOG_INFO << "Log record " << i;
}
}
void thread_foo5()
{
BOOST_LOG_SCOPED_THREAD_TAG("TerminalIP", "172.17.18.105");
BOOST_LOG_NAMED_SCOPE("thread_fooMain");
for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i)
{
LOG_INFO << "Log record " << i;
}
}
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", SeverityLevel)
BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp, "Timestamp", boost::posix_time::ptime)
BOOST_LOG_ATTRIBUTE_KEYWORD(dateStream, "DateStream", boost::posix_time::ptime)
BOOST_LOG_ATTRIBUTE_KEYWORD(thread_id, "ThreadID", boost::log::attributes::current_thread_id::value_type)
// The operator puts a human-friendly representation of the severity level to the stream
std::ostream & operator<< (std::ostream & strm, SeverityLevel level)
{
static const char* strings[] =
{
"TRACE",
"DEBUG",
"WARNING",
"INFO",
"ERROR",
"FATAL"
};
if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
strm << strings[level];
else
strm << static_cast< int >(level);
return strm;
}
int main(int argc, char* argv[])
{
try
{
// Create a text file sink
typedef boost::log::sinks::synchronous_sink< boost::log::sinks::text_multifile_backend > file_sink;
boost::shared_ptr< file_sink > sink(new file_sink);
// Set up how the file names will be generated
sink->locked_backend()->set_file_name_composer(
boost::log::sinks::file::as_file_name_composer(
boost::log::expressions::stream <<
"log/log_" <<
boost::log::expressions::format_date_time(dateStream, "__%Y_%m_%d__") <<
boost::log::expressions::attr< std::string >("TerminalIP") <<
".log"
)
);
sink->set_formatter
(
boost::log::expressions::stream <<
boost::log::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "[%Y/%m/%d %H:%M:%S.%f]") <<
"<" << severity << ">" <<
"[" << thread_id << "]:" <<
"<" << boost::log::expressions::format_named_scope("Scope", boost::log::keywords::format = "%n") << "> " <<
boost::log::expressions::smessage
);
// Add it to the core
boost::log::core::get()->add_sink(sink);
boost::log::core::get()->set_filter
(
severity >= WARNING
);
// Add some attributes too
boost::log::core::get()->add_global_attribute("TimeStamp", boost::log::attributes::local_clock());
boost::log::core::get()->add_global_attribute("DateStream", boost::log::attributes::local_clock());
boost::log::core::get()->add_global_attribute("Scope", boost::log::attributes::named_scope());
boost::log::core::get()->add_global_attribute("ThreadID", boost::log::attributes::current_thread_id());
// Create threads and make some logs
boost::thread_group threads;
threads.create_thread(&thread_fooMain);
threads.create_thread(&thread_foo2);
threads.create_thread(&thread_foo3);
threads.create_thread(&thread_foo4);
threads.create_thread(&thread_foo5);
threads.join_all();
return 0;
}
catch (std::exception& e)
{
std::cout << "FAILURE: " << e.what() << std::endl;
return 1;
}
}
but got this linker error:
ErrorC2664 'boost::log::v2s_mt_nt6::aux::light_function<void (boost::log::v2s_mt_nt6::basic_formatting_ostream<char,std::char_traits<char>,std::allocator<char>> &,const boost::posix_time::ptime &)>::result_type boost::log::v2s_mt_nt6::aux::light_function<void (boost::log::v2s_mt_nt6::basic_formatting_ostream<char,std::char_traits<char>,std::allocator<char>> &,const boost::posix_time::ptime &)>::operator ()(boost::log::v2s_mt_nt6::basic_formatting_ostream<char,std::char_traits<char>,std::allocator<char>> &,const boost::posix_time::ptime &) const':
cannot convert argument 1 from 'boost::log::v2s_mt_nt6::basic_formatting_ostream<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>' to 'boost::log::v2s_mt_nt6::basic_formatting_ostream<char,std::char_traits<char>,std::allocator<char>> &' sample.boost.asio e:\programming\c++\boost libraries\boost_1_66_0\boost\log\utility\functional\bind.hpp 93
Can anyone explain to me why this happened?
In Windows
boost::log::expressions::format_date_time<boost::posix_time::ptime,wchar_t>("TimeStamp",L"__%Y_%m_%d__")
In Unix
boost::log::expressions::format_date_time<boost::posix_time::ptime>("TimeStamp","__%Y_%m_%d__")
See text_multifile_backend.hpp
The essential is typedef filesystem::path result_type;
I recently tried to write a scraper capable of downloading 3.5 million
torrent files based on their magnet URL. I decided to start by hacking
an example from libtorrent's tutorial webpage, but while it works well
with just one torrent file, it fails segfaults in create_torrent() when
I try to download more than one file. Here's my code:
#include <thread>
#include <chrono>
#include <fstream>
#include <sstream>
#include <string>
#include <libtorrent/session.hpp>
#include <libtorrent/add_torrent_params.hpp>
#include <libtorrent/create_torrent.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/alert_types.hpp>
#include <libtorrent/bencode.hpp>
#include <libtorrent/torrent_status.hpp>
#include <libtorrent/torrent_info.hpp>
namespace lt = libtorrent;
using clk = std::chrono::steady_clock;
int torrents_left = 0;
int save_file(std::string const& filename, std::vector<char>& v)
{
FILE* f = std::fopen(filename.c_str(), "wb");
if (f == nullptr)
return -1;
int w = int(std::fwrite(&v[0], 1, v.size(), f));
std::fclose(f);
if (w < 0) return -1;
if (w != int(v.size())) return -3;
return 0;
}
void add_torrent_url(std::string url, lt::session& ses) {
// std::cerr << "DEBUG: Will download '" << url << "'" << std::endl;
lt::add_torrent_params atp;
atp.url = url;
atp.save_path = "."; // save in current dir
ses.async_add_torrent(atp);
torrents_left++;
}
void add_torrents_from_stdin(lt::session& ses) {
std::cerr << "DEBUG: reading stdin." << std::endl;
std::string url;
while(std::getline(std::cin, url)) {
add_torrent_url(url, ses);
}
std::cerr << "DEBUG: done reading stdin." << std::endl;
}
int main(int argc, char const* argv[])
{
lt::settings_pack pack;
pack.set_int(lt::settings_pack::alert_mask
, lt::alert::error_notification
| lt::alert::storage_notification
| lt::alert::status_notification);
lt::session ses(pack);
lt::add_torrent_params atp;
//add_torrent_url(argv[1]);
add_torrent_url("magnet:?xt=urn:btih:3E37CFE29B1049E03F858758A73EFD85BA170BE8", ses);
add_torrent_url("magnet:?xt=urn:btih:8FCDE178E3F9A24EA40856826C4E8A625A931B73", ses);
//add_torrents_from_stdin(ses);
// this is the handle we'll set once we get the notification of it being
// added
lt::torrent_handle h;
for (;;) {
std::vector<lt::alert*> alerts;
ses.pop_alerts(&alerts);
for (lt::alert const* a : alerts) {
if (auto at = lt::alert_cast<lt::add_torrent_alert>(a)) {
h = at->handle;
}
// if we receive the finished alert or an error, we're done
if (lt::alert_cast<lt::torrent_finished_alert>(a)) {
std::cout << "torrent finished or error." << std::endl;
goto done;
}
if (lt::alert_cast<lt::torrent_error_alert>(a)) {
std::cout << a->message() << std::endl;
goto done;
}
if (auto st = lt::alert_cast<lt::state_update_alert>(a)) {
if (st->status.empty()) continue;
// we only have a single torrent, so we know which one
// the status is for
lt::torrent_status const& s = st->status[0];
if (s.state == lt::torrent_status::downloading)
{
std::cout << "Hi!" << std::endl;
std::shared_ptr<const lt::torrent_info> ti = h.torrent_file();
if (ti == 0) {
std::cerr << "ERROR: ti == NULL" << std::endl;
goto done;
}
ses.remove_torrent(h, lt::session::delete_files);
lt::create_torrent new_torrent(*ti);
std::vector<char> out;
lt::bencode(std::back_inserter(out), new_torrent.generate());
std::stringstream ss;
ss << "downloaded/" << (*ti).info_hash() << ".torrent";
save_file(ss.str(), out);
h.pause();
torrents_left--;
std::cerr << "DEBUG: Done (" << torrents_left << " left): " << (*ti).info_hash() << std::endl;
if (torrents_left == 0)
goto done;
}
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// ask the session to post a state_update_alert, to update our
// state output for the torrent
ses.post_torrent_updates();
}
done:
{}
}
I suspect it's related to this part:
// we only have a single torrent, so we know which one
// the status is for
lt::torrent_status const& s = st->status[0];
But according to my debugger, when torrent_file() gives NULL, st->status[] only contains one element anyway.
What's happening here? How do I fix it?
It looks like I made wrong assumptions about what "h" points to in the example. Here's a diff that fixes the code in question:
--- scrape_rasterbar.cpp 2017-01-07 21:00:39.565636805 +0100
+++ scrape_rasterbar_old.cpp 2017-01-07 21:05:53.339718098 +0100
## -1,4 +1,3 ##
-#include <iostream>
#include <thread>
#include <chrono>
#include <fstream>
## -94,17 +93,18 ##
if (auto st = lt::alert_cast<lt::state_update_alert>(a)) {
if (st->status.empty()) continue;
- for (auto &s : st->status) {
// we only have a single torrent, so we know which one
// the status is for
+ lt::torrent_status const& s = st->status[0];
if (s.state == lt::torrent_status::downloading)
{
- std::shared_ptr<const lt::torrent_info> ti = s.handle.torrent_file();
+ std::cout << "Hi!" << std::endl;
+ std::shared_ptr<const lt::torrent_info> ti = h.torrent_file();
if (ti == 0) {
std::cerr << "ERROR: ti == NULL" << std::endl;
goto done;
}
- ses.remove_torrent(s.handle, lt::session::delete_files);
+ ses.remove_torrent(h, lt::session::delete_files);
lt::create_torrent new_torrent(*ti);
std::vector<char> out;
lt::bencode(std::back_inserter(out), new_torrent.generate());
## -112,7 +112,7 ##
std::stringstream ss;
ss << "downloaded/" << (*ti).info_hash() << ".torrent";
save_file(ss.str(), out);
- s.handle.pause();
+ h.pause();
torrents_left--;
std::cerr << "DEBUG: Done (" << torrents_left << " left): " << (*ti).info_hash() << std::endl;
if (torrents_left == 0)
## -120,7 +120,6 ##
}
}
}
- }
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// ask the session to post a state_update_alert, to update our
I need to write C++ code that measures the packet loss of ping - the percentage of the loss.
I saw the IPHLPAPI library with lots of statistics on RTT but without packet loss.
If someone know something about it, it will be great!
Thank you.
You can use the POCO library it's small (ALOT smaller than boost) and more readable than ACE when it comes to networking.
Here's the source of a ping program using the ICMPClient
You will find it under POCO_BASE/net/examples/Ping/src
#include "Poco/Util/Application.h"
#include "Poco/Util/Option.h"
#include "Poco/Util/OptionSet.h"
#include "Poco/Util/HelpFormatter.h"
#include "Poco/Util/AbstractConfiguration.h"
#include "Poco/Net/ICMPSocket.h"
#include "Poco/Net/ICMPClient.h"
#include "Poco/Net/IPAddress.h"
#include "Poco/Net/ICMPEventArgs.h"
#include "Poco/AutoPtr.h"
#include "Poco/NumberParser.h"
#include "Poco/Delegate.h"
#include <iostream>
#include <sstream>
using Poco::Util::Application;
using Poco::Util::Option;
using Poco::Util::OptionSet;
using Poco::Util::HelpFormatter;
using Poco::Util::AbstractConfiguration;
using Poco::Net::ICMPSocket;
using Poco::Net::ICMPClient;
using Poco::Net::IPAddress;
using Poco::Net::ICMPEventArgs;
using Poco::AutoPtr;
using Poco::NumberParser;
using Poco::Delegate;
class Ping: public Application
/// This sample demonstrates the Poco::Net::ICMPClient in conjunction with
/// Poco Foundation C#-like events functionality.
///
/// Try Ping --help (on Unix platforms) or Ping /help (elsewhere) for
/// more information.
{
public:
Ping():
_helpRequested(false),
_icmpClient(IPAddress::IPv4),
_repetitions(4),
_target("localhost")
{
}
protected:
void initialize(Application& self)
{
loadConfiguration(); // load default configuration files, if present
Application::initialize(self);
_icmpClient.pingBegin += Delegate<Ping, ICMPEventArgs>(this, &Ping::onBegin);
_icmpClient.pingReply += Delegate<Ping, ICMPEventArgs>(this, &Ping::onReply);
_icmpClient.pingError += Delegate<Ping, ICMPEventArgs>(this, &Ping::onError);
_icmpClient.pingEnd += Delegate<Ping, ICMPEventArgs>(this, &Ping::onEnd);
}
void uninitialize()
{
_icmpClient.pingBegin -= Delegate<Ping, ICMPEventArgs>(this, &Ping::onBegin);
_icmpClient.pingReply -= Delegate<Ping, ICMPEventArgs>(this, &Ping::onReply);
_icmpClient.pingError -= Delegate<Ping, ICMPEventArgs>(this, &Ping::onError);
_icmpClient.pingEnd -= Delegate<Ping, ICMPEventArgs>(this, &Ping::onEnd);
Application::uninitialize();
}
void defineOptions(OptionSet& options)
{
Application::defineOptions(options);
options.addOption(
Option("help", "h", "display help information on command line arguments")
.required(false)
.repeatable(false));
options.addOption(
Option("repetitions", "r", "define the number of repetitions")
.required(false)
.repeatable(false)
.argument("repetitions"));
options.addOption(
Option("target", "t", "define the target address")
.required(false)
.repeatable(false)
.argument("target"));
}
void handleOption(const std::string& name, const std::string& value)
{
Application::handleOption(name, value);
if (name == "help")
_helpRequested = true;
else if (name == "repetitions")
_repetitions = NumberParser::parse(value);
else if (name == "target")
_target = value;
}
void displayHelp()
{
HelpFormatter helpFormatter(options());
helpFormatter.setCommand(commandName());
helpFormatter.setUsage("OPTIONS");
helpFormatter.setHeader(
"A sample application that demonstrates the functionality of the "
"Poco::Net::ICMPClient class in conjunction with Poco::Events package functionality.");
helpFormatter.format(std::cout);
}
int main(const std::vector<std::string>& args)
{
if (_helpRequested)
displayHelp();
else
_icmpClient.ping(_target, _repetitions);
return Application::EXIT_OK;
}
void onBegin(const void* pSender, ICMPEventArgs& args)
{
std::ostringstream os;
os << "Pinging " << args.hostName() << " [" << args.hostAddress() << "] with " << args.dataSize() << " bytes of data:"
<< std::endl << "---------------------------------------------" << std::endl;
logger().information(os.str());
}
void onReply(const void* pSender, ICMPEventArgs& args)
{
std::ostringstream os;
os << "Reply from " << args.hostAddress()
<< " bytes=" << args.dataSize()
<< " time=" << args.replyTime() << "ms"
<< " TTL=" << args.ttl();
logger().information(os.str());
}
void onError(const void* pSender, ICMPEventArgs& args)
{
std::ostringstream os;
os << args.error();
logger().information(os.str());
}
void onEnd(const void* pSender, ICMPEventArgs& args)
{
std::ostringstream os;
os << std::endl << "--- Ping statistics for " << args.hostName() << " ---"
<< std::endl << "Packets: Sent=" << args.sent() << ", Received=" << args.received()
<< " Lost=" << args.repetitions() - args.received() << " (" << 100.0 - args.percent() << "% loss),"
<< std::endl << "Approximate round trip times in milliseconds: " << std::endl
<< "Minimum=" << args.minRTT() << "ms, Maximum=" << args.maxRTT()
<< "ms, Average=" << args.avgRTT() << "ms"
<< std::endl << "------------------------------------------";
logger().information(os.str());
}
private:
bool _helpRequested;
ICMPClient _icmpClient;
int _repetitions;
std::string _target;
};
int main(int argc, char** argv)
{
AutoPtr<Ping> pApp = new Ping;
try
{
pApp->init(argc, argv);
}
catch (Poco::Exception& exc)
{
pApp->logger().log(exc);
return Application::EXIT_CONFIG;
}
return pApp->run();
}
You can delete the logger / help stuff to make the source a bit shorter and use
OnReply / OnError / OnEnd to calculate the packet loss
You send 4 packets .... 3 packets were returned (OnReply) packet loss = 1-3/4 = 25%.
I have implemented some connection classes, using Boost ASIO, to replace some low level C code in an application, and everything is working great, except for one problem.
Basically, I have a UdpConnection class that does synchronous read and write, but it uses async methods to handle time-outs as per the boost examples. The problem is I can't figure out how to make it threadsafe.
I have tried adding strands to the event handlers to make this class threadsafe (code below), but that isn't working. I suspect it is because of the way timeout is implemented. I have included my code in 4 classes in pastebin.
Single threaded is working fine. I also have TcpConnection and UnixSocketConnection classes that don't need to be shared amongst multiple threads and they work fine. However, I can't get multi-threaded UDP code to work.
Am I missing something?
Connection.h && AsioConnection.h http://pastebin.com/Cbbw37gL
UdpConnection.h && UdpConnection.cpp http://pastebin.com/VLnHBnPs
EDIT Attaching code as suggested:
AsioConnection.h
/*
* AsioConnection.h
*
* Created on: 2011-04-08
* Author: cdunphy
*
* All classes that want to use the ASIO io_service
* and deadline timers will want to subclass this.
*/
#ifndef ASIOCONNECTION_H_
#define ASIOCONNECTION_H_
#include "Connection.h"
#include <boost/shared_ptr.hpp>
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <boost/bind.hpp>
namespace shaw_rsc
{
/*
* This exception throws if there is a timeout when connecting
* to a remote socket.
*/
struct SocketTimeoutException : public std::runtime_error
{
SocketTimeoutException(const std::string& msg) : std::runtime_error(msg)
{ }
}
;
/*
* This is the root class of every Connection
* class that wants to make use of boost asio.
*/
class AsioConnection : public Connection
{
public:
AsioConnection(
int c_timeout,
int r_timeout
) : Connection(),
conn_timeout_int(c_timeout),
read_timeout_int(r_timeout),
conn_timeout(c_timeout),
read_timeout(r_timeout),
io_service(),
strand(io_service),
deadline(strand.get_io_service()),
error()
{
reset_deadline();
}
const boost::system::error_code& getError() const
{
return error;
}
int get_read_timeout() const
{
return read_timeout_int;
}
int get_conn_timeout() const
{
return conn_timeout_int;
}
/*
* These are the callback handlers for our asynchronous
* IO operations.
*/
void handle_write(const boost::system::error_code& ec,
std::size_t len,
boost::system::error_code* out_ec,
std::size_t* out_len)
{
*out_ec = ec;
*out_len = len;
}
/*
* These are the callback handlers for our asynchronous
* IO operations.
*/
void handle_send(const boost::system::error_code& ec,
std::size_t len,
boost::system::error_code* out_ec,
std::size_t* out_len)
{
*out_ec = ec;
*out_len = len;
}
void handle_read(const boost::system::error_code& ec,
std::size_t len,
boost::system::error_code* out_ec,
std::size_t* out_len)
{
*out_ec = ec;
*out_len = len;
}
void handle_receive(const boost::system::error_code& ec,
std::size_t len,
boost::system::error_code* out_ec,
std::size_t* out_len)
{
*out_ec = ec;
*out_len = len;
}
void handle_connect(const boost::system::error_code& ec,
boost::system::error_code* out_ec)
{
*out_ec = ec;
}
protected:
int conn_timeout_int;
int read_timeout_int;
boost::posix_time::seconds conn_timeout;
boost::posix_time::seconds read_timeout;
boost::asio::io_service io_service;
boost::asio::strand strand;
boost::asio::deadline_timer deadline;
boost::system::error_code error;
void reset_deadline()
{
// No deadline is required until the first socket operation is started. We
// set the deadline to positive infinity so that the actor takes no action
// until a specific deadline is set.
deadline.expires_at(boost::posix_time::pos_infin);
}
};
}
#endif /* ASIOCONNECTION_H_ */
Connection.h
/*
* Connection.h
*
* Created on: 2011-02-25
* Author: cdunphy
*/
#ifndef CONNECTION_H_
#define CONNECTION_H_
#include <vector>
#include <string>
#include <sstream>
#include <stdexcept>
#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>
namespace shaw_rsc
{
class Connection;
const std::size_t BUF_SIZE = 128;
/*
* This is the type of reference we will
* provide to the clients.
*/
typedef boost::shared_ptr<Connection> ConnPtr;
typedef std::vector<char> DataBuffer;
typedef DataBuffer::iterator DB_Iter;
typedef DataBuffer::const_iterator DB_CIter;
// This is the mode we are using for the connection
enum Mode {
CLIENT,
SERVER
};
/*
* This is a generic class that allows data to be read or
* written to using a connection. This is quite abstract
* and it can be used both for file operations and for
* network operations.
*/
class Connection
{
public:
Connection() { }
virtual ~Connection() { }
/*
* This method writes the current contents of the data buffer
* to the connected resource. Be sure to set the right data
* in the buffer by calling the setData method first.
*
* The number of bytes written is returned.
*/
virtual std::size_t write(const DataBuffer& data) = 0;
/*
* This method reads data from the connected resource and stores
* it in our data buffer which we pass in by reference.
* Please note that it clears whatever data was in the buffer prior to
* reading.
*
* The number of bytes read is returned.
*/
virtual std::size_t read(DataBuffer& data) = 0;
virtual const std::string str() const = 0;
};
inline std::vector<unsigned char> convert_data_to_unsigned(const DataBuffer& data)
{
return std::vector<unsigned char>(data.begin(), data.end());
}
inline std::string dataBufferToStr(const DataBuffer& data)
{
return std::string(data.begin(), data.end());
}
}
#endif /* CONNECTION_H_ */
UdpConnection.h
/*
* UdpConnection.h
*
* Created on: 2011-02-25
* Author: cdunphy
*/
// Portions Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef DATAGRAMCONNECTION_H_
#define DATAGRAMCONNECTION_H_
#include "AsioConnection.h"
#include <boost/lexical_cast.hpp>
namespace shaw_rsc
{
struct UdpException: public std::runtime_error
{
UdpException(const std::string& msg) : std::runtime_error(msg) { }
};
/*
* This is the concrete class that manages UDP connections.
*/
class UdpConnection: public AsioConnection
{
public:
/*
* Use this constructor for clients (connecting to a remote socket).
*/
UdpConnection(
const std::string& _host,
const std::string& _port,
int r_timeout,
Mode mode
) : AsioConnection(0, r_timeout),
socket(strand.get_io_service()),
remote_endpoint(),
host(_host),
port(_port)
{
check_deadline();
connect(mode);
}
std::size_t write(const DataBuffer& data);
std::size_t read(DataBuffer& data);
const std::string str() const;
private:
void connect(Mode mode);
void check_deadline();
boost::asio::ip::udp::socket socket;
boost::asio::ip::udp::endpoint remote_endpoint;
std::string host;
std::string port;
};
}
#endif /* DATAGRAMCONNECTION_H_ */
UdpConnection.cpp
/*
* UdpConnection.cpp
*
* Created on: 2011-02-25
* Author: cdunphy
*/
#include "UdpConnection.h"
using std::string;
using std::endl;
using std::stringstream;
using std::exception;
using boost::asio::buffer;
using boost::asio::ip::udp;
using boost::system::error_code;
using boost::system::system_error;
using boost::asio::deadline_timer;
using boost::bind;
using boost::lexical_cast;
namespace shaw_rsc
{
size_t UdpConnection::write(const DataBuffer& data)
{
size_t bytes_written = 0;
/*
* Check to see if the socket is bad before writing
*/
if (error &&
error.value() != boost::asio::error::operation_aborted &&
error.value() != boost::asio::error::timed_out &&
error != boost::asio::error::try_again)
throw UdpException(error.message());
socket.async_send_to(buffer(data), remote_endpoint,
strand.wrap(bind(&AsioConnection::handle_send, this, _1, _2,
&error, &bytes_written)));
do
{
strand.get_io_service().run_one();
}
while (error == boost::asio::error::would_block
|| error == boost::asio::error::try_again || bytes_written == 0);
if (error)
{
if (error.value() == boost::asio::error::operation_aborted
|| error.value() == boost::asio::error::timed_out)
throw SocketTimeoutException(error.message());
else
throw UdpException(error.message());
}
reset_deadline();
return bytes_written;
}
size_t UdpConnection::read(DataBuffer& data)
{
/*
* Check to see if the socket is bad before writing
*/
if (error &&
error.value() != boost::asio::error::operation_aborted &&
error.value() != boost::asio::error::timed_out &&
error != boost::asio::error::try_again)
throw UdpException(error.message());
data.clear();
/*
* Reset the deadline timer to expire according
* to the configured read timeout.
*/
deadline.expires_from_now(read_timeout);
size_t bytes_read = 0;
boost::array<char, BUF_SIZE> buff;
error = boost::asio::error::would_block;
socket.async_receive_from(buffer(buff), remote_endpoint,
strand.wrap(boost::bind(&AsioConnection::handle_receive, this, _1, _2, &error,
&bytes_read)));
do
{
strand.get_io_service().run_one();
}
while (error == boost::asio::error::would_block ||
error == boost::asio::error::try_again || bytes_read == 0);
/*
* Check for errors after the read.
*/
if (error)
{
if (error.value() == boost::asio::error::operation_aborted
|| error.value() == boost::asio::error::timed_out)
throw SocketTimeoutException(error.message());
else
throw UdpException(error.message());
}
else
data.insert(data.end(), buff.begin(), buff.begin() + bytes_read);
// Reset the deadline timer so we can leave this socket open as long
// as we want.
reset_deadline();
return bytes_read;
}
void UdpConnection::connect(Mode mode)
{
socket.open(boost::asio::ip::udp::v4());
if (mode == SERVER)
{
socket.bind(
udp::endpoint(udp::v4(),
lexical_cast<int>(port)), error);
}
else if (mode == CLIENT)
{
udp::resolver resolver(strand.get_io_service());
udp::resolver::query query(udp::v4(), host, port);
remote_endpoint = *resolver.resolve(query, error);
}
}
void UdpConnection::check_deadline()
{
// Check whether the deadline has passed. We compare the deadline against
// the current time since a new asynchronous operation may have moved the
// deadline before this actor had a chance to run.
if (deadline.expires_at() <= deadline_timer::traits_type::now())
{
// The deadline has passed. The outstanding asynchronous operation needs
// to be cancelled so that the blocked receive() function will return.
//
// Please note that cancel() has portability issues on some versions of
// Microsoft Windows, and it may be necessary to use close() instead.
// Consult the documentation for cancel() for further information.
socket.cancel();
// There is no longer an active deadline. The expiry is set to positive
// infinity so that the actor takes no action until a new deadline is set.
reset_deadline();
}
// Put the actor back to sleep.
deadline.async_wait(strand.wrap(boost::bind(&UdpConnection::check_deadline, this)));
}
/*
* This member function is good for diagnostic purposes
*/
const string UdpConnection::str() const
{
stringstream sstr;
sstr << "Host: " << host << endl;
sstr << "Port: " << port << endl;
sstr << "Read timeout: " << read_timeout_int << endl;
sstr << "Remote Endpoint Address: " << remote_endpoint.address().to_string()
<< endl;
sstr << "Remote Endpoint Port: " << remote_endpoint.port() << endl;
try
{
sstr << "Socket Remote Endpoint Address: "
<< socket.remote_endpoint().address().to_string() << endl;
sstr << "Socket Remote Endpoint Port: "
<< socket.remote_endpoint().port() << endl;
}
catch (exception& e)
{ }
try
{
sstr << "Socket Local Endpoint Address: "
<< socket.local_endpoint().address().to_string() << endl;
sstr << "Socket Local Endpoint Port: " << socket.local_endpoint().port()
<< endl;
}
catch (exception& e)
{ }
return sstr.str();
}
}
EDIT2:
Here is the test code I am trying to get working:
Server that replies in C++. All tests are working EXCEPT the threaded Udp test:
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE TestLibRSCAsio
#include <cstdio>
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <iostream>
#include <string>
#include <exception>
#include <sstream>
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <boost/date_time.hpp>
#include <boost/thread.hpp>
#include <boost/lexical_cast.hpp>
#include "boost/date_time/posix_time/posix_time.hpp"
#include <rsc_asio/Connection.h>
#include <rsc_asio/TcpConnection.h>
#include <rsc_asio/UdpConnection.h>
#include <rsc_asio/UnixSocketConnection.h>
#include "Util.h"
#include "sha1/Sha1Calc.h"
#include "servers/TestTcpServer.h"
#include <boost/test/unit_test.hpp>
using std::vector;
using std::string;
using std::size_t;
using std::cerr;
using std::cout;
using std::endl;
using std::flush;
using std::exception;
using std::time;
using std::stringstream;
using boost::lexical_cast;
using boost::thread;
using boost::mutex;
using boost::unique_lock;
using namespace shaw_rsc;
const size_t TCP_BYTE_SZ = 1000000;
const size_t UDP_BYTE_SZ = 64;
const std::string FILE_SOCKET = "/tmp/rofl";
const std::string SERVER_HOST = "0.0.0.0";
const std::string SERVER_PORT = "10999";
const std::string EXPECTED_UDP_REQUEST = "GOT_ANSWER?";
const int TIMEOUT = 3;
const int THREAD_TIMEOUT = 10;
DataBuffer write_data(ConnPtr client, Sha1Calc& sha1calc, size_t size, size_t iter)
{
unique_lock<mutex>(global_mutex);
cout << "Iter: " << iter << endl;
DataBuffer data = getRandomData(size);
sha1calc.calc_client_digest(data);
size_t bytes_written = client->write(data);
cout << "Wrote " << bytes_written << " -> " << dataBufferToStr(data) << " to socket: " << endl << client->str() << endl;
return data;
}
void write_data_threaded(ConnPtr client, Sha1Calc& sha1calc, size_t size, size_t iter)
{
cout << "Iter: " << iter << endl;
DataBuffer data = getRandomData(size);
sha1calc.calc_client_digest(data);
size_t bytes_written = client->write(data);
cout << "Wrote " << bytes_written << " -> " << dataBufferToStr(data) << " to socket: " << endl << client->str() << endl;
}
DataBuffer read_data(ConnPtr server, Sha1Calc& sha1calc, size_t iter)
{
cout << "Iter: " << iter << endl;
DataBuffer data;
size_t bytes_read = server->read(data);
cout << "Read " << bytes_read << " -> " << dataBufferToStr(data) << " from socket: " << endl << server->str() << endl;
sha1calc.calc_server_digest(data);
return data;
}
/*
* This is a suite of tests to provide unit tests
* for the RRE.
*/
BOOST_AUTO_TEST_SUITE(TestLibRSCAsioSuite)
BOOST_AUTO_TEST_CASE(TcpTest)
{
boost::asio::io_service io_service;
cout << endl << "**** TCP Test ****" << endl;
Sha1Calc sha1calc;
cout << endl << "Generating " << TCP_BYTE_SZ << " bytes of data to serve up." << endl;
DataBuffer dataToServe = getRandomData(TCP_BYTE_SZ);
sha1calc.calc_server_digest(dataToServe);
cout << "SHA1 hash of server data: " <<
sha1_to_str(sha1calc.get_server_digest()) << endl;
SrvPtr server(new TestTcpServer(std::atoi(SERVER_PORT.c_str()), dataToServe, io_service));
server->start();
try
{
// Fire up a basic TCP client for testing
cout << "Firing up TCP client on port: " << SERVER_PORT << endl;
DataBuffer clientData;
ConnPtr client(new TcpConnection(SERVER_HOST, SERVER_PORT, TIMEOUT, TIMEOUT, io_service));
size_t bytesRead = client->read(clientData);
BOOST_REQUIRE( bytesRead == TCP_BYTE_SZ );
BOOST_REQUIRE( clientData.size() == TCP_BYTE_SZ );
sha1calc.calc_client_digest(clientData);
BOOST_REQUIRE( sha1calc.compare() );// SHA-1 hashes better matctype filter texth
}
catch (SocketTimeoutException& e)
{
cerr << "Socket timeout: " << e.what() << endl;
BOOST_FAIL("Socket Timeout");
}
catch (const TcpException& e)
{
cerr << "TCP Error: " << e.what() << endl;
BOOST_FAIL("TCP Exception");
}
catch (const exception& e)
{
cerr << "Other Error: " << e.what() << endl;
BOOST_FAIL("Unknown Exception");
}
}
BOOST_AUTO_TEST_CASE(UdpTest)
{
boost::asio::io_service io_service;
std::stringstream error;
try
{
cout << endl << "**** UDP Test ****" << endl;
ConnPtr client(new UdpConnection(SERVER_HOST, SERVER_PORT, TIMEOUT, CLIENT, io_service));
ConnPtr server(new UdpConnection(SERVER_HOST, SERVER_PORT, TIMEOUT, SERVER, io_service));
for (int i = 0; i != 10; ++i)
{
Sha1Calc sha1calc;
// Write the data to the client
DataBuffer clientData = write_data(client, sha1calc, UDP_BYTE_SZ, i);
// Read the data from the server
DataBuffer serverData = read_data(server, sha1calc, i);
// Make sure the client data matches the server data
BOOST_REQUIRE( sha1calc.compare() );
cout << endl; // new-line
}
}
catch (const SocketTimeoutException& e)
{
error << "Socket timeout: " << e.what() << endl;
BOOST_FAIL(error.str());
}
catch (const UdpException& e)
{
error << "UDP Exception: " << e.what() << endl;
BOOST_FAIL(error.str());
}
catch (const exception& e)
{
error << "Other Error: " << e.what() << endl;
BOOST_FAIL(error.str());
}
}
BOOST_AUTO_TEST_CASE(UdpThreadTest)
{
boost::asio::io_service io_service;
std::stringstream error;
try
{
cout << endl << "**** UDP Multi-thread Test ****" << endl;
ConnPtr server(new UdpConnection(SERVER_HOST, SERVER_PORT, THREAD_TIMEOUT, SERVER, io_service));
Sha1Calc sha1calc;
for (int i = 0; i != 10; ++i)
{
// Read the data from the server, make sure it matches
// the expected request?
DataBuffer serverData = read_data(server, sha1calc, i);
BOOST_REQUIRE(dataBufferToStr(serverData) == EXPECTED_UDP_REQUEST);
// Repply on the remote socket
thread t1(bind(&write_data_threaded, server, sha1calc, UDP_BYTE_SZ, i));
cout << endl; // new-line
}
}
catch (const SocketTimeoutException& e)
{
error << "Socket timeout: " << e.what() << endl;
BOOST_FAIL(error.str());
}
catch (const UdpException& e)
{
error << "UDP Exception: " << e.what() << endl;
BOOST_FAIL(error.str());
}
catch (const exception& e)
{
error << "Other Error: " << e.what() << endl;
BOOST_FAIL(error.str());
}
}
BOOST_AUTO_TEST_CASE(UnixSocketTest)
{
boost::asio::io_service io_service;
try
{
cout << endl << "**** UNIX Socket Test ****" << endl;
std::remove(FILE_SOCKET.c_str());
ConnPtr server(new UnixSocketConnection(FILE_SOCKET, TIMEOUT, SERVER, io_service));
ConnPtr client(new UnixSocketConnection(FILE_SOCKET, TIMEOUT, CLIENT, io_service));
Sha1Calc sha1calc;
DataBuffer clientData = write_data(client, sha1calc, UDP_BYTE_SZ, 0);
cout << "Wrote the data to the Unix Socket client:" << dataBufferToStr(clientData) << endl;
DataBuffer serverData = read_data(server, sha1calc, 0);
cout << "Read from UDP Server: " << dataBufferToStr(serverData) << endl;
BOOST_REQUIRE( sha1calc.compare() );
cout << sha1_to_str(sha1calc.get_server_digest()) << endl;
}
catch (const SocketTimeoutException& e)
{
cerr << "Socket timeout: " << e.what() << endl;
BOOST_FAIL("Socket Timeout");
}
catch (const UnixSocketException& e)
{
cerr << "UNIX Socket Error: " << e.what() << endl;
BOOST_FAIL("UNIXSocket Exception");
}
catch (const exception& e)
{
cerr << "Other Error: " << e.what() << endl;
BOOST_FAIL("Unknown Exception");
}
std::remove(FILE_SOCKET.c_str());
}
BOOST_AUTO_TEST_SUITE_END()
}
Client written in Java:
package com.shaw.udp.sender;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
public class Client {
public static void main(String[] args) throws Exception {
DatagramChannel channel = DatagramChannel.open();
SocketAddress address = new InetSocketAddress(0);
SocketAddress client = new InetSocketAddress(SERVER_HOST, 10999);
DatagramSocket socket = channel.socket();
// This is the local socket
socket.setSoTimeout(5000);
socket.bind(address);
for (int i = 0; i != 10; ++i) {
// Send the data to the remote server
ByteBuffer buffer = ByteBuffer.wrap("GOT_ANSWER?".getBytes());
channel.send(buffer, client);
System.out.println("Iter: " + i + " => Sent request: "
+ new String(buffer.array()));
// Listen for reply from the server
buffer = ByteBuffer.allocate(64);
channel.receive(buffer);
System.out.println("Iter: " + i + " => Got reply: "
+ new String(buffer.array()));
}
}
}