Setting TTL on outgoing ICMP packets? - c++

I have been trying to set the TTL on ICMP packets using the
boost::asio::ip::unicast::hops option (using Boost 1.43) and then reading it out with get_option.
get_option gets 1 regardless what I use in set_option. And when inspecting the
packets sent using wireshark, the TTL is 128. Am I missing something here?
Should I use another option to set the TTL? Is it at all possible through Asio?
Regards,
Peter
Update 2010-08-01 17:37 UTC: Here is the code I am using:
#include <sstream>
#include <stdexcept>
#include <boost/asio.hpp>
class MyClass: public boost::noncopyable
{
public:
MyClass(const char* host):
io(),
resolver(io),
query( boost::asio::ip::icmp::v4(), host, "" ),
socket(io, boost::asio::ip::icmp::v4())
{
destination = *resolver.resolve(query);
}
~MyClass()
{
socket.close();
}
void run()
{
const int ttl = 2;
// set TTL ?
const boost::asio::ip::unicast::hops option( ttl );
socket.set_option(option);
boost::asio::ip::unicast::hops op;
socket.get_option(op);
if( op.value() != ttl )
{
std::ostringstream o;
o << "TTL not set properly. Should be " << ttl << " but was set"
" to " << op.value() << '.';
throw std::runtime_error( o.str() );
}
}
private:
boost::asio::io_service io;
boost::asio::ip::icmp::resolver resolver;
boost::asio::ip::icmp::resolver::query query;
boost::asio::ip::icmp::socket socket ;
boost::asio::ip::icmp::endpoint destination;
};
#include <iostream>
int main( int argc, char** argv)
{
try
{
if( argc != 2 )
{
throw std::invalid_argument("Missing argument. First argument = host");
}
MyClass T( argv[1] );
T.run();
}
catch( const std::exception& e )
{
std::cerr << "Exception: " << e.what() << '\n';
}
}
From this I get:
"Exception: TTL not set properly. Should be 2 but was set to 1."

Linux platform? Based on the documentation you appear to be doing it correctly.

Related

POST request body is empty (cpp-netlib 0.13.0)

