Boost.asio server-client. Connect between 2 computers - c++

Im following the tutorials at the boost official web site http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/tutorial/tutdaytime1.html.
The program is working perfectly if i connect to "localhost" or "127.0.0.1" on the same machine. But if i run the client on another computer with the same network it fails to connect to the server. Why is this happening? and what would i have to do to get the client to run on another network?
Error: connect: No connection could be made because the target machine actively refused it.
Client:
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
int main()
{
try
{
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
char* serverName = "localhost";
tcp::resolver::query query(serverName, "daytime");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::socket socket(io_service);
while(true)
{
boost::asio::connect(socket, endpoint_iterator);
for (;;)
{
boost::array<char, 128> buf;
boost::system::error_code error;
size_t len = socket.read_some(boost::asio::buffer(buf), error);
if (error == boost::asio::error::eof)
break; // Connection closed cleanly by peer.
else if (error)
throw boost::system::system_error(error); // Some other error.
std::cout.write(buf.data(), len);
std::cout <<"\n";
}
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
Server:
#include <iostream>
#include <string>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
int main()
{
try
{
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 13));
for (;;)
{
tcp::socket socket(io_service);
acceptor.accept(socket);
std::string message = "This is the Server!";
boost::system::error_code ignored_error;
boost::asio::write(socket, boost::asio::buffer(message), ignored_error);
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}

I would guess your problem might be that you return on the first error. Resolving gives you an iterator on a number of endpoints. You try the first of those and if it does not work out you give up instead of letting the iterator go on.
Again, i am by no means an expert in boost::asio and far less in its TCP world but resolve may return more than one endpoint (for example IPv4 and IPv6) and possibly only one of them does not work out here.
For testing you could create the endpoint yourself by first creating a ip::address object, using its from_string() method to give it the address of the server (works only on your local network of course) and then using it for your endpoint:
boost::asio::ip::address address;
address.from_string("the.servers.ip.here");
boost::asio::ip::tcp::endpoint endpoint(address, 13);
boost::asio::connect(socket, endpoint);
And see if that works. If not, it probably is a problem on the server side.

To run the server and client on separate networks, Make the client connect to the servers external ip address. This is obvious but external ip addresses constantly change so to solve this problem you can go to www.noip.com and create a name that links to your ip address. This way in the client all you have to do is specify a name instead of an ip address.

most likely firewall issue, if you are using windows for server check windows firewall, if you are using linux, check the iptables.

Related

boost::asio::ip::tcp::socket - How to bind to a specific local port

I am making a client socket.
To make things easier for my testers, I'd like to specify the network card and port that the socket will use.
Yesterday, in my Google search, I found: Binding boost asio to local tcp endpoint
By performing the open, bind, and async_connect, I was able to bind to a specific network card and I started seeing traffic in Wireshark.
However, Wireshark reports that the socket has been given a random port rather than the one I specified. I would think if the port was in use it would have filled out the error_code passed to the bind method.
What am I doing wrong?
Here is my minimal example, extracted and edited from my real solution.
// Boost Includes
#include <boost/asio.hpp>
#include <boost/atomic.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/thread/condition_variable.hpp>
// Standard Includes
#include <exception>
#include <memory>
#include <string>
#include <sstream>
boost::asio::io_service g_ioService; /** ASIO sockets require an io_service to run on*/
boost::thread g_thread; /** thread that will run the io_service and hence where callbacks are called*/
boost::asio::ip::tcp::socket g_socket(g_ioService); /** Aync socket*/
boost::asio::ip::tcp::resolver g_resolver(g_ioService); /** Resolves IP Addresses*/
//--------------------------------------------------------------------------------------------------
void OnConnect(const boost::system::error_code & errorCode, boost::asio::ip::tcp::resolver::iterator endpoint)
{
if (errorCode || endpoint == boost::asio::ip::tcp::resolver::iterator())
{
// Error - An error occured while attempting to connect
throw std::runtime_error("An error occured while attempting to connect");
}
// We connected to an endpoint
/*
// Start reading from the socket
auto callback = boost::bind(OnReceive, boost::asio::placeholders::error);
boost::asio::async_read_until(g_socket, m_receiveBuffer, '\n', callback);
*/
}
//--------------------------------------------------------------------------------------------------
void Connect()
{
const std::string hostName = "10.84.0.36";
const unsigned int port = 1007;
// Resolve to translate the server machine name into a list of endpoints
std::ostringstream converter;
converter << port;
const std::string portAsString = converter.str();
boost::asio::ip::tcp::resolver::query query(hostName, portAsString);
boost::system::error_code errorCode;
boost::asio::ip::tcp::resolver::iterator itEnd;
boost::asio::ip::tcp::resolver::iterator itEndpoint = g_resolver.resolve(query, errorCode);
if (errorCode || itEndpoint == itEnd)
{
// Error - Could not resolve either machine
throw std::runtime_error("Could not resolve either machine");
}
g_socket.open(boost::asio::ip::tcp::v4(), errorCode);
if (errorCode)
{
// Could open the g_socket
throw std::runtime_error("Could open the g_socket");
}
boost::asio::ip::tcp::endpoint localEndpoint(boost::asio::ip::address::from_string("10.86.0.18"), 6000);
g_socket.bind(localEndpoint, errorCode);
if (errorCode)
{
// Could bind the g_socket to local endpoint
throw std::runtime_error("Could bind the socket to local endpoint");
}
// Attempt to asynchronously connect using each possible end point until we find one that works
boost::asio::async_connect(g_socket, itEndpoint, boost::bind(OnConnect, boost::asio::placeholders::error, boost::asio::placeholders::iterator));
}
//--------------------------------------------------------------------------------------------------
void g_ioServiceg_threadProc()
{
try
{
// Connect to the server
Connect();
// Run the asynchronous callbacks from the g_socket on this thread
// Until the io_service is stopped from another thread
g_ioService.run();
}
catch (...)
{
throw std::runtime_error("unhandled exception caught from io_service g_thread");
}
}
//--------------------------------------------------------------------------------------------------
int main()
{
// Start up the IO service thread
g_thread.swap(boost::thread(g_ioServiceg_threadProc));
// Hang out awhile
boost::this_thread::sleep_for(boost::chrono::seconds(60));
// Stop the io service and allow the g_thread to exit
// This will cancel any outstanding work on the io_service
g_ioService.stop();
// Join our g_thread
if (g_thread.joinable())
{
g_thread.join();
}
return true;
}
As you can see in the following screenshot, a random port 32781 was selected rather than my requested port 6000.
I doubt topic starter is still interested in this question, but for all of future seekers like myself, here is the solution.
The issue here is that boost::asio::connect closes the socket before calling connect for every endpoint in the provided range:
From boost/asio/impl/connect.hpp:
template <typename Protocol BOOST_ASIO_SVC_TPARAM,
typename Iterator, typename ConnectCondition>
Iterator connect(basic_socket<Protocol BOOST_ASIO_SVC_TARG>& s,
Iterator begin, Iterator end, ConnectCondition connect_condition,
boost::system::error_code& ec)
{
ec = boost::system::error_code();
for (Iterator iter = begin; iter != end; ++iter)
{
iter = (detail::call_connect_condition(connect_condition, ec, iter, end));
if (iter != end)
{
s.close(ec); // <------
s.connect(*iter, ec);
if (!ec)
return iter;
}
...
}
That is why bound address is reset. To keep it bound one can use socket.connect/async_connect(...) directly
6000 is the remote endpoint port, and it is correctly used (otherwise, you wouldn't be connecting to the server side).
From: https://idea.popcount.org/2014-04-03-bind-before-connect/
A TCP/IP connection is identified by a four element tuple: {source IP, source port, destination IP, destination port}. To establish a TCP/IP connection only a destination IP and port number are needed, the operating system automatically selects source IP and port.
Since you do not bind to a local port, one is selected randomly from the "ephemeral port range". This is, by far, the usual way to connect.
Fear not:
It is possible to ask the kernel to select a specific source IP and port by calling bind() before calling connect()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Let the source address be 192.168.1.21:1234
s.bind(("192.168.1.21", 1234))
s.connect(("www.google.com", 80))
The sample is python.
You do that, but still get another port. It's likely that the hint port is not available.
Check the information on SO_REUSEADDR and SO_REUSEPORT in the linked article

udp broadcast using boost::asio under windows

I'm having problems with the udp broadcast subsection of an application. I am using boost 1.62.0 under windows 10.
void test_udp_broadcast(void)
{
boost::asio::io_service io_service;
boost::asio::ip::udp::socket socket(io_service);
boost::asio::ip::udp::endpoint remote_endpoint;
socket.open(boost::asio::ip::udp::v4());
socket.set_option(boost::asio::ip::udp::socket::reuse_address(true));
socket.set_option(boost::asio::socket_base::broadcast(true));
remote_endpoint = boost::asio::ip::udp::endpoint(boost::asio::ip::address_v4::any(), 4000);
try {
socket.bind(remote_endpoint);
socket.send_to(boost::asio::buffer("abc", 3), remote_endpoint);
} catch (boost::system::system_error e) {
std::cout << e.what() << std::endl;
}
}
I receive:
send_to: The requested address is not valid in its context
From the catch.
I've attempted to change the endpoint from any() to broadcast(), however this only throws the same error on bind().
I normally program under linux, and this code works on my normal target. So I'm scratching my head as to what I'm doing wrong here. Can anyone give me a poke in the right direction?
I believe you want to bind your socket to a local endpoint with any() (if you wish to receive broadcast packets - see this question), and send to a remote endpoint using broadcast() (see this question).
The following compiles for me and does not throw any errors:
void test_udp_broadcast(void)
{
boost::asio::io_service io_service;
boost::asio::ip::udp::socket socket(io_service);
boost::asio::ip::udp::endpoint local_endpoint;
boost::asio::ip::udp::endpoint remote_endpoint;
socket.open(boost::asio::ip::udp::v4());
socket.set_option(boost::asio::ip::udp::socket::reuse_address(true));
socket.set_option(boost::asio::socket_base::broadcast(true));
local_endpoint = boost::asio::ip::udp::endpoint(boost::asio::ip::address_v4::any(), 4000);
remote_endpoint = boost::asio::ip::udp::endpoint(boost::asio::ip::address_v4::broadcast(), 4000);
try {
socket.bind(local_endpoint);
socket.send_to(boost::asio::buffer("abc", 3), remote_endpoint);
} catch (boost::system::system_error e) {
std::cout << e.what() << std::endl;
}
}

VK API throught C++ Boost

how to open socket for connecting to VK API, this code works good with other resources, but gives APPCRASH with api.vk.com. In browser it works with http, hence it should work here, and problem is not in 'http`, or am I wrong? Help!
P.S. I'm new to Boost and VK API, so if you can, explain it in details, thank you.
int main()
{
boost::asio::io_service io_service;
// Get a list of endpoints corresponding to the server name.
tcp::resolver resolver(io_service);
tcp::resolver::query query("api.vk.com", "http");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
// Try each endpoint until we successfully establish a connection.
tcp::socket socket(io_service);
boost::system::error_code error = boost::asio::error::host_not_found;
socket.connect(*endpoint_iterator, error);
return 0;
}
It looks like APPCRASH might be a thing you see in the Windows event log.
From that, I formed the hypothesis that you might be running this code in a windows service context.
Windows services do not have network access by default.
This means the DNS lookup may fail. You get an exception, e.g. resolve: Host not found (authoritative). This is what happens in a Linux console when I purposefully change the domain name to a nonexisting TLD:
$ ./test
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> >'
what(): resolve: Host not found (authoritative)
Aborted (core dumped)
Because you don't handle the exception or check for errors, the program is abnormally terminated.
Fixed Demo
Note:
I opted to handle errors rather than exceptions.
You failed to loop through the query results (using only the first without even checking whether it was valid)
Coliru, much like a restricted windows service, does not allow network connectivity outside the loopback adaptor either, so it shows a proper error
Live On Coliru
#include <boost/asio.hpp>
#include <iostream>
using boost::asio::ip::tcp;
int main()
{
boost::asio::io_service io_service;
boost::system::error_code error = boost::asio::error::host_not_found;
// Get a list of endpoints corresponding to the server name.
tcp::resolver resolver(io_service);
tcp::resolver::query query("api.vk.com", "http");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query, error), last;
if (!error) {
// Try each endpoint until we successfully establish a connection.
tcp::socket socket(io_service);
for (;endpoint_iterator != last; ++endpoint_iterator) {
socket.connect(*endpoint_iterator, error);
if (!error) {
std::cout << "Successfully connected to " << endpoint_iterator->endpoint() << "\n";
break; // found working endpoint
} else {
std::cout << "Skipped " << endpoint_iterator->endpoint() << " - not connecting\n";
}
}
return 0;
} else {
std::cout << error.message() << "\n";
return 255;
}
}
On my system it prints
Successfully connected to 87.240.131.97:80
I simply changed my DNS server and it works: Successfully connected to 87.240.131.119:80

Issue with broadcast using Boost.Asio

I apologize in advance if the question has been previously answered, but I've searched and found nothing that helps me. As indicated by the question's title, I'm trying to broadcast a package from a server to a set of clients listening for any message.
The client will count the number of messages it receives during one second.
The server side of things goes like this:
class Server
{
public:
Server(boost::asio::io_service& io)
: socket(io, udp::endpoint(udp::v4(), 8888))
, broadcastEndpoint(address_v4::broadcast(), 8888)
, tickHandler(boost::bind(&Server::Tick, this, boost::asio::placeholders::error))
, timer(io, boost::posix_time::milliseconds(20))
{
socket.set_option(boost::asio::socket_base::reuse_address(true));
socket.set_option(boost::asio::socket_base::broadcast(true));
timer.async_wait(tickHandler);
}
private:
void Tick(const boost::system::error_code&)
{
socket.send_to(boost::asio::buffer(buffer), broadcastEndpoint);
timer.expires_at(timer.expires_at() + boost::posix_time::milliseconds(20));
timer.async_wait(tickHandler);
}
private:
udp::socket socket;
udp::endpoint broadcastEndpoint;
boost::function<void(const boost::system::error_code&)> tickHandler;
boost::asio::deadline_timer timer;
boost::array<char, 100> buffer;
};
It is initialized and run in the following way:
int main()
{
try
{
boost::asio::io_service io;
Server server(io);
io.run();
}
catch (const std::exception& e)
{
std::cerr << e.what() << "\n";
}
return 0;
}
This (apparently) works fine. Now comes the client...
void HandleReceive(const boost::system::error_code&, std::size_t bytes)
{
std::cout << "Got " << bytes << " bytes\n";
}
int main(int argc, char* argv[])
{
if (argc != 2)
{
std::cerr << "Usage: " << argv[0] << " <host>\n";
return 1;
}
try
{
boost::asio::io_service io;
udp::resolver resolver(io);
udp::resolver::query query(udp::v4(), argv[1], "1666");
udp::endpoint serverEndpoint = *resolver.resolve(query);
//std::cout << serverEndpoint.address() << "\n";
udp::socket socket(io);
socket.open(udp::v4());
socket.bind(serverEndpoint);
udp::endpoint senderEndpoint;
boost::array<char, 300> buffer;
auto counter = 0;
auto start = std::chrono::system_clock::now();
while (true)
{
socket.receive_from(boost::asio::buffer(buffer), senderEndpoint);
++counter;
auto current = std::chrono::system_clock::now();
if (current - start >= std::chrono::seconds(1))
{
std::cout << counter << "\n";
counter = 0;
start = current;
}
}
}
catch (const std::exception& e)
{
std::cerr << e.what() << "\n";
}
This works when running both the server and client on the same machine, but doesn't when I run the server on a machine different from that of where I run the client.
First thing is, it seems odd to me that I have to resolve the server's address. Perhaps I don't know how broadcasting really works, but I thought the server would send a message using its socket with the broadcast option turned on, and it would arrive to all the sockets in the same network.
I read you should bind the client's socket to the address_v4::any() address. I did, it doesn't work (says something about a socket already using the address/port).
Thanks in advance.
PS: I'm under Windows 8.
I am a bit surprised this works on the same machine. I would not have expected the client, listening to port 1666, to receive data being sent to the broadcast address on port 8888.
bind() assigns a local endpoint (composed of a local address and port) to the socket. When a socket binds to an endpoint, it specifies that the socket will only receive data sent to the bound address and port. It is often advised to bind to address_v4::any(), as this will use all available interfaces for listening. In the case of a system with multiple interfaces (possible multiple NIC cards), binding to a specific interface address will result in the socket only listening to data received from the specified interface[1]. Thus, one might find themselves obtaining an address through resolve() when the application wants to bind to a specific network interface and wants to support resolving it by providing the IP directly (127.0.0.1) or a name (localhost).
It is important to note that when binding to a socket, the endpoint is composed of both an address and port. This is the source of my surprise that it works on the same machine. If the server is writing to broadcast:8888, a socket bound to port 1666 should not receive the datagram. Nevertheless, here is a visual of the endpoints and networking:
.--------.
.--------.|
.--------. address: any address: any .--------.||
| | port: any / \ port: 8888 | |||
| server |-( ----------->| address: broadcast |----------> )-| client ||'
| | \ port: 8888 / | |'
'--------' '--------'
The server binds to any address and any port, enables the broadcast option, and sends data to the remote endpoint (broadcast:8888). Clients bound to the any address on port 8888 should receive the data.
A simple example is as follows.
The server:
#include <boost/asio.hpp>
int main()
{
namespace ip = boost::asio::ip;
boost::asio::io_service io_service;
// Server binds to any address and any port.
ip::udp::socket socket(io_service,
ip::udp::endpoint(ip::udp::v4(), 0));
socket.set_option(boost::asio::socket_base::broadcast(true));
// Broadcast will go to port 8888.
ip::udp::endpoint broadcast_endpoint(ip::address_v4::broadcast(), 8888);
// Broadcast data.
boost::array<char, 4> buffer;
socket.send_to(boost::asio::buffer(buffer), broadcast_endpoint);
}
The client:
#include <iostream>
#include <boost/asio.hpp>
int main()
{
namespace ip = boost::asio::ip;
boost::asio::io_service io_service;
// Client binds to any address on port 8888 (the same port on which
// broadcast data is sent from server).
ip::udp::socket socket(io_service,
ip::udp::endpoint(ip::udp::v4(), 8888 ));
ip::udp::endpoint sender_endpoint;
// Receive data.
boost::array<char, 4> buffer;
std::size_t bytes_transferred =
socket.receive_from(boost::asio::buffer(buffer), sender_endpoint);
std::cout << "got " << bytes_transferred << " bytes." << std::endl;
}
When the client is not co-located with the server, then it could be a variety of network related issues:
Verify connectivity between the server and client.
Verify firewall exceptions.
Verify broadcast support/exceptions on the routing device.
Use a network analyzer tool, such as Wireshark, to verify that the time to live field in the packets is high enough that it will not be discarded during routing.
1. On Linux, broadcast datagrams received by an adapter will not be passed to a socket bound to a specific interface, as the datagram's destination is set to the broadcast address. On the other hand, Windows will pass broadcast datagrams received by an adapter to sockets bound to a specific interface.

boost::asio checker

try
{
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 13));
for (;;)
{
tcp::socket socket(io_service);
acceptor.accept(socket);
//how do i make a checker here if the client is not sending anything then server send or if the client sending then server recive
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
how do i make a checker, if the client is not sending anything then server send or if the client sending then server recive
The question is not immediately clear.
I would start an async_read() with an associated deadline_timer set to an appropriate value. If your timer expires before any reading was performed, then have your server send its data.