I am trying for a way to write a socket class to connect my NS-3 simulation to an outside program. So what I want to do is to create packets in NS-3 and send them through this socket to an outside tool, do some simple manipulations on the packet in that tool, and then send it back to NS-3. I don't think the built in NS-3 socket can be used for this purpose.
Has anyone come across something like this before or has any suggestions?
Your help is very much appreciated!
I'm using a TCP socket to connect an external Python TCP Socket using NS-3, here is the code:
/*
* Create a NS-3 Application that opens a TCP Socket and
* waits for incoming connections
*
*/
#include "ns3/icmpv4.h"
#include "ns3/assert.h"
#include "ns3/log.h"
#include "ns3/ipv4-address.h"
#include "ns3/socket.h"
#include "ns3/integer.h"
#include "ns3/boolean.h"
#include "ns3/inet-socket-address.h"
#include "ns3/packet.h"
#include "ns3/trace-source-accessor.h"
#include "ns3/config.h"
#include "ns3/tos-device.h"
#include "ns3/names.h"
#include "ns3/string.h"
#include "ns3/object.h"
namespace ns3 {
IOProxyServer::IOProxyServer ()
{
m_socket = 0;
}
TypeId IOProxyServer::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::IoProxyServer")
.SetParent<Application> ()
.AddConstructor<IOProxyServer> ()
.AddAttribute("RemotePortNumber",
"Remote port listening for connections",
IntegerValue(9999),
MakeIntegerAccessor(&IOProxyServer::m_remotePortNumber),
MakeIntegerChecker<int64_t>())
.AddAttribute("RemoteIp",
"Remote IP listening for connections",
StringValue("127.0.0.1"),
MakeStringAccessor(&IOProxyServer::m_remoteIp),
MakeStringChecker())
.AddAttribute("LocalPortNumber",
"Local port for incoming connections",
IntegerValue(3333),
MakeIntegerAccessor(&IOProxyServer::m_localPortNumber),
MakeIntegerChecker<int64_t>())
.AddAttribute("LocalIp",
"Local IP for incoming connections",
StringValue("127.0.0.1"),
MakeStringAccessor(&IOProxyServer::m_localIp),
MakeStringChecker());
return tid;
}
void IOProxyServer::StartApplication (void)
{
NS_LOG_FUNCTION (this);
m_socket = Socket::CreateSocket (GetNode (), TypeId::LookupByName ("ns3::TcpSocketFactory"));
NS_ASSERT_MSG (m_socket != 0, "An error has happened when trying to create the socket");
InetSocketAddress src = InetSocketAddress (Ipv4Address::GetAny(), m_localPortNumber );
InetSocketAddress dest = InetSocketAddress(Ipv4Address(m_remoteIp.c_str()), m_remotePortNumber);
int status;
status = m_socket->Bind (src);
NS_ASSERT_MSG (status != -1, "An error has happened when trying to bind to local end point");
status = m_socket->Connect(dest);
NS_ASSERT_MSG (status != -1, "An error has happened when trying to connect to remote end point");
// Configures the callbacks for the different events related with the connection
//m_socket->SetConnectCallback
m_socket->SetAcceptCallback (
MakeNullCallback<bool, Ptr<Socket>, const Address &> (),
MakeCallback (&IOProxyServer::HandleAccept, this));
m_socket->SetRecvCallback (
MakeCallback (&IOProxyServer::HandleRead, this));
m_socket->SetDataSentCallback (
MakeCallback (&IOProxyServer::HandleSend,this));
//m_socket->SetSendCallback
m_socket->SetCloseCallbacks (
MakeCallback (&IOProxyServer::HandlePeerClose, this),
MakeCallback (&IOProxyServer::HandlePeerError, this));
// If we need to configure a reception only socket or a sending only socket
// we need to call one of the following methods:
// m_socket->ShutdownSend();
// m_socket->ShutdownRecv();
}
void IOProxyServer::StopApplication (void)
{
NS_LOG_FUNCTION (this);
m_socket->Close();
}
void IOProxyServer::HandlePeerClose (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
}
void IOProxyServer::HandlePeerError (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
}
void IOProxyServer::HandleSend (Ptr<Socket> socket, uint32_t dataSent)
{
NS_LOG_FUNCTION (this << socket);
}
void IOProxyServer::HandleAccept (Ptr<Socket> s, const Address& from)
{
NS_LOG_FUNCTION (this << s << from);
s->SetRecvCallback (MakeCallback (&IOProxyServer::HandleRead, this));
}
void IOProxyServer::HandleRead (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
Ptr<Packet> packet;
while ((packet = socket->RecvFrom (from)))
{
if (packet->GetSize () == 0)
{ //EOF
break;
}
if (InetSocketAddress::IsMatchingType (from))
{
//Do whatever you need with the incoming info
}
}
}
void IOProxyServer::SendData()
{
//Do whatever you need for creating your packet and send it using the socket
//Ptr<Packet> packet = Create<Packet>(pointer, sizeof(pointer));
//m_socket->Send(packet, 0, from);
}
IOProxyServer::~IOProxyServer ()
{
}
void IOProxyServer::DoDispose (void)
{
NS_LOG_FUNCTION (this);
m_socket = 0;
Application::DoDispose ();
}
} // namespace ns3
Related
Imagine that you have some websocket client, that downloading some data in loop like this:
#include <boost/asio.hpp>
#include <boost/beast.hpp>
#include "nlohmann/json.hpp"
namespace beast = boost::beast;
namespace websocket = beast::websocket;
using tcp = boost::asio::ip::tcp;
class Client {
public:
Client(boost::asio::io_context &ctx) : ws_{ctx}, ctx_{ctx} {
ws_.set_option(websocket::stream_base::timeout::suggested(boost::beast::role_type::client));
#define HOST "127.0.0.1"
#define PORT "8000"
boost::asio::connect(ws_.next_layer(), tcp::resolver{ctx_}.resolve(HOST, PORT));
ws_.handshake(HOST ":" PORT, "/api/v1/music");
#undef HOST
#undef PORT
}
~Client() {
if (ws_.is_open()) {
ws_.close(websocket::normal);
}
}
nlohmann::json NextPacket(std::size_t offset) {
nlohmann::json request;
request["offset"] = offset;
ws_.write(boost::asio::buffer(request.dump()));
beast::flat_buffer buffer;
ws_.read(buffer);
return nlohmann::json::parse(std::string_view{reinterpret_cast<const char *>(buffer.data().data()), buffer.size()});
}
private:
boost::beast::websocket::stream<boost::asio::ip::tcp::socket> ws_;
boost::asio::io_context &ctx_;
};
// ... some function
int main() {
boost::asio::io_context context;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> guard{context.get_executor()};
std::thread{[&context]() { context.run(); }}.detach();
static constexpr std::size_t kSomeVeryBigConstant{1'000'000'000};
Client client{context};
std::size_t offset{};
while (offset < kSomeVeryBigConstant) {
offset += client.NextPacket(offset)["offset"].get<std::size_t>();
// UPDATE:
userDefinedLongPauseHere();
}
}
On the server side we have ping requests with some frequency. Were should I handle ping requests? As I understand it, control_callback controls calls to ping, pong and close functions, not requests. With the read or read_async functions, I also cannot catch the ping request.
Beast responds to pings with pongs automatically, as described here: https://github.com/boostorg/beast/issues/899#issuecomment-346333014
Whenever you call read(), it can process a ping and send a pong without you knowing about that.
https://github.com/cesanta/mongoose/blob/master/examples/websocket-server/main.c
#include "mongoose.h"
static const char *s_listen_on = "ws://localhost:80020";
static const char *s_web_root = ".";
// This RESTful server implements the following endpoints:
// /websocket - upgrade to Websocket, and implement websocket echo server
// /api/rest - respond with JSON string {"result": 123}
// any other URI serves static files from s_web_root
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_OPEN) {
// c->is_hexdumping = 1;
} else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_http_match_uri(hm, "/websocket")) {
// Upgrade to websocket. From now on, a connection is a full-duplex
// Websocket connection, which will receive MG_EV_WS_MSG events.
mg_ws_upgrade(c, hm, NULL);
} else if (mg_http_match_uri(hm, "/rest")) {
// Serve REST response
mg_http_reply(c, 200, "", "{\"result\": %d}\n", 123);
} else {
// Serve static files
struct mg_http_serve_opts opts = {.root_dir = s_web_root};
mg_http_serve_dir(c, ev_data, &opts);
}
} else if (ev == MG_EV_WS_MSG) {
// Got websocket frame. Received data is wm->data. Echo it back!
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
mg_ws_send(c, wm->data.ptr, wm->data.len, WEBSOCKET_OP_TEXT);
}
(void) fn_data;
}
int main(void) {
struct mg_mgr mgr; // Event manager
mg_mgr_init(&mgr); // Initialise event manager
printf("Starting WS listener on %s/websocket\n", s_listen_on);
mg_http_listen(&mgr, s_listen_on, fn, NULL); // Create HTTP listener
for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop
mg_mgr_free(&mgr);
return 0;
}
I want to create a websocket server, but I am getting an error while running this project.
I tried on different ports but the result did not change.
Error is:
mongoose.c:2774:mg_listen Failed: ws://localhost:80020, errno 0
Failed
I am trying to write a very elegant way of handling a reconnect loop with boost async_connect(...). The problem is, I don't see a way how I could elegantly solve the following problem:
I have a TCP client that should try to connect asynchronously to a server, if the connection fails because the server is offline or any other error occurs, wait a given amount of time and try to reconnect. There are multiple things to take into consideration here:
Avoidance of global variables if possible
It has to be async connect
A very basic client is instantiated like so:
tcpclient::tcpclient(std::string host, int port) : _endpoint(boost::asio::ip::address::from_string(host), port), _socket(_ios) {
logger::log_info("Initiating client ...");
}
Attempt to connect to the server:
void tcpclient::start() {
bool is_connected = false;
while (!is_connected) {
_socket.async_connect(_endpoint, connect_handler);
_ios.run();
}
// read write data (?)
}
The handler:
void tcpclient::connect_handler(const boost::system::error_code &error) {
if(error){
// trigger disconnect (?)
logger::log_error(error.message());
return;
}
// Connection is established at this point
// Update timer state and start authentication on server ?
logger::log_info("Connected?");
}
How can I properly start reconnecting everytime the connection fails (or is dropped)? Since the handler is static I can not modify a class attribute that indicates the connection status? I want to avoid using hacky global variable workarounds.
How can I solve this issue in a proper way?
My attempt would be something like this:
tcpclient.h
enum ConnectionStatus{
NOT_CONNECTED,
CONNECTED
};
class tcpclient {
public:
tcpclient(std::string host, int port);
void start();
private:
ConnectionStatus _status = NOT_CONNECTED;
void connect_handler(const boost::system::error_code& error);
boost::asio::io_service _ios;
boost::asio::ip::tcp::endpoint _endpoint;
boost::asio::ip::tcp::socket _socket;
};
tcpclient.cpp
#include "tcpclient.h"
#include <boost/chrono.hpp>
#include "../utils/logger.h"
tcpclient::tcpclient(std::string host, int port) : _endpoint(boost::asio::ip::address::from_string(host), port),
_socket(_ios) {
logger::log_info("Initiating client ...");
logger::log_info("Server endpoint: " + _endpoint.address().to_string());
}
void tcpclient::connect_handler(const boost::system::error_code &error) {
if(!error){
_status = CONNECTED;
logger::log_info("Connected.");
}
else{
_status = NOT_CONNECTED;
logger::log_info("Failed to connect");
_socket.close();
}
}
void tcpclient::start() {
while (_status == NOT_CONNECTED) {
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
_socket.close();
_socket.async_connect(_endpoint, std::bind(&tcpclient::connect_handler, this, std::placeholders::_1));
_ios.run();
}
}
The problem is that the reconnect is not working properly and the application seems to freeze for some reason? Aside from that reconnecting also seems problematic once a connection was established and is then dropped (e.g. due to the server crashing/closing).
std::this_thread::sleep_for(std::chrono::milliseconds(2000)); will freeze program for 2 seconds. What can you do here is to launch async timer when connection attempt fails:
::boost::asio::steady_timer m_timer{_ios, boost::asio::chrono::seconds{2}};
void tcpclient::connect_handler(const boost::system::error_code &error)
{
if(!error)
{
_status = CONNECTED;
logger::log_info("Connected.");
}
else
{
_status = NOT_CONNECTED;
logger::log_info("Failed to connect");
_socket.close();
m_timer.expires_from_now(boost::asio::chrono::seconds{2});
m_timer.async_wait(std::bind(&tcpclient::on_ready_to_reconnect, this, std::placeholders::_1));
}
}
void tcpclient::on_ready_to_reconnect(const boost::system::error_code &error)
{
try_connect();
}
void tcpclient::try_connect()
{
m_socket.async_connect(_endpoint, std::bind(&tcpclient::connect_handler, this, std::placeholders::_1));
}
void tcpclient::start()
{
try_connect();
_ios.run();
}
There is also no need for while (_status == NOT_CONNECTED) loop, because io service will be busy and _ios.run(); won't return until connection is established.
I have to write a program that initializes a array of TCP sockets, and use async i/o to read data using a thread pool. Im new to async io, thread pools, shared_ptrs. What I now have is a working program with one socket. Heres the clipping:
boost::shared_ptr< asio::ip::tcp::socket > sock1(
new asio::ip::tcp::socket( *io_service )
);
boost::shared_ptr< asio::ip::tcp::acceptor > acceptor( new asio::ip::tcp::acceptor( *io_service ) );
asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), portNum);
acceptor->open( endpoint.protocol() );
acceptor->set_option( asio::ip::tcp::acceptor::reuse_address( false ) );
acceptor->bind( endpoint );
acceptor->listen();
I am stuck in getting similar code for an "array of sockets", that is, I want to have acceptor[], that are binded to endpoint[]. I must pass around pointers to the acceptors an sockets, so shared_ptr comes in, and am unable to get it right.
for (i=0; i<10; i++) {
// init socket[i] with *io_service
// init endpoint[i]
// init acceptor[i] with *io_service
acceptor[i]->listen()
}
(btw, do I really need an socket[] array for this porpose?) Can someone please help me?
Here is a full example for using Boost ASIO to implement a TCP echo server listening to multiple ports, with a thread pool to distribute work across multiple cores. It is based on this example from the Boost documentation (providing a single-threaded TCP echo server).
Session class
The session class represents a single active socket connection with a client. It reads from the socket and then writes the same data into the socket to echo it back to the client. The implementation uses the async_... functions provided by Boost ASIO: These functions register a callback at the I/O service that will be triggered when the I/O operation has finished.
session.h
#pragma once
#include <array>
#include <memory>
#include <boost/asio.hpp>
/**
* A TCP session opened on the server.
*/
class session : public std::enable_shared_from_this<session> {
using endpoint_t = boost::asio::ip::tcp::endpoint;
using socket_t = boost::asio::ip::tcp::socket;
public:
session(boost::asio::io_service &service);
/**
* Start reading from the socket.
*/
void start();
/**
* Callback for socket reads.
*/
void handle_read(const boost::system::error_code &ec,
size_t bytes_transferred);
/**
* Callback for socket writes.
*/
void handle_write(const boost::system::error_code &ec);
/**
* Get a reference to the session socket.
*/
socket_t &socket() { return socket_; }
private:
/**
* Session socket
*/
socket_t socket_;
/**
* Buffer to be used for r/w operations.
*/
std::array<uint8_t, 4096> buffer_;
};
session.cpp
#include "session.h"
#include <functional>
#include <iostream>
#include <thread>
using boost::asio::async_write;
using boost::asio::buffer;
using boost::asio::io_service;
using boost::asio::error::connection_reset;
using boost::asio::error::eof;
using boost::system::error_code;
using boost::system::system_error;
using std::placeholders::_1;
using std::placeholders::_2;
session::session(io_service &service) : socket_{service} {}
void session::start() {
auto handler = std::bind(&session::handle_read, shared_from_this(), _1, _2);
socket_.async_read_some(buffer(buffer_), handler);
}
void session::handle_read(const error_code &ec, size_t bytes_transferred) {
if (ec) {
if (ec == eof || ec == connection_reset) {
return;
}
throw system_error{ec};
}
std::cout << "Thread " << std::this_thread::get_id() << ": Received "
<< bytes_transferred << " bytes on " << socket_.local_endpoint()
<< " from " << socket_.remote_endpoint() << std::endl;
auto handler = std::bind(&session::handle_write, shared_from_this(), _1);
async_write(socket_, buffer(buffer_.data(), bytes_transferred), handler);
}
void session::handle_write(const error_code &ec) {
if (ec) {
throw system_error{ec};
}
auto handler = std::bind(&session::handle_read, shared_from_this(), _1, _2);
socket_.async_read_some(buffer(buffer_), handler);
}
Server class
The server class creates an acceptor for each given port. The acceptor will listen to the port and dispatch a socket for each incoming connection request. The waiting for an incoming connection is again implemented with a async_... function.
server.h
#pragma once
#include <vector>
#include <boost/asio.hpp>
#include "session.h"
/**
* Listens to a socket and dispatches sessions for each incoming request.
*/
class server {
using acceptor_t = boost::asio::ip::tcp::acceptor;
using endpoint_t = boost::asio::ip::tcp::endpoint;
using socket_t = boost::asio::ip::tcp::socket;
public:
server(boost::asio::io_service &service, const std::vector<uint16_t> &ports);
/**
* Start listening for incoming requests.
*/
void start_accept(size_t index);
/**
* Callback for when a request comes in.
*/
void handle_accept(size_t index, std::shared_ptr<session> new_session,
const boost::system::error_code &ec);
private:
/**
* Reference to the I/O service that will call our callbacks.
*/
boost::asio::io_service &service_;
/**
* List of acceptors each listening to (a different) socket.
*/
std::vector<acceptor_t> acceptors_;
};
server.cpp
#include "server.h"
#include <functional>
#include <boost/asio.hpp>
using std::placeholders::_1;
using std::placeholders::_2;
using boost::asio::io_service;
using boost::asio::error::eof;
using boost::system::error_code;
using boost::system::system_error;
server::server(boost::asio::io_service &service,
const std::vector<uint16_t> &ports)
: service_{service} {
auto create_acceptor = [&](uint16_t port) {
acceptor_t acceptor{service};
endpoint_t endpoint{boost::asio::ip::tcp::v4(), port};
acceptor.open(endpoint.protocol());
acceptor.set_option(acceptor_t::reuse_address(false));
acceptor.bind(endpoint);
acceptor.listen();
return acceptor;
};
std::transform(ports.begin(), ports.end(), std::back_inserter(acceptors_),
create_acceptor);
for (size_t i = 0; i < acceptors_.size(); i++) {
start_accept(i);
}
}
void server::start_accept(size_t index) {
auto new_session{std::make_shared<session>(service_)};
auto handler =
std::bind(&server::handle_accept, this, index, new_session, _1);
acceptors_[index].async_accept(new_session->socket(), handler);
}
void server::handle_accept(size_t index, std::shared_ptr<session> new_session,
const boost::system::error_code &ec) {
if (ec) {
throw system_error{ec};
}
new_session->start();
start_accept(index);
}
Main
The main function creates the server for a series of ports.
For this example, the ports are set to 5000,...,5010. It then spawns a series of threads for each CPU core that calls the run function of the I/O service provided by Boost ASIO. The I/O service is capable of handling such a multi-threading scenario, dispatching work among the threads that have called its run function (reference):
Multiple threads may call the run() function to set up a pool of threads from which the io_context may execute handlers. All threads that are waiting in the pool are equivalent and the io_context may choose any one of them to invoke a handler.
server_main.cpp
#include "server.h"
#include <numeric>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
int main() {
std::vector<uint16_t> ports{};
// Fill ports with range [5000,5000+n)
ports.resize(10);
std::iota(ports.begin(), ports.end(), 5000);
boost::asio::io_service service{};
server s{service, ports};
// Spawn thread group for running the I/O service
size_t thread_count = std::min(
static_cast<size_t>(boost::thread::hardware_concurrency()), ports.size());
boost::thread_group tg{};
for (size_t i = 0; i < thread_count; ++i) {
tg.create_thread([&]() { service.run(); });
}
tg.join_all();
return 0;
}
You could compile the server for example with g++ -O2 -lboost_thread -lpthread {session,server,server_main}.cpp -o server. If you run the server with clients that send it random data, you would get output such as:
Thread 140043413878528: Received 4096 bytes on 127.0.0.1:5007 from 127.0.0.1:40856
Thread 140043405485824: Received 4096 bytes on 127.0.0.1:5000 from 127.0.0.1:42556
Thread 140043388700416: Received 4096 bytes on 127.0.0.1:5005 from 127.0.0.1:58582
Thread 140043388700416: Received 4096 bytes on 127.0.0.1:5001 from 127.0.0.1:40192
Thread 140043388700416: Received 4096 bytes on 127.0.0.1:5003 from 127.0.0.1:42508
Thread 140043397093120: Received 4096 bytes on 127.0.0.1:5008 from 127.0.0.1:37808
Thread 140043388700416: Received 4096 bytes on 127.0.0.1:5006 from 127.0.0.1:35440
Thread 140043397093120: Received 4096 bytes on 127.0.0.1:5009 from 127.0.0.1:58306
Thread 140043405485824: Received 4096 bytes on 127.0.0.1:5002 from 127.0.0.1:56300
You can see the server handling multiple ports, with work being distributed among the worker threads (not necessarily restricting each thread to a specific port).
See code. :P
I am able to receive new connections before async_accept() has been called. My delegate function is also never called so I can't manage any connections I receive, rendering the new connections useless. ;)
So here's my question. Is there a way to prevent the Boost ASIO acceptor from getting new connections on its own and only getting connections from async_accept()?
Thanks!
AlexSocket::AlexSocket(boost::asio::io_service& s): myService(s)
{
//none at the moment
connected = false;
listening = false;
using boost::asio::ip::tcp;
mySocket = new tcp::socket(myService);
}
AlexSocket::~AlexSocket()
{
delete mySocket;
}
bool AlexSocket::StartListening(int port)
{
bool didStart = false;
if (!this->listening)
{
//try to listen
acceptor = new tcp::acceptor(this->myService);
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), port);
acceptor->open(endpoint.protocol());
acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
acceptor->bind(endpoint);
//CAN GET NEW CONNECTIONS HERE (before async_accept is called)
acceptor->listen();
didStart = true; //probably change?
tcp::socket* tempNewSocket = new tcp::socket(this->myService);
//acceptor->async_accept(*tempNewSocket, boost::bind(&AlexSocket::NewConnection, this, tempNewSocket, boost::asio::placeholders::error) );
}
else //already started!
return false;
this->listening = didStart;
return didStart;
}
//this function is never called :(
void AlexSocket::NewConnection(tcp::socket* s, const boost::system::error_code& error)
{
cout << "New Connection Made" << endl;
//Start new accept async
tcp::socket* tempNewSocket = new tcp::socket(this->myService);
acceptor->async_accept(*tempNewSocket, boost::bind(&AlexSocket::NewConnection, this, tempNewSocket, boost::asio::placeholders::error) );
}
bool AlexSocket::ConnectToServer(std::string toConnectTo, string port)
{
if (connected)
return false;
this->serverConnectedTo = toConnectTo;
this->serverPort = port;
ip::tcp::resolver resolver(myService);
ip::tcp::resolver::query newQuery(toConnectTo, port);
ip::tcp::resolver::iterator myIter = resolver.resolve(newQuery);
ip::tcp::resolver::iterator end;
//error
boost::system::error_code error = boost::asio::error::host_not_found;
//try each endpoint
bool connected = false;
while (error && myIter != end)
{
ip::tcp::endpoint endpoint = *myIter++;
std::cout << endpoint << std::endl;
mySocket->close();
mySocket->connect(*myIter, error);
if (error)
{
//try to connect, if it didn't work return false
cout << "Did not Connect" << endl << error << endl;
}
else
{
//was able to connect
cout << "Connected!" << endl;
connected = true;
}
myIter++;
}
this->connected = connected;
return connected;
}
EDIT:
I've changed my code to reflect what the answers so far have said. I am passing in an io_service to the ctor of my class. As you can see below, main is NOT calling run on the service, so I would assume that nothing should be able to connect right?
I have put my debugger on the listen() line and went to "canyouseeme.org". Typed in 57422 and hit Connect. Couldn't. Ran the listen() line. Was able to connect. This shouldn't be possible right? Like never? :(
No idea what to do anymore. main() is below.
int main()
{
boost::asio::io_service s;
AlexSocket test(s);
test.StartListening(57422);
test.ConnectToServer("localhost", "57422");
cout << "Enter something to quit" << endl;
int a2;
cin >> a2;
return 0;
}
So here's my question. Is there a way to prevent the Boost ASIO acceptor from getting new connections on its own and only getting connections from async_accept()?
Why do you think this is happening? If you posted the complete code, that would greatly help. When I take your snippet and put a boilerplate main and io_service::run() around it, everything works fine.
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>
using namespace boost::asio;
class Socket {
public:
Socket(
io_service& io_service
) :
_io_service( io_service ),
_acceptor( new ip::tcp::acceptor(io_service) )
{
}
bool start(int port)
{
//try to listen
ip::tcp::endpoint endpoint(ip::tcp::v4(), port);
_acceptor->open(endpoint.protocol());
_acceptor->set_option(ip::tcp::acceptor::reuse_address(true));
_acceptor->bind(endpoint);
//CAN GET NEW CONNECTIONS HERE (before async_accept is called)
_acceptor->listen();
ip::tcp::socket* temp = new ip::tcp::socket( _io_service );
_acceptor->async_accept(
*temp,
boost::bind(
&Socket::NewConnection,
this,
temp,
boost::asio::placeholders::error
)
);
}
void NewConnection(
ip::tcp::socket* s,
const boost::system::error_code& error
)
{
std::cout << "New Connection Made" << std::endl;
//Start new accept async
ip::tcp::socket* temp = new ip::tcp::socket( _io_service );
_acceptor->async_accept(
*temp,
boost::bind(
&Socket::NewConnection,
this,
temp,
boost::asio::placeholders::error
)
);
}
private:
io_service& _io_service;
ip::tcp::acceptor* _acceptor;
};
int
main()
{
io_service foo;
Socket sock( foo );
sock.start(1234);
foo.run();
return 0;
}
compile and run:
macmini:~ samm$ g++ -lboost_system accept.cc
macmini:~ samm$ ./a.out
New Connection Made
telnet from another terminal
macmini:~ samm$ telnet 127.0.0.1 1234
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
I think you are mixing different things here.
On the one hand, you are creating a socket for data exchange. A socket is nothing more than an endpoint of an inter-process communication flow across a computer network. Your boost::asio::tcp::socket uses the TCP-protocoll for the communication; but in general, a socket can use other protocols. For opening a tcp-socket, one uses generally the sequence open-bind-listen-accept on the host.
On the other hand, you analyse the (underlying) TCP-connection.
So there are two different things here. While for the socket the connection is considered "established" only after the "accept" of the host, the underlying TCP-connection is already established after the client connects to a listening socket. (One the server side, that connection is put on a stack, from which it is dequeue when you call accept()).
So the only way to prohibit connection in your case, is not to call listen().
If you are truly getting a new connection at the point when you call acceptor->listen() then I am puzzled by that. What are you using to determine whether you've gotten a connection or not? The io_service is typically quite "reactive" in that it only reacts to events that it has been explicitly told to react to.
In your example above, the only thing I see that would cause a "new connection" to be initiated is calling async_accept. Additionally, what you described makes little sense from a low-level sockets standpoint (using BSD sockets, typically you must call bind, listen, and accept in that order, and only then can a new connection be made).
My suspicion is that you've actually got some faulty logic somewhere. Who calls StartListening and how often is it called (it should only need to be called once). You've gone through a bunch of extra effort to setup your acceptor object that's usually not necessary in Asio - you can typically just use the acceptor constructor to create an acceptor with all the parameters you need, and then just call async_accept:
acceptor = new tcp::acceptor(
this->myService,
boost::asio::ip::tcp::endpoint(
boost::asio::ip::tcp::v4(),
port),
true);
tcp::socket* tempNewSocket = new tcp::socket(this->myService);
acceptor->async_accept(
*tempNewSocket,
boost::bind(
&AlexSocket::NewConnection,
this,
tempNewSocket,
boost::asio::placeholders::error) );