I am using Apache Thrift TSimpleServer in my C++ projects. Once started, the server will be listening to clients connection and handling requests. All are fine, but occasionally the server stopped without any sign.
I follow the source of thrift library and can see the GlobalOutput(error_message) being created when TTransportException or TException are caught. I need to understand this so that I can create a recovery mechanism when the server dies.
Here is the source code I am talking about:
void TSimpleServer::serve() {
shared_ptr<TTransport> client;
shared_ptr<TTransport> inputTransport;
shared_ptr<TTransport> outputTransport;
shared_ptr<TProtocol> inputProtocol;
shared_ptr<TProtocol> outputProtocol;
// Start the server listening
serverTransport_->listen();
// Run the preServe event
if (eventHandler_) {
eventHandler_->preServe();
}
// Fetch client from server
while (!stop_) {
try {
client = serverTransport_->accept();
inputTransport = inputTransportFactory_->getTransport(client);
outputTransport = outputTransportFactory_->getTransport(client);
inputProtocol = inputProtocolFactory_->getProtocol(inputTransport);
outputProtocol = outputProtocolFactory_->getProtocol(outputTransport);
} catch (TTransportException& ttx) {
if (inputTransport) { inputTransport->close(); }
if (outputTransport) { outputTransport->close(); }
if (client) { client->close(); }
if (!stop_ || ttx.getType() != TTransportException::INTERRUPTED) {
string errStr = string("TServerTransport died on accept: ") + ttx.what();
GlobalOutput(errStr.c_str());
}
continue;
} catch (TException& tx) {
if (inputTransport) { inputTransport->close(); }
if (outputTransport) { outputTransport->close(); }
if (client) { client->close(); }
string errStr = string("Some kind of accept exception: ") + tx.what();
GlobalOutput(errStr.c_str());
continue;
} catch (string s) {
if (inputTransport) { inputTransport->close(); }
if (outputTransport) { outputTransport->close(); }
if (client) { client->close(); }
string errStr = string("Some kind of accept exception: ") + s;
GlobalOutput(errStr.c_str());
break;
}
// Get the processor
shared_ptr<TProcessor> processor = getProcessor(inputProtocol,
outputProtocol, client);
void* connectionContext = NULL;
if (eventHandler_) {
connectionContext = eventHandler_->createContext(inputProtocol, outputProtocol);
}
try {
for (;;) {
if (eventHandler_) {
eventHandler_->processContext(connectionContext, client);
}
if (!processor->process(inputProtocol, outputProtocol,
connectionContext) ||
// Peek ahead, is the remote side closed?
!inputProtocol->getTransport()->peek()) {
break;
}
}
} catch (const TTransportException& ttx) {
string errStr = string("TSimpleServer client died: ") + ttx.what();
GlobalOutput(errStr.c_str());
} catch (const std::exception& x) {
GlobalOutput.printf("TSimpleServer exception: %s: %s",
typeid(x).name(), x.what());
} catch (...) {
GlobalOutput("TSimpleServer uncaught exception.");
}
if (eventHandler_) {
eventHandler_->deleteContext(connectionContext, inputProtocol, outputProtocol);
}
try {
inputTransport->close();
} catch (const TTransportException& ttx) {
string errStr = string("TSimpleServer input close failed: ")
+ ttx.what();
GlobalOutput(errStr.c_str());
}
try {
outputTransport->close();
} catch (const TTransportException& ttx) {
string errStr = string("TSimpleServer output close failed: ")
+ ttx.what();
GlobalOutput(errStr.c_str());
}
try {
client->close();
} catch (const TTransportException& ttx) {
string errStr = string("TSimpleServer client close failed: ")
+ ttx.what();
GlobalOutput(errStr.c_str());
}
}
if (stop_) {
try {
serverTransport_->close();
} catch (TTransportException &ttx) {
string errStr = string("TServerTransport failed on close: ") + ttx.what();
GlobalOutput(errStr.c_str());
}
stop_ = false;
}
}
Deep inside TOutput.cpp there's line fprintf(stderr, "Thrift: %s %s\n", dbgtime, msg); (source here) and that's where by default all Thrift GlobalOutput messages end up (in standard error).
But you can change it (if for any reason you can't use stderr) by providing own handler to GlobalOutput in form of function pointer:
void myOutputFunction(const char* x)
{
fprintf(myLogFile, "Thrift internal message: %s\n", x);
}
// Inside some init function or main
GlobalOutput.setOutputFunction(myOutputFunction);
Related
My program should synchronize with remote postgres database. My code:
db_handler::DB_Handler::DB_Handler(const char* host, unsigned int port, const char* db, const char* user, const char* password, int& error) {
...
this->password = reinterpret_cast<char*>(malloc(std::strlen(password) + 1));
strcpy(this->password, password);
this->connection = nullptr;
if (this->connect_to_db() != 0) {
error = 1;
}
else {
error = 0;
}
}
int db_handler::DB_Handler::connect_to_db() {
try {
std::string connection_params = "host=";
connection_params += std::string(host);
...
connection_params += " password=";
connection_params += password;
if (this->connection != nullptr) {
delete this->connection;
}
this->connection = new pqxx::connection(connection_params.c_str());
return 0;
}
catch (...) {
return 1;
}
}
int db_handler::DB_Handler::sync() {
for (int tries = 0; tries < 2; tries++) {
try {
this->connection->prepare("get_keys", "*query here*");
...
}
catch (const pqxx::broken_connection& e) {
this->connect_to_db();
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
catch (...) {
return 1;
}
}
return 1;
}
And on this row this->connection->prepare("get_keys", "*query here*"); my program freezes forever until I will restart it.
Where am I wrong and how can I fix it?
Maybe I should add some timeouts? How can I do it?
Good afternoon. I am using this code exactly and would like to modify it to suit my needs.
https://www.boost.org/doc/libs/1_77_0/doc/html/boost_asio/example/cpp11/echo/async_tcp_echo_server.cpp
In the function do_write, I want to raise another server, which will be on an arbitrary port, which at a certain moment could turn itself off and send a signal about this to the connection within which it was created (the connection is active all this time, of course).
I cannot add a server to the io_context that is already started at the moment. is there any other possibility? I tried to create another instance of io_context and create the server on a different thread, but that also didn't work for me.
I started from that linked example and made the session read linewise commands:
LISTEN <port> to start a new listener on that port
STOP <port> to stop a listener
EXIT to close the connection
Listeners are kept in a global map by port number:
using Port = uint16_t;
using Server = std::weak_ptr<class server>;
std::map<Port, Server> g_listeners;
Servers take an optional Callback:
using Callback = std::function<void()>;
That is used to report the exit of a listener to the connection that originally started it, if it is still active.
Commands
Reading the linewise commands is pretty straightforward:
void do_read()
{
async_read_until(
socket_, data_, "\n",
[this, self = shared_from_this()](error_code ec, size_t length) {
if (ec) {
std::cerr << "do_read: " << ec.message() << std::endl;
} else {
std::string line;
getline(std::istream(&data_), line);
if (handle(line))
do_read();
}
});
}
handle parses them - I kept it as simple as I could:
bool handle(std::string const& command)
{
try {
std::istringstream parser(command);
parser.exceptions(std::ios::failbit | std::ios::badbit);
std::string cmd;
Port port;
if (parser >> cmd && (cmd == "LISTEN") && parser >> port) {
start_listener(port);
return true;
}
parser.clear();
parser.str(command);
if (parser >> cmd && (cmd == "STOP") && parser >> port) {
stop_listener(port);
return true;
}
parser.clear();
parser.str(command);
if (parser >> cmd && (cmd == "EXIT")) {
message("Goodbye");
return false;
}
message("Invalid command"s);
} catch (std::exception const& e) {
message("Invalid argument"s);
}
return true;
}
Now the commands start_listener and stop_listener insert or erase server instances from the g_listeners container:
void session::start_listener(Port port)
{
auto [it, inserted] = g_listeners.try_emplace(port);
if (!inserted) {
message("Already listening on port " + std::to_string(port));
} else {
auto on_close = [handle = weak_from_this(), port] {
if (auto self = handle.lock())
self->message("The listener for port " + std::to_string(port) + " has closed");
};
auto s = std::make_shared<server>( //
socket_.get_executor(), port, on_close);
it->second = s;
s->start();
message("Started listening on port " + std::to_string(port));
}
}
void session::stop_listener(Port port)
{
auto it = g_listeners.find(port);
if (it != g_listeners.end()) {
if (auto server = it->second.lock())
{
message("Stopping listener on port " + std::to_string(port));
server->stop();
} else {
// when two connections simultaneously STOP the same listener?
message("Listener on port " + std::to_string(port) + " already stopped");
}
g_listeners.erase(it);
} else {
message("No listener on port " + std::to_string(port));
}
}
Note that weak pointers are used on both ends to prevent operating on already-destructed instances e.g.
when the connection that started a listener has closed before the callback can be executed
when a listener is being commanded to STOP from multiple connections at the exact same time
Other Notes
For the response messages I use the outbox_ pattern so the buffer lifetimes are guaranteed even if more than one message is queued.
For the server, I changed the io_context& argument to be an executor; the result is equivalent but it's much simpler to get an executor from any IO object than to pass a reference to the io_context& around. Of course, you can "cheat" by making io_context a global variable.
Demo
Live On Coliru
#include <boost/asio.hpp>
#include <deque>
#include <iostream>
#include <iomanip>
using boost::asio::ip::tcp;
using boost::system::error_code;
using executor_type = boost::asio::any_io_executor;
using namespace std::literals;
using Port = uint16_t;
using Callback = std::function<void()>;
using Server = std::weak_ptr<class server>;
std::map<Port, Server> g_listeners;
class session : public std::enable_shared_from_this<session> {
public:
session(tcp::socket socket) : socket_(std::move(socket)) {}
void start()
{
do_read();
message("Welcome");
}
private:
void do_read()
{
async_read_until(
socket_, data_, "\n",
[this, self = shared_from_this()](error_code ec, size_t length) {
if (ec) {
std::cerr << "do_read: " << ec.message() << std::endl;
} else {
std::string line;
getline(std::istream(&data_), line);
if (handle(line))
do_read();
}
});
}
bool handle(std::string const& command)
{
try {
std::istringstream parser(command);
parser.exceptions(std::ios::failbit | std::ios::badbit);
std::string cmd;
Port port;
if (parser >> cmd && (cmd == "LISTEN") && parser >> port) {
start_listener(port);
return true;
}
parser.clear();
parser.str(command);
if (parser >> cmd && (cmd == "STOP") && parser >> port) {
stop_listener(port);
return true;
}
parser.clear();
parser.str(command);
if (parser >> cmd && (cmd == "EXIT")) {
message("Goodbye");
return false;
}
message("Invalid command"s);
} catch (std::exception const& e) {
message("Invalid argument"s);
}
return true;
}
void message(std::string msg) {
outbox_.push_back(std::move(msg) + "\n");
if (outbox_.size() == 1)
do_write();
}
void do_write() {
async_write( //
socket_, boost::asio::buffer(outbox_.front()),
[this, self = shared_from_this()](error_code ec, size_t) {
if (ec) {
std::cerr << "do_write: " << ec.message() << std::endl;
}
outbox_.pop_front();
if (!outbox_.empty())
do_write();
});
}
void start_listener(Port port);
void stop_listener(Port port);
tcp::socket socket_;
boost::asio::streambuf data_{32*1024}; // max size
std::deque<std::string> outbox_;
};
class server: public std::enable_shared_from_this<server> {
public:
server(
executor_type exe, short port, Callback callback = [] {})
: acceptor_(exe, tcp::endpoint(tcp::v4(), port))
, on_close_(callback)
{
}
void start() { do_accept(); }
void stop() { acceptor_.close(); }
private:
void do_accept()
{
acceptor_.async_accept(
[this, self = shared_from_this()] //
(error_code ec, tcp::socket socket) {
if (!ec) {
std::make_shared<session>(std::move(socket))->start();
do_accept();
} else {
if (on_close_)
on_close_();
}
});
}
tcp::acceptor acceptor_;
Callback on_close_;
};
void session::start_listener(Port port)
{
auto [it, inserted] = g_listeners.try_emplace(port);
if (!inserted) {
message("Already listening on port " + std::to_string(port));
} else {
auto on_close = [handle = weak_from_this(), port] {
if (auto self = handle.lock())
self->message("The listener for port " + std::to_string(port) + " has closed");
};
auto s = std::make_shared<server>( //
socket_.get_executor(), port, on_close);
it->second = s;
s->start();
message("Started listening on port " + std::to_string(port));
}
}
void session::stop_listener(Port port)
{
auto it = g_listeners.find(port);
if (it != g_listeners.end()) {
if (auto server = it->second.lock())
{
message("Stopping listener on port " + std::to_string(port));
server->stop();
} else {
// when two connections simultaneously STOP the same listener?
message("Listener on port " + std::to_string(port) + " already stopped");
}
g_listeners.erase(it);
} else {
message("No listener on port " + std::to_string(port));
}
}
int main(int argc, char* argv[])
{
try {
if (argc != 2) {
std::cerr << "Usage: async_tcp_echo_server <port>\n";
return 1;
}
boost::asio::io_context io_context;
{
Port port = std::atoi(argv[1]);
auto s = std::make_shared<server>(io_context.get_executor(), port);
s->start();
g_listeners.emplace(port, s);
}
io_context.run();
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
}
Which demo's interactively:
I try to re - transmit sse from an osgi plugin . I have this code and at the moment i can only read the sse from event source and print it out in the console correctly. The sse write part is not working properly and buffering instead. Is there any way to fix it and read and write from the same function?
Thanks in advance!
#ApplicationPath("MySensor")
public class MySensor extends ResourceConfig {
private static String sensorA = "smotion";
// private static String sensorB = "sdist";
// private static String sensorC = "slight";
private int id = 0;
private String idn = "";
public MySensor() {
super(MySensor.class, SseFeature.class);
}
// creates new broadcaster
private static SseBroadcaster BROADCASTER = new SseBroadcaster();
#MethodDescription(value = "sse")
#GET
#Consumes(SseFeature.SERVER_SENT_EVENTS)
#Produces(SseFeature.SERVER_SENT_EVENTS)
public EventOutput getServerSentEvents() {
id = id + 1;
idn = sensorA + " " + id;
BROADCASTER.broadcast(new OutboundEvent.Builder().data(String.class, idn).build());
// System.out.println(BROADCASTER.);
String LocalNetworkIP = "192.168.1.134";
EventOutput eventOutput = new EventOutput();
Client client = ClientBuilder.newBuilder().register(SseFeature.class).build();
WebTarget target = client.target("http://" + LocalNetworkIP + "/" + sensorA);
EventInput eventInput = target.request().get(EventInput.class);
while (!eventInput.isClosed()) {
InboundEvent inboundEvent = eventInput.read();
if (inboundEvent == null) {
break; // connection has been closed
}
try {
// handleevent
// inboundEvent.readData(String.class);
System.out.println(inboundEvent.readData(String.class));
OutboundEvent.Builder eventBuilder = new OutboundEvent.Builder();
eventBuilder.name(inboundEvent.getName());
eventBuilder.data(inboundEvent.readData(String.class));
OutboundEvent event = eventBuilder.build();
eventOutput.write(event);
BROADCASTER.add(eventOutput);
} catch (IOException e) {
throw new RuntimeException("Error when writing the event.", e);
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
try {
eventOutput.close();
} catch (IOException ioClose) {
throw new RuntimeException("Error when closing the event output.", ioClose);
}
return eventOutput;
}
I Finally find it out!
#MethodDescription(value = "Return the Server Sent Event")
#GET
#Consumes(SseFeature.SERVER_SENT_EVENTS)
#Produces(SseFeature.SERVER_SENT_EVENTS)
public EventOutput getServerSentEvents() {
id = id + 1;
idn = sensorA + " " + id;
final EventOutput eventOutput = new EventOutput();
Client client = ClientBuilder.newBuilder().register(SseFeature.class).build();
WebTarget target = client.target("http://" + LocalNetworkIP + "/" + sensorA);
final EventInput eventInput = target.request().get(EventInput.class);
new Thread(new Runnable() {
#Override
public synchronized void run() {
try {
while (!eventInput.isClosed()) {
// Thread.sleep(500);
InboundEvent inboundEvent = eventInput.read();
if (inboundEvent == null) {
break; // connection has been closed
}
try {
// handleevent
// inboundEvent.readData(String.class);
System.out.println(inboundEvent.readData(String.class));
OutboundEvent.Builder eventBuilder = new OutboundEvent.Builder();
eventBuilder.name(inboundEvent.getName());
eventBuilder.data(inboundEvent.readData(String.class));
OutboundEvent event = eventBuilder.build();
eventOutput.write(event);
} catch (IOException e) {
try { //extra
eventOutput.close(); //extra
eventInput.close(); //extra
} catch (IOException ioClose) { //extra
throw new RuntimeException("Error when closing the event output internal.", ioClose); //extra
} //extra
throw new RuntimeException("Error when writing or reading the event.", e);
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (!eventOutput.isClosed()) { //extra
eventOutput.close(); //extra
} //extra
if (!eventInput.isClosed()) { //extra
eventInput.close();
} //extra
} catch (IOException ioClose) {
throw new RuntimeException("Error when closing the event output.", ioClose);
}
}
}
}).start();
return eventOutput;
}
}
I am implementing a class that uses boost::asio to implement a library for TLS connections.
I am implementing only synchronous operations and some of them accept a timeout. I implement the timeout methods using a deadline_timer and io_service.run_one, as explained in this example: http://www.boost.org/doc/libs/1_45_0/doc/html/boost_asio/example/timeouts/async_tcp_client.cpp
My problem is with a method that reads exactly 'n' bytes from the socket and accepts a timeout as a parameter. The problem is that the io_service.run_one() is raising a SIGSEV and I do no know why. Below is the code (it is so long, but I do not know any other better way to explain this):
The code
Below are the methods involved in the test I am executing:
void CMDRboostConnection::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 (m_timeoutOpsTimer->expires_at() <= boost::asio::deadline_timer::traits_type::now())
{
// TODO do I need to cancel async operations?
m_timeoutOpsErrorCode = boost::asio::error::timed_out;
// 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.
m_timeoutOpsTimer->expires_at(boost::posix_time::pos_infin);
}
// Put the actor back to sleep.
m_timeoutOpsTimer->async_wait(
boost::bind(&CMDRboostConnection::check_deadline, this));
}
bool CMDRboostConnection::connect()
{
// TODO: This method already throws an exception, it should be void.
DEBUG("Connecting to " + m_url + " : " + m_port);
try
{
// If the socket is already connected, disconnect it before
// opening a new conneciont.
if (isConnected())
{
disconnect();
}
m_socket = new SSLSocket(m_ioService, m_context);
tcp::resolver resolver(m_ioService);
tcp::resolver::query query(m_url, m_port);
tcp::resolver::iterator end;
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
boost::asio::connect(m_socket->lowest_layer(), resolver.resolve(query));
if (endpoint_iterator == end)
{
DEBUG("Endpoint cannot be resolved, disconnecting...");
disconnect();
}
else
{
m_timeoutOpsTimer = new boost::asio::deadline_timer(m_ioService);
m_timeoutOpsTimer->expires_at(boost::posix_time::pos_infin);
// Start the persistent actor that checks for deadline expiry.
check_deadline();
DEBUG("Endpoint resolved, performing handshake");
m_socket->set_verify_mode(boost::asio::ssl::verify_none);
m_socket->handshake(SSLSocket::client);
DEBUG("Handshake done, connected to " + m_url + " : " + m_port);
m_isConnected = true;
}
}
catch (boost::system::system_error &err)
{
disconnect();
throw;
}
return m_isConnected;
}
std::streambuf& CMDRboostConnection::readNBytes(int n, unsigned int timeout)
{
try
{
if(!isConnected())
{
std::string err = "Cannot read, not connected";
ERROR(err);
throw std::logic_error(err);
}
if(n == 0)
{
return m_buffer;
}
m_timeoutOpsTimer->expires_from_now(
boost::posix_time::milliseconds(timeout));
m_timeoutOpsErrorCode = boost::asio::error::would_block;
boost::asio::async_read(
*m_socket,
m_buffer,
boost::asio::transfer_exactly(n),
boost::bind(
&CMDRboostConnection::timoutOpsCallback,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred)
);
do
{
m_ioService.run_one();
} while (m_timeoutOpsErrorCode == boost::asio::error::would_block);
if(m_timeoutOpsErrorCode)
{
throw boost::system::system_error(m_timeoutOpsErrorCode);
}
return m_buffer;
}
catch(boost::system::system_error &err)
{
ERROR("Timeout reached trying to read a message");
disconnect();
throw;
}
}
void CMDRboostConnection::disconnect()
{
try
{
DEBUG("Disconnecting...");
if(isConnected())
{
m_socket->shutdown();
DEBUG("Closing socket...");
m_socket->lowest_layer().close();
if(m_socket != NULL)
{
delete m_socket;
m_socket = NULL;
}
}
if(m_timeoutOpsTimer != NULL)
{
delete m_timeoutOpsTimer;
m_timeoutOpsTimer = NULL;
}
DEBUG("Disconnection performed properly");
m_isConnected = false;
}
catch (boost::system::system_error &err)
{
ERROR("Exception thrown, error = " << err.code() <<
", category: " << err.code().category().name() << std::endl);
m_isConnected = false;
throw;
}
}
The test
Below it is the test I am running to test the method:
TEST(CMDRboostConnection, readNbytesTimeoutDoesNotMakeTheProgramCrashWhenTmeout)
{
std::auto_ptr<CMDR::SSL::ICMDRsslConnection> m_connection =
std::auto_ptr<CMDR::SSL::ICMDRsslConnection>(
new CMDR::SSL::CMDRboostConnection("localhost", "9999"));
unsigned int sleepInterval = 0; // seconds
unsigned int timeout = 10; // milliseconds
unsigned int numIterations = 10;
std::string msg("delay 500000"); // microseconds
if(!m_connection->isConnected())
{
m_connection->connect();
}
for(unsigned int i = 0; i < numIterations; i++)
{
if(!m_connection->isConnected())
{
m_connection->connect();
}
ASSERT_NO_THROW( m_connection->write(msg) );
ASSERT_THROW (
m_connection->readNBytes(msg.size(), timeout),
boost::system::system_error);
ASSERT_FALSE(m_connection->isConnected());
ASSERT_NO_THROW( m_connection->connect() );
sleep(sleepInterval);
}
}
The problem
In the above test, the first loop iteration goes ok, that is, the first time the method readNBytes is called, it works (throws an exception as expected). The second time it is executed, it raises the SIGSEV.
EDIT
I am executing the above test among others that test other functionalities. I have realized that if I execute the above test only, it works. But, If I execute it in addition other, then the program crashes with the mentioned SIGSEV.
This is one of the tests that causes the problem:
TEST(CMDRboostConnection, canConnectDisconnect)
{
std::auto_ptr<CMDR::SSL::ICMDRsslConnection> m_connection =
std::auto_ptr<CMDR::SSL::ICMDRsslConnection>(
new CMDR::SSL::CMDRboostConnection("localhost", "9999"));
unsigned int sleepInterval = 0; // seconds
unsigned int timeout = 1000; // milliseconds
unsigned int numIterations = 10;
std::string msg("normally");
if(!m_connection->isConnected())
{
ASSERT_NO_THROW (m_connection->connect() );
}
for(unsigned int i = 0; i < numIterations; i++)
{
ASSERT_NO_THROW( m_connection->disconnect() );
sleep(sleepInterval);
ASSERT_NO_THROW( m_connection->connect() );
}
}
In conclusion, If I execute both the above tests, the first one crashes. But if I execute only the first one, it works.
EDIT 2
Fixed the bug mentioned in the comments.
You've messed up pointers and object lifetime management. If connect method is called when already connected you overwrite old socket with new and only then check whether it was already connected or used somewhere. Also auto_ptr is deprecated. You should use unique_ptr to manage owning pointers instead.
I have replaced all the member attributes by pointers, and now it works (that is, I can pass all tests I have write). The methods disconnect / connect are now as following:
bool CMDRboostConnection::connect()
{
// TODO: This method already throws an exception, it should be void.
DEBUG("Connecting to " + m_url + " : " + m_port);
try
{
// If the socket is already connected, disconnect it before
// opening a new conneciont.
if (isConnected())
{
disconnect();
}
m_ioService = new boost::asio::io_service();
m_timeoutOpsTimer = new boost::asio::deadline_timer(*m_ioService);
m_context = new boost::asio::ssl::context(boost::asio::ssl::context::sslv23);
m_socket = new SSLSocket(*m_ioService, *m_context);
tcp::resolver resolver(*m_ioService);
tcp::resolver::query query(m_url, m_port);
tcp::resolver::iterator end;
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
boost::asio::connect(m_socket->lowest_layer(), resolver.resolve(query));
if (endpoint_iterator == end)
{
DEBUG("Endpoint cannot be resolved, disconnecting...");
disconnect();
}
else
{
m_timeoutOpsTimer->expires_at(boost::posix_time::pos_infin);
// Start the persistent actor that checks for deadline expiry.
check_deadline();
DEBUG("Endpoint resolved, performing handshake");
m_socket->set_verify_mode(boost::asio::ssl::verify_none);
m_socket->handshake(SSLSocket::client);
DEBUG("Handshake done, connected to " + m_url + " : " + m_port);
m_isConnected = true;
}
}
catch (boost::system::system_error &err)
{
disconnect();
throw;
}
return m_isConnected;
}
void CMDRboostConnection::disconnect()
{
try
{
DEBUG("Disconnecting...");
if(isConnected())
{
m_socket->shutdown();
DEBUG("Closing socket...");
m_socket->lowest_layer().close();
if(m_socket != NULL)
{
delete m_socket;
m_socket = NULL;
}
}
if(m_timeoutOpsTimer != NULL)
{
delete m_timeoutOpsTimer;
m_timeoutOpsTimer = NULL;
}
if(m_context != NULL)
{
delete m_context;
m_context = NULL;
}
if(m_ioService != NULL)
{
delete m_ioService;
m_ioService = NULL;
}
DEBUG("Disconnection performed properly");
m_isConnected = false;
}
catch (boost::system::system_error &err)
{
ERROR("Exception thrown, error = " << err.code() <<
", category: " << err.code().category().name() << std::endl);
if(m_timeoutOpsTimer != NULL)
{
delete m_timeoutOpsTimer;
m_timeoutOpsTimer = NULL;
}
if(m_context != NULL)
{
delete m_context;
m_context = NULL;
}
if(m_ioService != NULL)
{
delete m_ioService;
m_ioService = NULL;
}
m_isConnected = false;
throw;
}
}
As you can see, now the socket, the io_service, the deadline_timer and the context are created on connecting and released on disconnecting. I still do not understand what is going on, let me explain:
I have tried to reimplement the above variables one next one, that is, first the socket, then the timer, then the context and finally the io_service.
The tests have passed only when the io_service is a ptr, but I can't understand why. If io_service is a class-scoped variable, it should be deleted every time the class instance goes out of scope, that is, every time one of my TESTs finishes.
It seems that, before implementing it as a ptr, that was not happening. I suspect that maybe, when the readNBytes throws an exception due to a timeout, the read_async call remains in the io_service action queue, and maybe that caused the problem.
I'm a c# programmer and i need to implement a multicast socket in c++.
I've try to google search it and didnt find much help.
So if someone could give me some links to a good c++ multicast socket tutorial it will be highly appreciated.
My c# socket implementation looks like this:
public class UdpMulticast
{
private Socket s;
private Thread listenThread;
private string mcastGroup;
private int port;
public UdpMulticast(string mcastGroup, int port)
{
this.mcastGroup = mcastGroup;
this.port = port;
}
private void Listen()
{
while (true)
{
try
{
byte[] b = new byte[1024];
Thread.Sleep(1);
int recv = s.Receive(b);
if (OnNewDataRecv != null)
{
byte[] tmp = new byte[recv];
for (int i = 0; i < recv; i++)
{
tmp[i] = b[i];
}
byte[] decByte = Encryption.Decrypt(tmp);
if(this.OnNewDataRecv !=null)
this.OnNewDataRecv(decByte, decByte.Length);
}
if (s == null)
{
break;
}
}
catch (ThreadAbortException)
{
break;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
public delegate void newData4Send(byte[] data, int dataLen);
public event newData4Send OnNewDataRecv;
public bool StartListen()
{
bool ret = false;
try
{
if (s == null)
{
s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
}
if (s != null)
{
Console.WriteLine("PORT multi cast :" + port);
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, port);
s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
s.Bind(ipep);
IPAddress ip = IPAddress.Parse(mcastGroup);
s.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(ip, IPAddress.Any));
s.SetSocketOption(SocketOptionLevel.IP,
SocketOptionName.MulticastTimeToLive, int.Parse("1"));
listenThread = new Thread(new ThreadStart(Listen));
listenThread.IsBackground = true;
}
if (listenThread != null)
listenThread.Start();
ret = true;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
return ret;
}
public void StopListen()
{
if (listenThread != null)
{
if (listenThread.IsAlive)
{
listenThread.Abort();
listenThread = null;
}
}
if (s != null)
{
s.Close();
s = null;
}
}
public void Send(byte[] data, int len)
{
if (s == null)
{
s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
}
if (s != null)
{
IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(mcastGroup), port);
byte[] encByte = Encryption.Encrypt(data);
s.SendTo(encByte, encByte.Length, SocketFlags.None, ipep);
}
else Console.WriteLine("s is NULL");
}
}
I think i found something about it in wcf but i cant find a good tutorial.
There's no Socket class or sth. similar in plain C++. I suggest using a framework like Boost.Asio.
http://www.boost.org/doc/libs/1_46_1/doc/html/boost_asio/examples.html
There is a multicast example in the documentation.