I have upgraded cpp-netlib from v0.11.0 to 0.13.0 and run into some difficulties.
Previously, when a request was sent to the server, the body of the request could be read from the request object.
The request body is now empty when I send the same request to a server using v0.13.0.
The rest of the request object appears to be correct - only the body is empty.
Is there something I need to do differently? I can't find any examples on the site that show how the body is extracted.
I have confirmed the same behaviour from the hello world example.
#include <boost/network/protocol/http/server.hpp>
#include <iostream>
namespace http = boost::network::http;
struct hello_world;
typedef http::server<hello_world> server;
struct hello_world
{
void operator()(const server::request &request, server::connection_ptr connection)
{
///////////////////////////////////
// request.body is empty
///////////////////////////////////
server::string_type ip = source(request);
unsigned int port = request.source_port;
std::ostringstream data;
data << "Hello, " << ip << ':' << port << '!';
connection->set_status(server::connection::ok);
connection->write(data.str());
}
};
int main(int argc, char *argv[]) {
try {
hello_world handler;
server::options options(handler);
server server_(options.address("192.168.0.19").port("9999"));
server_.run();
}
catch (std::exception &e) {
std::cerr << e.what() << std::endl;
return 1;
}
return 0;
}
Here is the request I'm sending:
curl -v -X POST http://192.168.0.19:9999/my-app/rest/foo/1.0/bar -H 'Content-Type: application/x-www-form-urlencoded' --data key=value
In older versions of cpp-netlib you could choose between a sync_server and a async_server class. Since version 0.12 only the async_server class is available. This class does not read body data of a POST request into request.body automatically, but requires the user to read the data in an asynchronous way using connection->read(callback).
Long story short, I've compiled a minimal echo server example that shows how to do this correctly. It also explains how to deal with the not well known Expect: 100-continue header that might be involved.
Please check out echo_async_server.cpp which has been added to the repo recently.
#include <vector>
#include <boost/config/warning_disable.hpp>
#include <boost/network/include/http/server.hpp>
#include <boost/network/utils/thread_pool.hpp>
#include <boost/range/algorithm/find_if.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
namespace net = boost::network;
namespace http = boost::network::http;
namespace utils = boost::network::utils;
struct async_hello_world;
typedef http::async_server<async_hello_world> server;
struct connection_handler : boost::enable_shared_from_this<connection_handler> {
connection_handler(server::request const &request)
:req(request), body("") {}
~connection_handler() {
std::cout << "connection_handler dctor called!" << std::endl;
}
void operator()(server::connection_ptr conn) {
int cl;
server::request::headers_container_type const &hs = req.headers;
for(server::request::headers_container_type::const_iterator it = hs.begin(); it!=hs.end(); ++it) {
if(boost::to_lower_copy(it->name)=="content-length") {
cl = boost::lexical_cast<int>(it->value);
break;
}
}
read_chunk(cl, conn);
}
void read_chunk(size_t left2read, server::connection_ptr conn) {
std::cout << "left2read: " << left2read << std::endl;
conn->read(
boost::bind(
&connection_handler::handle_post_read,
connection_handler::shared_from_this(),
_1, _2, _3, conn, left2read
)
);
}
void handle_post_read(
server::connection::input_range range, boost::system::error_code error, size_t size, server::connection_ptr conn, size_t left2read) {
if(!error) {
std::cout << "read size: " << size << std::endl;
body.append(boost::begin(range), size);
size_t left = left2read - size;
if(left>0) {
read_chunk(left, conn);
} else {
//std::cout << "FINISHED at " << body.size()<< std::endl;
}
}
std::cout << "error: " << error.message() << std::endl;
}
void handle_post_request(server::connection_ptr conn)
{
std::cout << "handle request..." << std::endl;
std::cout << "post size: " << body.size() << std::endl;
}
server::request const &req;
std::string body;
};
struct async_hello_world {
void operator()(server::request const &request, server::connection_ptr conn) {
boost::shared_ptr<connection_handler> h(new connection_handler(request));
(*h)(conn);
}
void error(boost::system::error_code const & ec) {
// do nothing here.
std::cout << "async error: " << ec.message() << std::endl;
}
};
int main(int argc, char * argv[]) {
utils::thread_pool thread_pool(4);
async_hello_world handler;
server instance("0.0.0.0", "1935", handler, thread_pool);
instance.run();
return 0;
}
You need to read manually the body. Now a connection_ptr object is used and a handler must be attached for doing the read.
So it must look something like this:
if (r.method == "POST") {
auto foundIt = std::find_if(r.headers.begin(), r.headers.end(),
[](auto const & h) { return h.name == "Content-Length"; });
if (foundIt == r.headers.end())
throw std::logic_error("No Content-Length header found in POST");
auto handleReadFunc = [this](auto &&... args) {
this->handleReadBody(args...);
};
//This attaches a callback to read the body
connection->read(handleReadFunc);
}

How can I run miltiple ServerApplications with POCO C++?

