I've made a simple module for transferring data. Most of my code is based on code I found on SO
When I run the instances on my local machine, everything works fine. But if I try running this over a local area network, I get the error "Can't Assign Requested Address".
Note: My "instances basically involve running ./server 1 0 and ./server 1 1 so, they;re waiting for data. And then ./server 0 to send it over.
Here's the code
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <iostream>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/asio.hpp>
static std::string const server_ip = "10.0.0.4";
static std::string const client_ip = "10.0.0.5";
using std::cout;
using std::endl;
struct Location {
double rent[6];
double cost[7];
std::string name, group;
std::string locationOfObjectFile;
int locationNo;
template <typename Ar> void serialize(Ar &ar, unsigned) {
ar &rent;
ar &cost;
ar &name;
ar &group;
ar &locationOfObjectFile;
ar &locationNo;
}
};
struct Player {
int currentPosition;
double currentMoney;
template <typename Ar> void serialize(Ar &ar, unsigned) {
ar ¤tPosition;
ar ¤tMoney;
}
};
struct Monopoly {
std::vector<Location> locations;
std::vector<Player> players;
std::string currency;
template <typename Ar> void serialize(Ar &ar, unsigned) {
ar &locations;
ar &players;
ar ¤cy;
}
};
Location l1;
Player p1;
Monopoly game, game2;
void readData(int x) {
boost::asio::io_service io_service;
uint16_t port = x;
boost::asio::ip::tcp::acceptor acceptor(
io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(server_ip), port));
/* code */
boost::asio::ip::tcp::socket socket(io_service);
acceptor.accept(socket);
std::cout << "connection from " << socket.remote_endpoint() << std::endl;
// read header
size_t header;
boost::asio::read(socket, boost::asio::buffer(&header, sizeof(header)));
std::cout << "body is " << header << " bytes" << std::endl;
// read body
boost::asio::streambuf buf;
const size_t rc = boost::asio::read(socket, buf.prepare(header));
buf.commit(header);
std::cout << "read " << rc << " bytes" << std::endl;
// deserialize
std::istream is(&buf);
boost::archive::text_iarchive ar(is);
ar &game2;
cout << game2.locations[0].rent[1] << endl;
cout << game2.players[0].currentPosition << "how cool is this?";
socket.close();
}
void sendData() {
for (int i = 0; i <= 1; i++) {
boost::asio::streambuf buf;
std::ostream os(&buf);
boost::archive::text_oarchive ar(os);
ar &game;
boost::asio::io_service io_service;
boost::asio::ip::tcp::socket socket(io_service);
short port = i + 1234;
socket.connect(boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(client_ip), port));
const size_t header = buf.size();
std::cout << "buffer size " << header << " bytes" << std::endl;
// send header and buffer using scatter
std::vector<boost::asio::const_buffer> buffers;
buffers.push_back(boost::asio::buffer(&header, sizeof(header)));
buffers.push_back(buf.data());
const size_t rc = boost::asio::write(socket, buffers);
std::cout << "wrote " << rc << " bytes" << std::endl;
;
socket.close();
}
}
int main(int argc, char **argv) {
l1.name = "soemthig";
l1.group = 2;
p1.currentMoney = 300;
p1.currentPosition = 422;
for (int i = 0; i < 7; ++i) {
l1.cost[i] = i;
/* code */
}
for (int i = 0; i < 6; ++i) {
l1.rent[i] = 2 * i;
/* code */
}
l1.locationOfObjectFile = "ajhsdk/asdc.obj";
l1.locationNo = 5;
game.locations.push_back(l1);
game.players.push_back(p1);
game.currency = "dollar";
cout << game.currency;
if (atoi(argv[1]) ==
1) // argv[0]=0 implies server, argv[0]==1 implies client while argv[1] specifies 1st or second client
{
cout << "reading data";
if (atoi(argv[2]) == 0) {
readData(1234);
/* code */
} else {
readData(1235);
}
} else {
cout << "writing data";
sendData();
}
}
Here is the error message stack trace:
libc++abi.dylib: terminating with uncaught exception of type boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> >: bind: Can't assign requested address dollarreading dataAbort trap: 6 and 10.0.0.4 is the IP address of the computer that is supposed to send the data.
You get this when the connection is refused.
This, in turn, could happen when the client is not listening on the required interface.
Make sure that the IP addresses are actually the public IP addresses on the network and the machines can reach eachother. E.g. use netstat -tlpn (or similar on your OS) to ascertain that the clients are listening:
tcp 0 0 192.168.2.136:1234 0.0.0.0:* LISTEN 18186/valgrind.bin
tcp 0 0 192.168.2.136:1235 0.0.0.0:* LISTEN 18187/valgrind.bin
Now, try to connect using e.g. netcat from the server machine:
netcat 192.168.2.136 1234
This will probably crash the client, but it will also tell you whether connecting was possible.
If not, then either the address is not reachable, the client is not listening on the right interface, the firewall is filtering your traffic etc.
PS. I've made your code self contained and running live on Coliru. Please do this; making your code selfcontained make it possible for people to actually fix things
Related
Able to send UDP message to a particular IP port using Poco Lib socket communication, But unable to receive the UDP message as it is getting stuck at receiveFrom API of DatagramSocket as in below code.
I am sending message every second and also have to receive acknowledgement every second, for that i have timer , Client and Server Threads running parallelly. The problem here is I am unable to receive the UDP packets which are being captured on wireshark. It is getting stuck at receiveFrom.
Please find below Client Server and main files.
` Server.hpp
#pragma once
#include "Poco/Net/StreamSocket.h"
#include "Poco/Net/DatagramSocket.h"
#include "Poco/Net/SocketAddress.h"
#include "Poco/Net/MulticastSocket.h"
#include "Poco/RunnableAdapter.h"
#include "Poco/Thread.h"
#include <cstring>
#include <iostream>
using namespace std;
using namespace Poco;
using namespace Poco::Net;
struct Server
{
int bufferSize;
SocketAddress sockets;
static bool debugModeEnabled;
Server() :
bufferSize(1024) { //sockets = SocketAddress(10000);
}
Server(const UInt16& port, const int& bufferSize)
{
sockets = SocketAddress(port);
this->bufferSize = bufferSize;
}
void receiveMessages()
{
char buffer[bufferSize];
try
{
Poco::Net::DatagramSocket datagram(sockets);//(socket);
datagram.bind(sockets);
cout << "Server started socket" << endl;
while (!datagram.available())
{
SocketAddress sender;
cout << "Server started socket 2" << endl;
int size = datagram.receiveFrom(buffer, bufferSize, sender);
//int size = datagram.receiveBytes(buffer, bufferSize);
cout << "received bytes size" << size << endl;
buffer[size] = '\0';
//std::string str(buffer);
//cout << (debugModeEnabled ? (sender.toString() + ": ") : "- ") << buffer << endl;
cout << "received: " << size << buffer << endl;
//cout << buffer << "Server adasdasd" << endl;
if (string(buffer) == "\\end")
{
//cerr << "\nUser: " << sender.toString() << " ended connection" << endl;
datagram.close(); // Closes the server
}
}
}
catch (const Poco::Exception& exc)
{
std::cerr << exc.displayText() << std::endl;
}
}
};
bool Server::debugModeEnabled = false;
`
`Client.hpp
#pragma once
#include "Poco/Net/DatagramSocket.h"
#include "Poco/Net/SocketAddress.h"
#include "Poco/RunnableAdapter.h"
#include "Poco/Thread.h"
#include <iostream>
#include <string>
using namespace std;
using namespace Poco;
using namespace Poco::Net;
struct Client
{
SocketAddress socket;
string str;
// By default the client connects to itself
Client() { socket = SocketAddress("127.0.0.1", 10000); }
Client(const Poco::Net::IPAddress& IP, const UInt16& port, const string& val) :
str(val)
{
socket = SocketAddress(IP, port);
}
void sendMessages()
{
DatagramSocket datagram;
datagram.connect(socket);
string message = str;
//cout << "sending: " << hex << hexify(message) << endl;
unsigned int bytes_sent = 0;
while (!datagram.available())
{
//getline(cin, message);
//bytes_sent = datagram.sendBytes(message.data(), static_cast<int>(message.size()));
bytes_sent = datagram.sendTo(message.data(), static_cast<int>(message.size()),socket);
cout << "number of bytes sent: " << std::dec << bytes_sent << endl;
if (bytes_sent >= message.size())
{
datagram.close();
}
}
}
string IP() { return socket.host().toString(); }
UInt16 port() { return socket.port(); }
static void sendMessage(const Poco::Net::IPAddress& IP, const UInt16& port, const string& message)
{
SocketAddress socket(IP, port);
DatagramSocket datagram;
datagram.connect(socket);
datagram.sendBytes(message.data(), int(message.size()));
}
};
`
` main.cpp
int bufferSize = 1024;
int exit_status = 0;
Client client(IP, ciPort, str);
Server server(mdilPort, bufferSize);
RunnableAdapter<Client> clientRunnable(client, &Client::sendMessages);
RunnableAdapter<Server> serverRunnable(server, &Server::receiveMessages);
Thread clientThread, serverThread;
// Client::sendMessage(IP, ciPort, "hello!!");
try
{
Timer t = Timer();
t.setInterval([&]() {
cout << "client Tick" << endl;
// pApp->SendIndications();
clientThread.start(clientRunnable);
clientThread.join();
},
1000);
t.setInterval([&]() {
cout<< "server Tick" << endl;
serverThread.start(serverRunnable);
serverThread.join();
},
1000);
t.setTimeout([&]() {
std::cout << "Hey.. After 30s. But I will stop the timer!" << std::endl;
t.stop();
exit(exit_status);
},
30000);
std::cout << "I am Timer" << std::endl;
while (true); // Keep main thread active
}
catch (...)
{
std::cout << "catched exception" << std::endl;
//return -1;
}
`
I tried the conventional Socket Programming API's to receive the UDP packets but there also it is getting stuck at receiveFrom API. also tried running both client and server on different process to make sure there is no issue with the multi threading synchronization, but both the approach didnt help. I am able to capture the response at Wireshark but unable to receive on the application side using Poco Lib socket API's. Also allowed visual studio code through firewall as well
I am using boost asio for a client server application and ran into this problem, with this not so informative error message(at least to me ;)), I am sending structs as messages to and fro, the sending works perfectly well from the client side, but almost similar attempt from the server's side causes this problem(rather error) : Send failed: Bad file descriptor
here's a snippet of the sending part ( please ask for any other details required in the comments):
void read_from_ts(const char* buf, int len) { // this is the read callback function
if (len <= 0) {
std::cerr << "Error: Connection closed by peer. " << __FILE__ << ":" << __LINE__ << std::endl;
tcp_client_.close(tcp_connection_);
tcp_connection_ = nullptr;
ios_.stop(); // exit
return;
}
const UserLoginRequest *obj = reinterpret_cast<const UserLoginRequest *>(buf);
int tempId = obj->MessageHeaderIn.TemplateID;
Responses r;
switch(tempId)
{
case 10018: //login
const UserLoginRequest *obj = reinterpret_cast<const UserLoginRequest *>(buf);
//std::cout<<"Login request received"<<"\n";
boost::asio::ip::tcp::socket sock_(ios_);
r.login_ack(sock_);
/*will add more*/
}
std::cout << "RX: " << len << " bytes\n";
}
class Responses
{
public:
int login_ack(boost::asio::ip::tcp::socket& socket)
{
//std::cout<<"here"<<"\n";
UserLoginResponse info;
MessageHeaderOutComp mh;
ResponseHeaderComp rh;
rh.MsgSeqNum = 0; //no use for now
rh.RequestTime = 0; //not used at all
mh.BodyLen = 53; //no use
mh.TemplateID = 10019; // IMP
info.MessageHeaderOut = mh;
info.LastLoginTime = 0;
info.DaysLeftForPasswdExpiry = 10; //not used
info.GraceLoginsLeft = 10; //not used
rh.SendingTime = 0;
info.ResponseHeader = rh;
//Pad6 not used
async_write(socket, boost::asio::buffer(&info, sizeof(info)), on_send_completed);
}
static void on_send_completed(boost::system::error_code ec, size_t bytes_transferred) {
if (ec)
std::cout << "Send failed: " << ec.message() << "\n"; //**error shows up here**
else
std::cout << "Send succesful (" << bytes_transferred << " bytes)\n";
}
};
};
UPDATE Just noticed a third, trivial explanation when reading your code, see added bullet
Typically when the file-descriptor is closed elsewhere.
If you're using Asio, this typically means
the socket¹ object was destructed. This can be a beginner error when the code doesn't extend the lifetime of objects for the duration of asynchronous operations
the file-descriptor was passed to other code that closed it (e.g. using native_handle(https://www.boost.org/doc/libs/1_73_0/doc/html/boost_asio/reference/basic_stream_socket/native_handle.html) and the other code closed it (e.g. because it assumes ownership and did error-handling).
UPDATE Or, it can mean your socket was never initialized to begin. In your code I read:
//std::cout<<"Login request received"<<"\n";
boost::asio::ip::tcp::socket sock_(ios_);
r.login_ack(sock_);
However, that just constructs a new socket, never connects or binds it and tries to do login_ack on it. That won't work because login_ack doesn't bind nor connect the socket and invokes async_write on it.
Did you mean to use tcp_connection_.sock_ or similar?
In general closing file-descriptors in third-party code is an error in multi-threaded code because it invites race conditions which will lead to arbitrary stream corruption (see e.g. How do you gracefully select() on sockets that could be closed on another thread?)
You can use shutdown instead in the majority of cases
UNDEFINED BEHAVIORS
Also, note that
info doesn't have sufficient lifetime (it goes out of scope before the async_write would be completed
your login_ack never returns a value
Imagining Fixes
This is what I imagine surrounding code to look like, when removing the above problems.
In fact it could be significantly simpler due to the static nature of the response, but I didn't want to assume all responses would be that simple, so I went with shared-pointer lifetime:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/core/ignore_unused.hpp>
#include <iostream>
using boost::asio::ip::tcp;
struct MyProgram {
boost::asio::io_context ios_;
struct UserLoginRequest {
struct MessageHeaderInComp {
int TemplateID = 10018;
} MessageHeaderIn;
};
struct Connection {
tcp::socket sock_;
template <typename Executor>
Connection(Executor ex) : sock_{ex} {}
};
std::unique_ptr<Connection> tcp_connection_ = std::make_unique<Connection>(ios_.get_executor());
struct {
void close(std::unique_ptr<Connection> const&);
} tcp_client_;
struct Responses {
static auto login_ack() {
struct UserLoginResponse {
struct MessageHeaderOutComp {
int BodyLen = 53; // no use
int TemplateID = 10019; // IMP
} MessageHeaderOut;
int LastLoginTime = 0;
int DaysLeftForPasswdExpiry = 10; // not used
int GraceLoginsLeft = 10; // not used
struct ResponseHeaderComp {
int MsgSeqNum = 0; // no use for now
int RequestTime = 0; // not used at all
int SendingTime = 0;
} ResponseHeader;
};
return std::make_shared<UserLoginRequest>();
}
};
void read_from_ts(const char* buf, int len) { // this is the read callback function
if (len <= 0) {
std::cerr << "Error: Connection closed by peer. " << __FILE__ << ":" << __LINE__ << std::endl;
tcp_client_.close(tcp_connection_);
tcp_connection_ = nullptr;
ios_.stop(); // exit
return;
}
const UserLoginRequest *obj = reinterpret_cast<const UserLoginRequest *>(buf);
int tempId = obj->MessageHeaderIn.TemplateID;
switch(tempId) {
case 10018: //login
const UserLoginRequest *obj = reinterpret_cast<const UserLoginRequest *>(buf);
//std::cout<<"Login request received"<<"\n";
boost::asio::ip::tcp::socket sock_(ios_);
auto response = Responses::login_ack();
async_write(tcp_connection_->sock_, boost::asio::buffer(response.get(), sizeof(*response)),
[response](boost::system::error_code ec, size_t bytes_transferred) {
if (ec)
std::cout << "Send failed: " << ec.message() << "\n"; //**error shows up here**
else
std::cout << "Send succesful (" << bytes_transferred << " bytes)\n";
});
/*will add more*/
boost::ignore_unused(obj);
}
std::cout << "RX: " << len << " bytes\n";
}
};
int main() {
MyProgram p;
}
¹ (or acceptor/posix::strean_descriptor)
I am new to boost and networking ;). I am making a client server application with boost::asio, I need to pass structs as messages so used boost::asio::serialization for it :
test.h
#pragma once
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/serialization.hpp>
struct Test
{
public:
int a;
int b;
template<typename archive> void serialize(archive& ar, const unsigned version) {
ar & a;
ar & b;
}
};
client side sending:
void send_asynchronously(tcp::socket& socket) {
Test info;
info.a = 1;
info.b = 2;
{
std::ostream os(&buf);
boost::archive::binary_oarchive out_archive(os);
out_archive << info;
}
async_write(socket, buf, on_send_completed);
}
On the receiver side, I read the data into a boost::asio::buffer, I want to know a way to parse this buffer and extract the object on server side. Please help.
You don't show enough code to know how you declared buf or managed the lifetime.
I'm assuming you used boost::asio::streambuf buf; and it has static storage duration (namespace scope) or is a class member (but you didn't show a class).
Either way, whatever you have you can do "the same" in reverse to receive.
Here's a shortened version (that leaves out the async so we don't have to make guesses about the lifetimes of things like I mentioned above);
Connect
Let's connect to an imaginary server (we can make one below) at port 3001 on localhost:
asio::io_context ioc;
asio::streambuf buf;
tcp::socket s(ioc, tcp::v4());
s.connect({{}, 3001});
Serialize
Basically what you had:
{
std::ostream os(&buf);
boost::archive::binary_oarchive oa(os);
Test req {13,31};
oa << req;
}
Note the {} scope around the stream/archive make sure the archive is completed before sending.
Send
/*auto bytes_sent =*/ asio::write(s, buf);
Receive
Let's assume our server sends back another Test object serialized in the same way¹.
Reading into the buffer, assuming no framing we'll just "read until the end of the stream":
boost::system::error_code ec;
/*auto bytes_received =*/ asio::read(s, buf, ec);
if (ec && ec != asio::error::eof) {
std::cout << "Read error: " << ec.message() << "\n";
return 1;
}
In real life you want timeouts and limits to the amount of data read. Often your protocol will add framing where you know what amount of data to read or what boundary marker to expect.
Deserialize
Test response; // uninitialized
{
std::istream is(&buf);
boost::archive::binary_iarchive ia(is);
ia >> response;
}
Full Demo
Live On Coliru
#include <boost/asio.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/serialization.hpp>
#include <iostream>
namespace asio = boost::asio;
using tcp = boost::asio::ip::tcp;
struct Test {
int a,b;
template<typename Ar> void serialize(Ar& ar, unsigned) { ar & a & b; }
};
int main() {
asio::io_context ioc;
asio::streambuf buf;
tcp::socket s(ioc, tcp::v4());
s.connect({{}, 3001});
///////////////////
// send a "request"
///////////////////
{
std::ostream os(&buf);
boost::archive::binary_oarchive oa(os);
Test req {13,31};
oa << req;
}
/*auto bytes_sent =*/ asio::write(s, buf);
/////////////////////
// receive "response"
/////////////////////
boost::system::error_code ec;
/*auto bytes_received =*/ asio::read(s, buf, ec);
if (ec && ec != asio::error::eof) {
std::cout << "Read error: " << ec.message() << "\n";
return 1;
}
Test response; // uninitialized
{
std::istream is(&buf);
boost::archive::binary_iarchive ia(is);
ia >> response;
}
std::cout << "Response: {" << response.a << ", " << response.b << "}\n";
}
Using netcat to mock a server with a previously generated response Test{42,99} (base64 encoded here):
base64 -d <<<"FgAAAAAAAABzZXJpYWxpemF0aW9uOjphcmNoaXZlEgAECAQIAQAAAAAAAAAAKgAAAGMAAAA=" | nc -N -l -p 3001
It prints:
Response: {42, 99}
¹ on the same architecture and compiled with the same version of boost, because Boost's binary archives are not portable. The live demo is good demonstration of this
I have written a small test program that uses boost::asio::ip::tcp::iostream to transmit about 38 MiB of data:
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/asio.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include <chrono>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
using namespace std;
class Message {
public:
Message() {
}
virtual ~Message() {
}
string text;
std::vector<int> bigLoad;
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {
ar &text;
ar &bigLoad;
}
};
BOOST_CLASS_EXPORT(Message)
void runClient() {
// Give server time to startup
this_thread::sleep_for(chrono::milliseconds(3000));
boost::asio::ip::tcp::iostream stream("127.0.0.1", "3000");
// const boost::asio::ip::tcp::no_delay option(true);
// stream.rdbuf()->set_option(option);
Message message;
stringstream ss;
ss << "Hello World!";
message.text = ss.str();
int items = 10000000;
int size = sizeof(int) * items;
std::cout << "Size in Byte = " << size << endl;
std::cout << "Size in KiB = " << size / 1024 << endl;
std::cout << "Size in MiB = " << size / 1024 / 1024 << endl;
for (int i = 0; i < items; i++)
message.bigLoad.push_back(i);
boost::archive::text_oarchive archive(stream);
cout << "Client start to send message" << endl;
try {
archive << message;
} catch (std::exception &ex) {
cout << ex.what() << endl;
}
cout << "Client send message" << endl;
stream.close();
cout << "Client shutdown" << endl;
}
void handleIncommingClientConnection(boost::asio::ip::tcp::acceptor &acceptor) {
boost::asio::ip::tcp::iostream stream;
// const boost::asio::ip::tcp::no_delay option(true);
// stream.rdbuf()->set_option(option);
acceptor.accept(*stream.rdbuf());
boost::archive::text_iarchive archive(stream);
while (true) {
try {
Message message;
archive >> message;
cout << message.text << endl;
} catch (std::exception &ex) {
cout << ex.what() << endl;
if (stream.eof()) {
cout << "eof" << endl;
stream.close();
cout << "Server: shutdown client handling..." << endl;
break;
} else
throw ex;
}
}
}
void runServer() {
boost::asio::io_service ios;
boost::asio::ip::tcp::endpoint endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 3000);
boost::asio::ip::tcp::acceptor acceptor(ios, endpoint);
handleIncommingClientConnection(acceptor);
}
template <typename TimeT = std::chrono::milliseconds>
struct measure {
template <typename F, typename... Args>
static typename TimeT::rep execution(F &&func, Args &&... args) {
auto start = std::chrono::steady_clock::now();
std::forward<decltype(func)>(func)(std::forward<Args>(args)...);
auto duration = std::chrono::duration_cast<TimeT>(std::chrono::steady_clock::now() - start);
return duration.count();
}
};
void doIt() {
thread clientThread(runClient);
thread serverThread(runServer);
clientThread.join();
serverThread.join();
}
int main(int argc, char **argv) {
std::cout << measure<std::chrono::seconds>::execution(doIt) << std::endl;
return 0;
}
The output of the program in release mode looks like this:
Size in Byte = 40000000
Size in KiB = 39062
Size in MiB = 38
Client start to send message
Client send message
Client shutdown
Hello World!
input stream error
eof
Server: shutdown client handling...
148
It took 148 seconds (more than 2 minutes) to transfer 38 MB. I could copy the data to a USB stick and hand it over manually faster than boost::asio does.
Is there any way to improve bandwidth performance?
Your time likely is wasted in the serialization to/from text.
Dropping in binary archive does increase the speed from 80Mbit/s to 872MBit/s for me:
Client start to send message
Client send message
Client shutdown
Received: Hello World!
3
The total time in seconds is reduced to 3s, which happens to be the initial sleep :)
Proof Of Concept Live On Coliru
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/asio.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include <chrono>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
using namespace std;
class Message {
public:
Message() {}
virtual ~Message() {}
string text;
std::vector<int> bigLoad;
private:
friend class boost::serialization::access;
template <class Archive> void serialize(Archive &ar, const unsigned int /*version*/) {
ar & text & bigLoad;
}
};
BOOST_CLASS_EXPORT(Message)
void runClient() {
// Give server time to startup
this_thread::sleep_for(chrono::seconds(1));
boost::asio::ip::tcp::iostream stream("127.0.0.1", "3000");
const boost::asio::ip::tcp::no_delay option(false);
stream.rdbuf()->set_option(option);
Message message;
stringstream ss;
ss << "Hello World!";
message.text = ss.str();
int items = 8 << 20;
for (int i = 0; i < items; i++)
message.bigLoad.push_back(i);
boost::archive::binary_oarchive archive(stream);
cout << "Client start to send message" << endl;
try {
archive << message;
} catch (std::exception &ex) {
cout << ex.what() << endl;
}
cout << "Client send message" << endl;
stream.close();
cout << "Client shutdown" << endl;
}
void handleIncommingClientConnection(boost::asio::ip::tcp::acceptor &acceptor) {
boost::asio::ip::tcp::iostream stream;
// const boost::asio::ip::tcp::no_delay option(false);
// stream.rdbuf()->set_option(option);
acceptor.accept(*stream.rdbuf());
boost::archive::binary_iarchive archive(stream);
{
try {
Message message;
archive >> message;
cout << "Received: " << message.text << endl;
} catch (std::exception &ex) {
cout << ex.what() << endl;
if (stream.eof()) {
cout << "eof" << endl;
stream.close();
cout << "Server: shutdown client handling..." << endl;
return;
} else
throw;
}
}
}
void runServer() {
using namespace boost::asio;
using ip::tcp;
io_service ios;
tcp::endpoint endpoint = tcp::endpoint(tcp::v4(), 3000);
tcp::acceptor acceptor(ios, endpoint);
handleIncommingClientConnection(acceptor);
}
template <typename TimeT = std::chrono::milliseconds> struct measure {
template <typename F, typename... Args> static typename TimeT::rep execution(F &&func, Args &&... args) {
auto start = std::chrono::steady_clock::now();
std::forward<decltype(func)>(func)(std::forward<Args>(args)...);
auto duration = std::chrono::duration_cast<TimeT>(std::chrono::steady_clock::now() - start);
return duration.count();
}
};
void doIt() {
thread clientThread(runClient);
thread serverThread(runServer);
clientThread.join();
serverThread.join();
}
int main() { std::cout << measure<std::chrono::seconds>::execution(doIt) << std::endl; }
Caution:
One thing is "lost" here, that wasn't really supported with the old version of the code, either: receiving multiple archives directly head to head.
You might want to device some kind of framing protocol. See e.g.
Boost Serialization Binary Archive giving incorrect output
Outputting more things than a Polymorphic Text Archive
Streams Are Not Archives (http://www.boost.org/doc/libs/1_51_0/libs/serialization/doc/)
I've done a number of "overhead of Boost Serialization" posts on here:
how to do performance test using the boost library for a custom library
Boost C++ Serialization overhead
Later in my application I will have three threads: one thread that listen data on a port, one thread that send the data to an address on the same port. The last one is not important here.
For the moment, I don't use threads, I just want to do some tests.
My problem is the following :
If I just use the server to send and receive data, there is no problem. But when it is the client that send the data, it's like the distant peer did not receive anything.
My code for server :
#include "server.h"
using namespace boost::asio::ip;
using Peer = udp::endpoint;
Server::Server(boost::asio::io_service& io_service, Peer peer, Agent& agent) :
socket_(io_service, Peer(boost::asio::ip::udp::v6(), peer.port())),
recv_buffer_(), send_buffer_(), root_(peer), agent_(agent)
{
/* //when this is uncomment, that works fine
BOOST_LOG_TRIVIAL(info) << "Server Initialisation:";
std::shared_ptr<PeersReq> peersreq(new PeersReq());
std::vector<std::shared_ptr<Tlv>> tlv_vector;
tlv_vector.push_back(peersreq);
Packet packet(tlv_vector);
write_log(packet, peer);
boost::asio::streambuf request;
std::ostream os(&request);
packet.format(os);
std::cout << peer.address() << std::endl;
int n = static_cast<int>(socket_.send_to(request.data(),peer));
BOOST_LOG_TRIVIAL(debug) << "Bytes sent: " << n << std::endl;
*/
}
void Server::write_log(Packet packet, Peer peer) {
BOOST_LOG_TRIVIAL(info) << "Packet send to " << peer.address().to_string() << ":" << peer.port() << std::endl;
BOOST_LOG_TRIVIAL(info) << "Packet\n" << packet << std::endl;
}
void Server::receive() {
Peer peer;
BOOST_LOG_TRIVIAL(info) << "Now listening on port:" << root_.port() << std::endl;
socket_.receive_from(boost::asio::buffer(recv_buffer_), peer);
BOOST_LOG_TRIVIAL(info) << "Packet received" << std::endl;
std::cout << static_cast<int>(recv_buffer_[0]) << std::endl;
std::cout << static_cast<int>(recv_buffer_[1]) << std::endl;
if (recv_buffer_[0] != 57) //TO DO magic number
exit(EXIT_FAILURE); //Change this to send a bad
else if(recv_buffer_[1] != 0) //TO DO magic number
exit(EXIT_FAILURE);
uint16_t packet_length = static_cast<uint16_t>((recv_buffer_[2]<<8)+recv_buffer_[3]);
std::cout << packet_length << std::endl;
if (packet_length > 1024) //TODO magic number
exit(EXIT_FAILURE);
//maybe log ?
for(int i = 0 ; i < packet_length ; i++)
std::cout << i << ":" << (static_cast<int>(recv_buffer_[i+4]) & 0xFF) << std::endl;
Packet packet = Packet::from_string(recv_buffer_.data()+4, packet_length);
std::cout << "packet received" << std::endl;
std::cout << packet << std::endl;
//agent_.notify(packet, peer);
/*
boost::asio::streambuf request;
std::ostream os(&request);
packet_to_send.format(os);
//std::cout << request.size() << std::endl;
socket_.send_to(request.data(), peer);
*/
receive();
}
And the code for my client is the following :
Client::Client(boost::asio::io_service& io_service, Peer peer) : socket_(io_service, Peer(boost::asio::ip::udp::v6(), peer.port())) , peers_(), queue_peer_() {
//socket_.open(boost::asio::ip::udp::v6());
peers_.insert(peer);
queue_peer_.insert(std::make_pair(peer.address().to_string(),std::vector<std::shared_ptr<Tlv>>()));
std::shared_ptr<PeersReq> peersReq(new PeersReq());
add_tlv_to_queue(peersReq, peer);
send();
}
void Client::add_tlv_to_queue(std::shared_ptr<Tlv> tlv, Peer peer) {
if(queue_peer_.find(peer.address().to_string())!= queue_peer_.end()) {
peers_.insert(peer);
queue_peer_.insert(std::make_pair(peer.address().to_string(),std::vector<std::shared_ptr<Tlv>>()));
}
queue_peer_.find(peer.address().to_string())->second.push_back(tlv);
}
void Client::send() {
for ( std::unordered_map<std::string, std::vector<std::shared_ptr<Tlv>>>::iterator it = queue_peer_.begin(); it != queue_peer_.end(); ++it ) {
if(it->second.size()>0) {
boost::asio::streambuf request;
std::ostream os(&request);
Packet packet(it->second);
packet.format(os);
Peer peer = get_peer_from_string(it->first);
std::cout << peer.address() << std::endl;
int n = static_cast<int>(socket_.send_to(request.data(),peer));
}
}
}
Peer Client::get_peer_from_string(std::string string) {
Peer peer;
for(std::set<Peer>::const_iterator it =peers_.begin() ; it != peers_.end() ; it++) {
if(it->address().to_string() == string)
peer=*it;
}
//undefined behaviour if the peer is not registred. But it shouldn't happen
return peer;
}
In the main I use the following code :
Agent agent(storage, peer);
Server server(io_service_server, peer, agent);
Client client(io_service_client, peer);
server.receive();
I don't know if I did something bad or there is something special to get this behviour.
EDIT :
This is a snippet :
#include<array>
#include<boost/asio.hpp>
#include<iostream>
using Peer = boost::asio::ip::udp::endpoint;
using namespace boost::asio::ip;
class Server {
public:
Server(boost::asio::io_service& io_service, Peer peer);
void receive();
private:
boost::asio::ip::udp::socket socket_;
std::array<char, 1024> recv_buffer_;
};
Server::Server(boost::asio::io_service& io_service, Peer peer) :
socket_(io_service, Peer(boost::asio::ip::udp::v6(), peer.port())),
recv_buffer_()
{
boost::asio::streambuf request;
std::ostream os(&request);
os << "Server";
int n = static_cast<int>(socket_.send_to(request.data(),peer));
}
void Server::receive() {
Peer peer;
socket_.receive_from(boost::asio::buffer(recv_buffer_), peer);
std::cout << static_cast<int>(recv_buffer_[0]) << std::endl;
receive();
}
class Client {
public:
Client(boost::asio::io_service& io_service, Peer peer);
void send(Peer peer);
private:
boost::asio::ip::udp::socket socket_;
};
Client::Client(boost::asio::io_service& io_service, Peer peer) : socket_(io_service) {
socket_.open(boost::asio::ip::udp::v6());
send(peer);
}
void Client::send(Peer peer) {
boost::asio::streambuf request;
std::ostream os(&request);
os << "Client";
int n = static_cast<int>(socket_.send_to(request.data(),peer));
}
int main(int argc, char* argv[]) {
boost::asio::io_service io_service_client;
boost::asio::io_service io_service_server;
Peer peer;
peer.address(); //Add an address here
peer.port(12345);
Client client(io_service_client, peer);
Server server(io_service_server, peer);
return 0;
}