I am currently trying to create a server to client connection to send XML documents. It appears that it is possible to send these documents after serializing them. My plan is to establish a connection, send one message from client to the server (a list of filters) and then send N messages from the server side when a message meets the filters criterias.
However, before getting to that, I wanted to try a simple implementation of this using only strings to see how it works and to get a better understanding of the boost asio library.
I then implemented my server part:
#include <iostream>
#include <thread>
#include <queue>
#include <chrono>
#include <array>
#include <boost/asio.hpp>
int main()
{
const int BACKLOG_SIZE = 30;
unsigned short PORT = 3333;
// Endpoint and io_service creation
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address_v4::any(),PORT);
boost::asio::io_service ios;
boost::asio::ip::tcp::acceptor acceptor(ios, ep.protocol());
// Binding to client
acceptor.bind(ep);
acceptor.listen(BACKLOG_SIZE);
boost::asio::ip::tcp::socket socket(ios);
// accepting connection (blocking process)
acceptor.accept(socket);
std::cout << "Binded" << std::endl;
// Receiving data from client
boost::asio::streambuf sb;
boost::system::error_code ec;
while (boost::asio::read(socket, sb, ec))
{
std::cout << "received: " << &sb << "\n";
if (ec)
{
std::cout << "status: " << ec.message() << "\n";
break;
}
}
// Sending data to client
socket.send("Filters received!");
}
and my client part:
#include <boost/asio.hpp>
#include <iostream>
int main()
{
std::string raw_ip_address = "127.0.0.1";
unsigned short port_num = 3333;
std::string FILTER = "ATL";
try {
// Endpoint creation
boost::asio::ip::tcp::endpoint
ep(boost::asio::ip::address::from_string(raw_ip_address),
port_num);
boost::asio::io_service ios;
// Creating and opening a socket.
boost::asio::ip::tcp::socket sock(ios, ep.protocol());
// Connecting a socket.
sock.connect(ep);
// Sending data to server
std::cout << "Connected to " << raw_ip_address << " Port: " << port_num << std::endl;
sock.send(boost::asio::buffer(FILTER));
boost::asio::streambuf sb;
boost::system::error_code ec;
// Receiving data from server
while (boost::asio::read(socket, sb, ec))
{
std::cout << "received: " << &sb << "\n";
if (ec)
{
std::cout << "status: " << ec.message() << "\n";
break;
}
}
}
// Overloads of asio::ip::address::from_string() and
// asio::ip::tcp::socket::connect() used here throw
// exceptions in case of error condition.
catch (boost::system::system_error& e) {
std::cout << "Error occured! Error code = " << e.code()
<< ". Message: " << e.what();
return e.code().value();
}
return 0;
}
I first started with only sending the filters from the client to the server and it all worked well. However when I tried adding the response from the server to the client, it caused an error which I don't really understand: error
As anyone faced this issue and know how to solve it? Is it even possible to establish a connection like that where both side of the socket can send messages as they want (as I said the idea will be to have the server sending N messages coming from another application, after receiving the filters list).
Thanks for your help.
CodePudding user response:
First problem: string literals are not a buffer (or buffer sequence).
You need to describe the buffer, e.g. like so:
std::string response("Filters received!");
socket.send(boost::asio::buffer(response));
There are many ways. E.g. not using a temporary, you could use a string view literal:
socket.send(boost::asio::buffer("Filters received!"sv));
Second problem:
while (boost::asio::read(socket, sb, ec)) {
This cannot work, because you meant sock
, not socket
(which exists, but is ::socket
, type int(&)(int,int,int)
.
That said, it's not very useful to read-to-EOF in a while loop, since the loop will always break with EOF (or another error).
Finally, because currently, client will never close the socket until a response is received, there will be an indefinite waiting of both programs. Consider a partial shutdown:
sock.send(asio::buffer(FILTER));
sock.shutdown(tcp::socket::shutdown_send);
Here are client and server combined
#include <boost/asio.hpp>
#include <iostream>
namespace asio = boost::asio;
using asio::ip::tcp;
using boost::system::error_code;
using namespace std::string_view_literals;
static const std::string raw_ip_address = "127.0.0.1";
static const std::string FILTER = "ATL";
static const int BACKLOG_SIZE = 30;
static const uint16_t PORT = 3333;
int main(int argc, char**) {
asio::io_service ios;
asio::streambuf sb;
error_code ec;
try {
if (argc > 1) { // client
tcp::endpoint ep({}, PORT);
tcp::socket sock(ios, ep.protocol());
sock.connect(ep);
// Sending data to server
std::cout << "Connected to " << sock.remote_endpoint() << std::endl;
sock.send(asio::buffer(FILTER));
sock.shutdown(tcp::socket::shutdown_send);
read(sock, sb, ec);
std::cout << "received: " << &sb << "\n"
<< "status: " << ec.message() << "\n";
} else // server
{
tcp::acceptor acceptor(ios, tcp::v4());
acceptor.bind({{}, PORT});
acceptor.listen(BACKLOG_SIZE);
tcp::socket sock(ios);
acceptor.accept(sock);
std::cout << "Accepted " << sock.remote_endpoint() << std::endl;
read(sock, sb, ec);
std::cout << "received: " << &sb << "\n"
<< "status: " << ec.message() << "\n";
sock.send(asio::buffer("Filters received!"sv));
}
} catch (boost::system::system_error& e) {
std::cout << "Error occured! Error code = " << e.code()
<< ". Message: " << e.what() << "\n";
return 1; // e.code().value() not very useful, as the category is lost
}
}
Run and build with e.g.
g -std=c 20 -O2 -Wall -pedantic -pthread main.cpp -isystem /usr/local/include/
./a.out&
sleep 1; ./a.out client
Prints e.g.
Connected to 127.0.0.1:3333
Accepted 127.0.0.1:50816
received: ATL
status: End of file
received: Filters received!
status: End of file