I've started learning POCO C++ library and I'm stuck while trying to run 2 servers in the same application (so that they can use some common runtime variables). These are 2 different servers, one of them is TCP TimeServer and the other one is simple UDP EchoServer. The code:
#include "Poco/Net/TCPServer.h"
#include "Poco/Net/TCPServerConnection.h"
#include "Poco/Net/TCPServerConnectionFactory.h"
#include "Poco/Net/TCPServerParams.h"
#include "Poco/Net/StreamSocket.h"
#include "Poco/Net/ServerSocket.h"
#include "Poco/Net/DatagramSocket.h"
#include "Poco/Timestamp.h"
#include "Poco/DateTimeFormatter.h"
#include "Poco/DateTimeFormat.h"
#include "Poco/Exception.h"
#include "Poco/Util/ServerApplication.h"
#include "Poco/Util/Option.h"
#include "Poco/Util/OptionSet.h"
#include "Poco/Util/HelpFormatter.h"
#include <iostream>
using Poco::Net::ServerSocket;
using Poco::Net::StreamSocket;
using Poco::Net::TCPServerConnection;
using Poco::Net::TCPServerConnectionFactory;
using Poco::Net::TCPServer;
using Poco::Timestamp;
using Poco::DateTimeFormatter;
using Poco::DateTimeFormat;
using Poco::Util::ServerApplication;
using Poco::Util::Application;
using Poco::Util::Option;
using Poco::Util::OptionSet;
using Poco::Util::HelpFormatter;
class TimeServerConnection : public TCPServerConnection
{
public:
TimeServerConnection(const StreamSocket& s, const std::string& format) :
TCPServerConnection(s),
_format(format)
{
}
void run()
{
Application& app = Application::instance();
bool isOpen = true;
Poco::Timespan timeOut(10, 0);
unsigned char incommingBuffer[1000];
app.logger().information("SYSLOG from " + this->socket().peerAddress().toString());
while (isOpen) {
if (socket().poll(timeOut, Poco::Net::Socket::SELECT_READ) == false) {
std::cout << "TIMEOUT!" << std::endl << std::flush;
} else {
int nBytes = -1;
try {
nBytes = socket().receiveBytes(incommingBuffer, sizeof(incommingBuffer));
std::cout << incommingBuffer << std::endl;
} catch (Poco::Exception& exc) {
std::cerr << "Network error: " << exc.displayText() << std::endl;
isOpen = false;
}
if (nBytes == 0) {
std::cout << "Client closes connection!" << std::endl << std::flush;
isOpen = false;
} else {
std::cout << "Receiving nBytes: " << nBytes << std::endl << std::flush;
}
}
}
try
{
Timestamp now;
std::string dt(DateTimeFormatter::format(now, _format));
dt.append("\r\n");
socket().sendBytes(dt.data(), (int)dt.length());
}
catch (Poco::Exception& exc)
{ app.logger().log(exc); }
}
private:
std::string _format;
};
class TimeServerConnectionFactory : public TCPServerConnectionFactory
{
public:
TimeServerConnectionFactory(const std::string& format) :
_format(format)
{
}
TCPServerConnection* createConnection(const StreamSocket& socket)
{ return new TimeServerConnection(socket, _format); }
private:
std::string _format;
};
class UDPServer : public Poco::Util::ServerApplication
{
public:
UDPServer(){}
~UDPServer(){}
protected:
void initialize(Application& self)
{
loadConfiguration(); // load default configuration files, if present
ServerApplication::initialize(self);
}
void uninitialize() { ServerApplication::uninitialize(); }
int main(const std::vector<std::string>& args)
{
unsigned short port = (unsigned short)config().getInt("udpport", 9002);
std::cout << "[UDP] Using port " << port << std::endl;
std::string format(config().getString("TimeServer.format", DateTimeFormat::ISO8601_FORMAT));
Poco::Net::SocketAddress socketaddress(Poco::Net::IPAddress(), 9001);
Poco::Net::DatagramSocket datagramsocket(socketaddress);
char buffer[1024]; // 1K byte
while (1) {
Poco::Net::SocketAddress sender;
int n = datagramsocket.receiveFrom(buffer, sizeof(buffer) - 1, sender);
buffer[n] = '\0';
std::cout << sender.toString() << ":" << buffer << std::endl;
}
return 0;
}
};
class TimeServer : public Poco::Util::ServerApplication
{
public:
TimeServer() : _helpRequested(false)
{
}
~TimeServer()
{
}
protected:
void initialize(Application& self)
{
loadConfiguration(); // load default configuration files, if present
ServerApplication::initialize(self);
}
void uninitialize()
{
ServerApplication::uninitialize();
}
void defineOptions(OptionSet& options)
{
ServerApplication::defineOptions(options);
options.addOption(
Option("help", "h", "display help information on command line arguments")
.required(false)
.repeatable(false));
}
void handleOption(const std::string& name, const std::string& value)
{
ServerApplication::handleOption(name, value);
if (name == "help")
_helpRequested = true;
}
void displayHelp()
{
HelpFormatter helpFormatter(options());
helpFormatter.setCommand(commandName());
helpFormatter.setUsage("OPTIONS");
helpFormatter.setHeader("A server application that serves the current date and time.");
helpFormatter.format(std::cout);
}
int main(const std::vector<std::string>& args)
{
if (_helpRequested)
{
displayHelp();
}
else
{
unsigned short port = (unsigned short)config().getInt("tcpport", 9911);
std::cout << "Using port " << port << std::endl;
std::string format(config().getString("TimeServer.format", DateTimeFormat::ISO8601_FORMAT));
ServerSocket svs(port);
TCPServer srv(new TimeServerConnectionFactory(format), svs);
srv.start();
std::cout << "Server started!\n";
waitForTerminationRequest();
srv.stop();
std::cout << "Server stopped!\n";
}
return Application::EXIT_OK;
}
private:
bool _helpRequested;
};
int main(int argc, char** argv)
{
TimeServer app;
UDPServer app2;
app.run(argc, argv);
app2.run(argc, argv);
}
In the end of code I have int main() method where I'm trying to run 2 servers. However I get assertion violation here. There is a similar question on StackOverflow, however boost is used there while I'm using plain C++, so that solution is not relevant to me.
How can I run simultaneously these 2 servers?
ServerApplication was not designed for multiple instances. What you should do is run one ServerApplication and launch TCPServer and UDPServer in that application.
Actually if you want to made like this (as you question), seperated both
tcp (a) class and
udp (b) class.
Call both in other class (c) and
define which one
(c) -> (a)
(c) -> (b)
u need to call first and when. So u need make condition and decision.
Note: give them space time before run to made poco breath. 😂

Getting a copy of any received ICMP packet with boost asio

My goal is to write a little program in c++ with boost asio that notifies me whenever an ICMP packet is received on any network interface.
The program doesn't print any errors and there are no exceptions.
But it also doesn't receive any ICMP packets sent by any program but one (the ping example of boost asio, which can be found here).
What's even stranger is the fact that the ICMP echo request packet from the boost example (after adjusting the payload and the ICMP identifier accordingly) and the default windows ICMP echo request (when using "ping" in the windows commmand line) look almost exactly the same in wireshark. The only difference beeing the identification field in the IPv4 header and thus also the ICMP checksum.
The same behaviour can be observed no matter where the echo request comes from, be it a virtual machine or another real computer in the network.
I can ping all those machines without any issues.
Disabling the windows firewall yields the same result.
OS: Windows 10 64 bit Enterprise N (10.0.10586 Build 10586)
Boost version: 1.62.0.
IDE: Microsoft Visual Studio Community 15.
Here is what I came up with:
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/icmp.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/placeholders.hpp>
#include <boost/system/error_code.hpp>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <iostream>
class ICMPReceiver {
public:
ICMPReceiver( boost::asio::io_service & IOS ):
m_sock( IOS ),
m_localEP( boost::asio::ip::icmp::v4(), 0 )
{
}
bool open() {
boost::system::error_code ec;
if ( !m_sock.is_open() ) {
m_sock.open( boost::asio::ip::icmp::v4(), ec );
if ( ec ) {
std::cerr << "Error in socket.open():\n" << ec.message() << '\n';
return false;
}
}
return true;
}
bool bind() {
boost::system::error_code ec;
m_sock.bind( m_localEP, ec );
if ( ec ) {
std::cerr << "Error in socket.bind():\n" << ec.message() << '\n';
return false;
}
}
bool startReceiving() {
try {
m_sock.async_receive_from( boost::asio::buffer( m_receiveBuffer ),
m_remoteEP,
boost::bind( &ICMPReceiver::receiveHandle,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred ) );
} catch ( std::exception const & e ) {
std::cerr << "Exception in socket.async_receive_from():\n" << e.what() << '\n';
return false;
}
return true;
}
private:
void receiveHandle( boost::system::error_code const & ec, size_t bytes ) {
if ( ec ) {
if ( ec != boost::asio::error::operation_aborted ) {
std::cerr << "Error in receiveHandle():\n" << "Code: " << ec << ": " << ec.message() << '\n';
return;
} else {
std::cerr << "operation aborted\n";
return;
}
} else {
std::cout << "ICMP packet received\n";
}
startReceiving();
}
boost::asio::ip::icmp::socket m_sock;
boost::asio::ip::icmp::endpoint m_localEP;
boost::asio::ip::icmp::endpoint m_remoteEP;
boost::array< char, 2048 > m_receiveBuffer;
};
int main() {
try {
boost::asio::io_service IOS;
ICMPReceiver receiver( IOS );
receiver.open();
receiver.bind();
receiver.startReceiving();
IOS.run();
return 0;
} catch ( std::exception const & e ) {
std::cerr << "Unhandled exception: " << e.what() << '\n';
return 1;
}
}

HOW-TO: Client connection manager for Boost::asio?

I created a server using boost:asio. When a client connects it sends a file_size, file_name and the file_data. The server stores this in a file on disk. This works perfectly! Though now I'm running both client application and server application in the main thread of their application (so I've got a server and client app) which blocks the rest of the application(s) from executing.
So in abstract I want to create something like this:
server app
have one thread to receive and handle all incoming file transfers
have another thread in which the rest of the application can do the things it want to
client app
when I press the space bar, or whenever i want, I want to send a file to the server in a separate thread from the main one so my application can continue doing other stuff it needs to do.
My question: how do I create a manager for my client file transfers?
File transfer server accepts new file transfer client connections
#include "ofxFileTransferServer.h"
ofxFileTransferServer::ofxFileTransferServer(unsigned short nPort)
:acceptor(
io_service
,boost::asio::ip::tcp::endpoint(
boost::asio::ip::tcp::v4()
,nPort
)
,true
)
,port(nPort)
{
}
// test
void ofxFileTransferServer::startThread() {
boost::thread t(boost::bind(
&ofxFileTransferServer::accept
,this
));
}
void ofxFileTransferServer::accept() {
ofxFileTransferConnection::pointer new_connection(new ofxFileTransferConnection(io_service));
acceptor.async_accept(
new_connection->socket()
,boost::bind(
&ofxFileTransferServer::handleAccept
,this
,new_connection
,boost::asio::placeholders::error
)
);
std::cout << __FUNCTION__ << " start accepting " << std::endl;
io_service.run();
}
void ofxFileTransferServer::handleAccept(
ofxFileTransferConnection::pointer pConnection
,const boost::system::error_code& rErr
)
{
std::cout << __FUNCTION__ << " " << rErr << ", " << rErr.message() << std::endl;
if(!rErr) {
pConnection->start();
ofxFileTransferConnection::pointer new_connection(new ofxFileTransferConnection(io_service));
acceptor.async_accept(
new_connection->socket()
,boost::bind(
&ofxFileTransferServer::handleAccept
,this
,new_connection
,boost::asio::placeholders::error
)
);
}
}
File transfer client
#include "ofxFileTransferClient.h"
#include "ofMain.h"
using boost::asio::ip::tcp;
ofxFileTransferClient::ofxFileTransferClient(
boost::asio::io_service &rIOService
,const std::string sServer
,const std::string nPort
,const std::string sFilePath
):resolver_(rIOService)
,socket_(rIOService)
,file_path_(sFilePath)
,server_(sServer)
,port_(nPort)
{
}
ofxFileTransferClient::~ofxFileTransferClient() {
std::cout << "~~~~ ofxFileTransferClient" << std::endl;
}
void ofxFileTransferClient::start() {
// open file / get size
source_file_stream_.open(
ofToDataPath(file_path_).c_str()
,std::ios_base::binary | std::ios_base::ate
);
if(!source_file_stream_) {
std::cout << ">> failed to open:" << file_path_ << std::endl;
return;
}
size_t file_size = source_file_stream_.tellg();
source_file_stream_.seekg(0);
// send file size and name to server.
std::ostream request_stream(&request_);
request_stream << file_path_ << "\n"
<< file_size << "\n\n";
std::cout << ">> request_size:" << request_.size()
<< " file_path: " << file_path_
<< " file_size: "<< file_size
<< std::endl;
// resolve ofxFileTransferServer
tcp::resolver::query query(server_, port_);
resolver_.async_resolve(
query
,boost::bind(
&ofxFileTransferClient::handleResolve
,shared_from_this()
,boost::asio::placeholders::error
,boost::asio::placeholders::iterator
)
);
}
void ofxFileTransferClient::handleResolve(
const boost::system::error_code& rErr
,tcp::resolver::iterator oEndPointIt
)
{
if(!rErr) {
tcp::endpoint endpoint = *oEndPointIt;
socket_.async_connect(
endpoint
,boost::bind(
&ofxFileTransferClient::handleConnect
,shared_from_this()
,boost::asio::placeholders::error
,++oEndPointIt
)
);
}
else {
std::cout << ">> error: " << rErr.message() << std::endl;
}
}
void ofxFileTransferClient::handleConnect(
const boost::system::error_code& rErr
,tcp::resolver::iterator oEndPointIt
)
{
if(!rErr) {
cout << ">> connected!" << std::endl;
boost::asio::async_write(
socket_
,request_
,boost::bind(
&ofxFileTransferClient::handleFileWrite
,shared_from_this()
,boost::asio::placeholders::error
)
);
}
else if (oEndPointIt != tcp::resolver::iterator()) {
// connection failed, try next endpoint in list
socket_.close();
tcp::endpoint endpoint = *oEndPointIt;
socket_.async_connect(
endpoint
,boost::bind(
&ofxFileTransferClient::handleConnect
,shared_from_this()
,boost::asio::placeholders::error
,++oEndPointIt
)
);
}
else {
std::cout << ">> error: " << rErr.message() << std::endl;
}
}
void ofxFileTransferClient::handleFileWrite(
const boost::system::error_code& rErr
)
{
if(!rErr) {
if(source_file_stream_.eof() == false) {
source_file_stream_.read(buf_.c_array(), buf_.size());
if(source_file_stream_.gcount() <= 0) {
std::cout << ">> read file error." << std::endl;
return;
}
std::cout << ">> send: " << source_file_stream_.gcount() << " bytes, total: " << source_file_stream_.tellg() << " bytes\n";
boost::asio::async_write(
socket_
,boost::asio::buffer(buf_.c_array(), source_file_stream_.gcount())
,boost::bind(
&ofxFileTransferClient::handleFileWrite
,this
,boost::asio::placeholders::error
)
);
if(rErr) {
std::cout <<">> send error: " << rErr << std::endl; // not sure bout this one..
}
}
else {
return; // eof()
}
}
else {
std::cout << ">> error:" << rErr.message() << std::endl;
}
}
And a tiny manager to manager client transfers (which is used in the client app)
Again the threading code is only for testing purposes and isnt used.
#include "ofxFileTransferManager.h"
ofxFileTransferManager::ofxFileTransferManager() {
}
void ofxFileTransferManager::transferFile(
const std::string sServer
,const std::string nPort
,const std::string sFile
)
{
ofxFileTransferClient::pointer client(new ofxFileTransferClient(
io_service_
,sServer
,nPort
,sFile
));
client->start();
io_service_.run();
}
void ofxFileTransferManager::startThread() {
boost::thread t(boost::bind(
&ofxFileTransferManager::run
,this
));
}
void ofxFileTransferManager::run() {
cout << "starting filemanager" << std::endl;
while(true) {
io_service_.run();
boost::this_thread::sleep(boost::posix_time::milliseconds(250));
cout << ".";
}
cout << "ready filemanager" << std::endl;
}
It would be awesome if someone can help me out here. The example of boost all use a "one-time" client connection which doesn't really help me further.
roxlu
Great! I just figured it out. I had to wrap my io_service around a boost::asio::io_service::work object! (and forgot a shared_from_this()) somewhere. I've uploaded my code here: http://github.com/roxlu/ofxFileTransfer
For convenience here is the manager code:
#include "ofxFileTransferManager.h"
ofxFileTransferManager::ofxFileTransferManager()
:work_(io_service_)
{
}
void ofxFileTransferManager::transferFile(
const std::string sServer
,const std::string nPort
,const std::string sFile
,const std::string sRemoteFile
)
{
ofxFileTransferClient::pointer client(new ofxFileTransferClient(
io_service_
,sServer
,nPort
,sFile
,sRemoteFile
));
client->start();
}
void ofxFileTransferManager::startThread() {
boost::thread t(boost::bind(
&ofxFileTransferManager::run
,this
));
}
void ofxFileTransferManager::run() {
io_service_.run();
}
From what I can tell, all you really need is to create a new thread and put in its main loop io_service.run();.
Obviously, you would have to take care of protecting classes and variables in mutexes that are shared between the appss main thread and asio's thread.
Edit: Something like this?
static sem_t __semSendFile;
static void* asioThread(void*)
{
while( true )
{
sem_wait( &__semSendFile );
io_service.run();
}
return NULL;
}
void ofxFileTransferManager::transferFile(
const std::string sServer
,const std::string nPort
,const std::string sFile
)
{
ofxFileTransferClient::pointer client(new ofxFileTransferClient(
io_service_
,sServer
,nPort
,sFile
));
client->start();
sem_post( &__semSendFile );
}
int main(int argc, char **argv)
{
if ( sem_init( &__semSendFile, 0, 0 ) != 0 )
{
std::cerr << strerror( errno ) << std::endl;
return -1;
}
pthread_t thread;
if ( pthread_create( &thread, NULL, asioThread, NULL ) != 0 )
{
std::cerr << strerror( errno ) << std::endl;
return -1;
}
[...]

c++, boost: how to fill buffer and transfer (image) file data over network?

I'm working on an application where I need to send a "filename", "filesize" and the filedata over the network. I created a server using boost which, for now, reads in the filesize and name.
I'm wondering how I can fill a buffer with the file data (if necessary) and how to transfer it to the server.
This is what I've got now:
#ifndef OFXFILETRANSFERSENDH
#define OFXFILETRANSFERSENDH
#undef check // necessary to get Boost running on Mac
#include <vector>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include "ofxFileTransferConnection.h"
using boost::asio::ip::tcp;
class ofxFileTransferSend : public boost::enable_shared_from_this<connection> {
public:
typedef boost::shared_ptr<ofxFileTransferSend> pointer;
static pointer create(
boost::asio::io_service& rIOService
,std::string sServer
,const char* sPort)
{
return pointer(new ofxFileTransferSend(rIOService,sServer, sPort));
}
private:
//--------------------------------------------------------------
ofxFileTransferSend(
boost::asio::io_service &rIOService
,const std::string sServer
,const char* sPort
)
:port(sPort)
,socket_(rIOService)
,resolver_(rIOService)
{
tcp::resolver::query query(sServer, sPort);
resolver_.async_resolve(
query
,boost::bind(
&ofxFileTransferSend::handleResolve
,this
,boost::asio::placeholders::error
,boost::asio::placeholders::iterator
)
);
}
//--------------------------------------------------------------
void handleResolve(
const boost::system::error_code &rError
,tcp::resolver::iterator oEndPointIterator
)
{
if (!rError) {
tcp::endpoint end_point = *oEndPointIterator;
socket_.async_connect(
end_point
,boost::bind(
&ofxFileTransferSend::handleConnect
,this
,boost::asio::placeholders::error
,++oEndPointIterator
)
);
}
else {
std::cout << "Error while resolving server: " << std::endl;
}
}
//--------------------------------------------------------------
void handleConnect(
const boost::system::error_code &rError
,tcp::resolver::iterator rOEndPointIterator
)
{
if(!rError) {
std::cout << "Connected to remote server!" << std::endl;
std::size_t size = 1235;
std::ostream send_data_stream(&send_data);
send_data_stream << "filename.jpg" << "\r\n";
send_data_stream << size << "\r\n";
boost::asio::async_write(
socket_
,send_data
,boost::bind(
&ofxFileTransferSend::handleSendFileInfo
,this
,boost::asio::placeholders::error
)
);
}
else {
// #todo on failure retry!
std::cout << "Error connecting to ofxFileTransferServer:" << rError.message()<< std::endl;
}
}
//--------------------------------------------------------------
void handleSendFileInfo(
const boost::system::error_code &rError
)
{
if(!rError) {
cout << "okay nice, send file data done!\n";
}
else {
std::cout << "Error sending file info: " << rError.message() << std::endl;
}
}
tcp::resolver resolver_;
tcp::socket socket_;
boost::asio::streambuf send_data;
const char* port;
};
#endif
How about just dumping the file on the wire? That's how HTTP does it. In fact, I see you are using almost the same protocol as HTTP. Send all metadata as clear text first (name, size, etc), put in an empty line as a terminator for the metadata (\r\n) and now all you need is to dump the file itself:
void handleSendFileInfo(
const boost::system::error_code &rError
)
{
if(!rError) {
std::ofstream fileData(fileName);
boost::asio::async_write(
socket_
,fileData
,boost::bind(
&ofxFileTransferSend::handleSendFileData
,this
,boost::asio::placeholders::error
)
);
}
else {
std::cout << "Error sending file info: " << rError.message() << std::endl;
}
}