Event-Based Programming


Twisted and boost::asio

Twisted via Python

Mike Warren

https://@meejah.ca

boost::asio via C++

Kurt Neufeld

burgundywall.com

@kneufeld

Talk Outline

  • Event-Based Programming Overview
  • Demo chat server
  • Programming with Twisted
  • Programming with boost::asio

Overview: background

  • Two kinds of programs:
    • CPU-bound (no waiting)
    • I/O-bound (waiting)
  • Threading: CPU
  • Event based: I/O

Definitions...

event
(verb) an occurence requiring handling, usually with associated data (network, timer, user input, etc)
thread
(noun) a task that runs in parallel, blocking for I/O
context switch
(verb) storing/restoring the state of a thread before/after getting cpu time. expensive.

Definitions…

syncronous
(concept) a blocking call, does not return until data or error
error = read(fd, buffer);

asyncronous
(concept) a non-blocking call, returns instantly; callback happens later with data

void callback(error, buffer) { /* ... */ }
read(fd, callback);

Threaded I/O

Threaded I/O

Event-based I/O

Event-based I/O

Event-based vs. Threading

  • OS resources
  • Context-switching (latency, overhead)
  • Complexity
  • One core?! (e.g. share socket)
  • Repeatability/testing

Event-Based Programming

  • “event” → run code
  • event: timer, data received, data sent
    (network, user, disk, etc.)
  • code: “callback” errback
  • unit-test callbacks, errbacks
  • Pattern: Reactor or Proactor

demo chat server & client

  • msgpack encoding (and framing)
  • list-of-strings: ['nickname', 'insightful message']
  • tcp 10001
  • thula.meejah.ca:10001

chat server sequence

twisted

  • https://twistedmatrix.com
  • Since 2001
  • 330k Line of Code (168k is tests)
  • TCP, UDP, ICMP … SSH, HTTPS, XMPP
  • Console, serial, GPS, timers …
  • Servers or clients
  • Interop with other event-loops (Qt, gtk, pygame, pyglet, asyncio, win32
  • Many, many other batteries are included

twisted infographic

server protocol

class ChatSession(Protocol):
    def connectionMade(self):
            pass
    def connectionLost(self, reason):
            pass
    def dataReceived(self, data):
            pass

server protocol

class ChatSession(Protocol):
        def __init__(self):
            self.decoder = msgpack.Unpacker()

        def connectionMade(self):
            pass

        def connectionLost(self, reason):
            pass

        def dataReceived(self, data):
            pass
              

server protocol

class ChatSession(Protocol):
        def __init__(self):
            self.decoder = msgpack.Unpacker()

        def connectionMade(self):
            global clients  # better ways, e.g. Factory

        def connectionLost(self, reason):
            pass

        def dataReceived(self, data):
            pass
              

server protocol

class ChatSession(Protocol):
        def __init__(self):
            self.decoder = msgpack.Unpacker()

        def connectionMade(self):
            global clients  # better ways, e.g. Factory
            clients.add(self)

        def connectionLost(self, reason):
            pass

        def dataReceived(self, data):
            pass
              

server protocol

class ChatSession(Protocol):
        def __init__(self):
            self.decoder = msgpack.Unpacker()

        def connectionMade(self):
            global clients
            clients.add(self)

        def connectionLost(self, reason):
            global clients
            clients.remove(self)

        def dataReceived(self, data):
            pass
              

server protocol

class ChatSession(Protocol):
        # ...
        def dataReceived(self, data):
            pass
              

server protocol

class ChatSession(Protocol):
        # ...
        def dataReceived(self, data):
            self.decoder.feed(data)
              

server protocol

class ChatSession(Protocol):
        # ...
        def dataReceived(self, data):
            self.decoder.feed(data)
            msg = self.decoder.next()
              

server protocol

class ChatSession(Protocol):
        # ...
        def dataReceived(self, data):
            self.decoder.feed(data)
            try:
                msg = self.decoder.next()
            except StopIteration:
                print("No message yet.")
          

server protocol

class ChatSession(Protocol):
        # ...
        def dataReceived(self, data):
            self.decoder.feed(data)
            for msg in self.decoder:
                # handle msg
          

server protocol

class ChatSession(Protocol):
        # ...
        def dataReceived(self, data):
            self.decoder.feed(data)
            for msg in self.decoder:
                self.gotMessage(msg)

        def gotMessage(self, msg):
            pass
          

server protocol

class ChatSession(Protocol):
        # ...
        def dataReceived(self, data):
            self.decoder.feed(data)
            for msg in self.decoder:
                self.gotMessage(msg)

        def gotMessage(self, msg):
            packed = msgpack.packb(msg)
          

server protocol

class ChatSession(Protocol):
        # ...
        def dataReceived(self, data):
            self.decoder.feed(data)
            for msg in self.decoder:
                self.gotMessage(msg)

        def gotMessage(self, msg):
            packed = msgpack.packb(msg)
            global clients
          

server protocol

class ChatSession(Protocol):
        # ...
        def dataReceived(self, data):
            self.decoder.feed(data)
            for msg in self.decoder:
                self.gotMessage(msg)

        def gotMessage(self, msg):
            packed = msgpack.packb(msg)
            global clients
            for proto in clients:
          

server protocol

class ChatSession(Protocol):
        # ...
        def dataReceived(self, data):
            self.decoder.feed(data)
            for msg in self.decoder:
                self.gotMessage(msg)

        def gotMessage(self, msg):
            packed = msgpack.packb(msg)
            global clients
            for proto in clients:
                proto.transport.write(packed)
          

server protocol

class ChatSession(Protocol):
        def __init__(self):
            self.decoder = msgpack.Unpacker()

        def dataReceived(self, data):
            self.decoder.feed(data)
            for msg in self.decoder:
                self.gotMessage(msg)

        def gotMessage(self, msg):
            global clients
            packed = msgpack.packb(msg)
            for proto in clients:
                proto.transport.write(packed)
          

server protocol

class ChatSession(Protocol):
        def __init__(self):
            self.decoder = msgpack.Unpacker()

        def dataReceived(self, data):
            self.decoder.feed(data)
            for msg in self.decoder:
               self.gotMessage(msg)

        def gotMessage(self, msg):
            global clients
            packed = msgpack.packb(msg)
            for proto in clients:
                proto.transport.write(packed)

        def connectionMade(self):
            global clients
            clients.add(self)

        def connectionLost(self, reason):
            global clients
            clients.remove(self)
          

server protocol


class ChatFactory(Factory):
    def buildProtocol(self, addr):
        pass
              

server protocol


class ChatFactory(Factory):
    def buildProtocol(self, addr):
        return ChatSession()
              

server protocol


class ChatFactory(Factory):
    def buildProtocol(self, addr):
        return ChatSession()

def main(reactor):
              

server protocol


class ChatFactory(Factory):
    def buildProtocol(self, addr):
        return ChatSession()

def main(reactor):
    endpoint = TCP4ServerEndpoint(reactor, 10001)
              

server protocol


class ChatFactory(Factory):
    def buildProtocol(self, addr):
        return ChatSession()

def main(reactor):
    endpoint = TCP4ServerEndpoint(reactor, 10001)
    endpoint.listen(ChatFactory())
              

server protocol


class ChatFactory(Factory):
    def buildProtocol(self, addr):
        return ChatSession()

def main(reactor):
    endpoint = TCP4ServerEndpoint(reactor, 10001)
    endpoint.listen(ChatFactory())
    return Deferred()  # loop forever
              

server protocol


class ChatFactory(Factory):
    def buildProtocol(self, addr):
        return ChatSession()

def main(reactor):
    endpoint = TCP4ServerEndpoint(reactor, 10001)
    endpoint.listen(ChatFactory())
    return Deferred()  # loop forever

react(main)  # from twisted.internet.task
              

client protocol

class ChatClient(Protocol):
              

client protocol

class ChatClient(Protocol):
    def __init__(self, done):
        self.done = done
              

client protocol

class ChatClient(Protocol):
    def __init__(self, done):
        self.done = done
        self.unpacker = msgpack.Unpacker()
              

client protocol

class ChatClient(Protocol):
    def __init__(self, done):
        self.done = done
        self.unpacker = msgpack.Unpacker()

    def connectionLost(self, reason):
              

client protocol

class ChatClient(Protocol):
    def __init__(self, done):
        self.done = done
        self.unpacker = msgpack.Unpacker()

    def connectionLost(self, reason):
        self.done.callback(reason)
              

client protocol

class ChatClient(Protocol):
    def __init__(self, done):
        self.done = done
        self.unpacker = msgpack.Unpacker()

    def connectionLost(self, reason):
        self.done.callback(reason)

    def dataReceived(self, data):
              

client protocol

class ChatClient(Protocol):
    def __init__(self, done):
        self.done = done
        self.unpacker = msgpack.Unpacker()

    def connectionLost(self, reason):
        self.done.callback(reason)

    def dataReceived(self, data):
        self.unpacker.feed(data)
              

client protocol

class ChatClient(Protocol):
    def __init__(self, done):
        self.done = done
        self.unpacker = msgpack.Unpacker()

    def connectionLost(self, reason):
        self.done.callback(reason)

    def dataReceived(self, data):
        self.unpacker.feed(data)
        for msg in self.unpacker:
              

client protocol

class ChatClient(Protocol):
    def __init__(self, done):
        self.done = done
        self.unpacker = msgpack.Unpacker()

    def connectionLost(self, reason):
        self.done.callback(reason)

    def dataReceived(self, data):
        self.unpacker.feed(data)
        for msg in self.unpacker:
            print("{}: {}".format(*tuple(msg)))
              

client protocol

class ChatClient(Protocol):
    def __init__(self, done):
        self.done = done
        self.unpacker = msgpack.Unpacker()

    def connectionLost(self, reason):
        self.done.callback(reason)

    def dataReceived(self, data):
        self.unpacker.feed(data)
        for msg in self.unpacker:
            print("{}: {}".format(*tuple(msg)))

    def sendMessage(self, nick, data):
              

client protocol

class ChatClient(Protocol):
    def __init__(self, done):
        self.done = done
        self.unpacker = msgpack.Unpacker()

    def connectionLost(self, reason):
        self.done.callback(reason)

    def dataReceived(self, data):
        self.unpacker.feed(data)
        for msg in self.unpacker:
            print("{}: {}".format(*tuple(msg)))

    def sendMessage(self, nick, data):
        data = msgpack.packb([nick, msg])
              

client protocol

class ChatClient(Protocol):
    def __init__(self, done):
        self.done = done
        self.unpacker = msgpack.Unpacker()

    def connectionLost(self, reason):
        self.done.callback(reason)

    def dataReceived(self, data):
        self.unpacker.feed(data)
        for msg in self.unpacker:
            print("{}: {}".format(*tuple(msg)))

    def sendMessage(self, nick, data):
        data = msgpack.packb([nick, msg])
        self.transport.write(data)
              

user input

from twisted.protocols.basic import LineReceiver
        

user input

from twisted.protocols.basic import LineReceiver
from twisted.internet.stdio import StandardIO
        

user input

from twisted.protocols.basic import LineReceiver
from twisted.internet.stdio import StandardIO
class StandardInput(LineReceiver, StandardIO):
        

user input

from twisted.protocols.basic import LineReceiver
from twisted.internet.stdio import StandardIO
class StandardInput(LineReceiver, StandardIO):
    delimiter = os.linesep
        

user input

from twisted.protocols.basic import LineReceiver
from twisted.internet.stdio import StandardIO
class StandardInput(LineReceiver, StandardIO):
    delimiter = os.linesep
    def lineReceived(self, line):
        

user input

from twisted.protocols.basic import LineReceiver
from twisted.internet.stdio import StandardIO
class StandardInput(LineReceiver, StandardIO):
    delimiter = os.linesep
    def lineReceived(self, line):
        return self.protocol.sendMessage(self.nick, line)

        

user input

from twisted.protocols.basic import LineReceiver
from twisted.internet.stdio import StandardIO
class StandardInput(LineReceiver, StandardIO):
    delimiter = os.linesep
    def lineReceived(self, line):
        return self.protocol.sendMessage(self.nick, line)

    def __init__(self, nick, proto):
        

user input

from twisted.protocols.basic import LineReceiver
from twisted.internet.stdio import StandardIO
class StandardInput(LineReceiver, StandardIO):
    delimiter = os.linesep
    def lineReceived(self, line):
        return self.protocol.sendMessage(self.nick, line)

    def __init__(self, nick, proto):
        self.nick = nick
        self.protocol = proto
        

user input

from twisted.protocols.basic import LineReceiver
from twisted.internet.stdio import StandardIO
class StandardInput(LineReceiver, StandardIO):
    delimiter = os.linesep
    def lineReceived(self, line):
        return self.protocol.sendMessage(self.nick, line)

    def __init__(self, nick, proto):
        self.nick = nick
        self.protocol = proto

    def connectionLost(self, reason):
        

user input

from twisted.protocols.basic import LineReceiver
from twisted.internet.stdio import StandardIO
class StandardInput(LineReceiver, StandardIO):
    delimiter = os.linesep
    def lineReceived(self, line):
        return self.protocol.sendMessage(self.nick, line)

    def __init__(self, nick, proto):
        self.nick = nick
        self.protocol = proto

    def connectionLost(self, reason):
        self.protocol.transport.loseConnection()
        

Testing!

from twisted.test import proto_helpers
        

Testing!

from twisted.test import proto_helpers
import pytest
        

Testing!

from twisted.test import proto_helpers
import pytest

@pytest.fixture(scope='function')
def proto(factory):
        

Testing!

from twisted.test import proto_helpers
import pytest

@pytest.fixture(scope='function')
def proto(factory):
    transport = proto_helpers.StringTransportWithDisconnection()
        

Testing!

from twisted.test import proto_helpers
import pytest

@pytest.fixture(scope='function')
def proto(factory):
    transport = proto_helpers.StringTransportWithDisconnection()
    p = factory.buildProtocol(IPv4Address(...))
    transport.protocol = p
    p.makeConnection(transport)
    return p
        

Testing!

def test_two_messages_at_once(proto):
        

Testing!

def test_two_messages_at_once(proto):
    # set up
    test_message0 = ['ZoP', 'Beautiful is better than ugly.']
    test_message1 = ['ZoP', 'Explicit is better than implicit.']
        

Testing!

def test_two_messages_at_once(proto):
    # set up
    test_message0 = ['ZoP', 'Beautiful is better than ugly.']
    test_message1 = ['ZoP', 'Explicit is better than implicit.']
    data = msgpack.packb(test_message0)
    data += msgpack.packb(test_message1)
        

Testing!

def test_two_messages_at_once(proto):
    # set up
    test_message0 = ['ZoP', 'Beautiful is better than ugly.']
    test_message1 = ['ZoP', 'Explicit is better than implicit.']
    data = msgpack.packb(test_message0)
    data += msgpack.packb(test_message1)

    # send our test-messages over the protocol, all at once
    proto.dataReceived(data)
        

Testing!

def test_two_messages_at_once(proto):
    # ...

    # server just echos message to all connected clients
    unpacker = msgpack.Unpacker()
    unpacker.feed(proto.transport.value())
    messages = list(unpacker)
    assert len(messages) == 2
    assert messages[0] == test_message0
    assert messages[1] == test_message1
        

Testing!

def test_message_trickle(proto):
        

Testing!

def test_message_trickle(proto):
    test_message = ['albert', 'The only source of knowledge is experience.']
    data = msgpack.packb(test_message)
        

Testing!

def test_message_trickle(proto):
    test_message = ['albert', 'The only source of knowledge is experience.']
    data = msgpack.packb(test_message)

    # send our message in two chunks
    proto.dataReceived(data[:5])
        

Testing!

def test_message_trickle(proto):
    test_message = ['albert', 'The only source of knowledge is experience.']
    data = msgpack.packb(test_message)

    # send our message in two chunks
    proto.dataReceived(data[:5])
    assert proto.transport.value() == ''
        

Testing!

def test_message_trickle(proto):
    test_message = ['albert', 'The only source of knowledge is experience.']
    data = msgpack.packb(test_message)

    # send our message in two chunks
    proto.dataReceived(data[:5])
    assert proto.transport.value() == ''
    proto.dataReceived(data[5:])
        

Testing!

def test_message_trickle(proto):
    test_message = ['albert', 'The only source of knowledge is experience.']
    data = msgpack.packb(test_message)

    # send our message in two chunks
    proto.dataReceived(data[:5])
    assert proto.transport.value() == ''
    proto.dataReceived(data[5:])

    # server just echos message to all connected clients
    sent_data = proto.transport.value()
    assert msgpack.unpackb(sent_data) == test_message
        

Test Runner

$ py.test test/test_*.py
===================== test session starts =====================
platform linux2 -- Python 2.7.8 -- py-1.4.26 -- pytest-2.6.4
plugins: cov
collected 9 items

test/test_client.py ....
test/test_server.py .....

================== 9 passed in 0.04 seconds ==================

boost::asio

boost
the greatest open source c++ library ever
lots of pieces, asio is just one of them
asio
asynchronous input/output
a cross-platform C++ library for network and low-level I/O programming

asio connection sequence

simplified

asio client msgpack object

class chat_message
{
public:

  std::string nickname;
  std::string message;

  MSGPACK_DEFINE( nickname, message );
};

asio client header

using boost::system::error_code;

class posix_chat_client // equiv to txchat.ChatClient
{
};

asio client header

using boost::system::error_code;

class posix_chat_client // equiv to txchat.ChatClient
{
public:
    posix_chat_client(
        boost::asio::io_service& io_service,
        tcp::resolver::iterator endpoint_iterator,
        std::string nickname );
};

asio client header

using boost::system::error_code;

class posix_chat_client // equiv to txchat.ChatClient
{
public:
    posix_chat_client(
        boost::asio::io_service& io_service,
        tcp::resolver::iterator endpoint_iterator,
        std::string nickname );

private:
};

asio client header

using boost::system::error_code;

class posix_chat_client // equiv to txchat.ChatClient
{
public:
    posix_chat_client(
        boost::asio::io_service& io_service,
        tcp::resolver::iterator endpoint_iterator,
        std::string nickname );

private:

    void handle_connect( const error_code& ec );
};

asio client header

using boost::system::error_code;

class posix_chat_client // equiv to txchat.ChatClient
{
public:
    posix_chat_client(
        boost::asio::io_service& io_service,
        tcp::resolver::iterator endpoint_iterator,
        std::string nickname );

private:

    void handle_connect( const error_code& ec );

    void listen_on_socket();
    void cb_read_socket( const error_code& ec, std::size_t bytes_recv );
    void cb_write_socket( const error_code& ec, std::size_t length );
};

asio client header

using boost::system::error_code;

class posix_chat_client // equiv to txchat.ChatClient
{
public:
    posix_chat_client(
        boost::asio::io_service& io_service,
        tcp::resolver::iterator endpoint_iterator,
        std::string nickname );

private:

    void handle_connect( const error_code& ec );

    void listen_on_socket();
    void cb_read_socket( const error_code& ec, std::size_t bytes_recv );
    void cb_write_socket( const error_code& ec, std::size_t length );

    void listen_on_input();
    void cb_read_input( const error_code& ec, std::size_t length );

    tcp::socket m_socket;
    // other member variables etc
};

asio client constructor

posix_chat_client::posix_chat_client( asio::io_service& io_service,
                                      tcp::resolver::iterator endpoint_iterator,
                                      std::string nickname )
    : m_socket( io_service ),
      m_stdin( io_service, ::dup( STDIN_FILENO ) ),
      m_stdout( io_service, ::dup( STDOUT_FILENO ) ),
      m_input_buffer( max_msg_length )
{
    m_nickname = nickname;

    // attempt to connect to server, call handle_connect when we do
    auto handler = boost::bind( &posix_chat_client::handle_connect, this,
                                asio::placeholders::error );
    asio::async_connect( m_socket, endpoint_iterator, handler );
}

asio client connect

void posix_chat_client::handle_connect( const error_code& ec )
{
    if( error )
    {
        std::cerr << "could not connect to " << m_socket.remote_endpoint() << std::endl;
        return;
    }

    listen_on_socket();
    listen_on_input();
}

asio client listen

void posix_chat_client::listen_on_socket()
{
    // read from socket
    auto buffer = asio::buffer( m_read_buffer, 1024 );
    auto handler = boost::bind( &posix_chat_client::cb_read_socket, this,
                                asio::placeholders::error,
                                asio::placeholders::bytes_transferred );

    m_socket.async_read_some( buffer, handler );
}
void posix_chat_client::listen_on_input()
{
    // read from console until newline
    auto handler = boost::bind( &posix_chat_client::cb_read_input, this,
                                asio::placeholders::error,
                                asio::placeholders::bytes_transferred );

    asio::async_read_until( m_stdin, m_input_buffer, '\n', handler );
}

asio client read callback

void posix_chat_client::cb_read_socket( const error_code& ec,
                                        std::size_t bytes_recv )
{
    if( error ) ; // handle it and return

    if( feed_to_unpacker( m_read_buffer, bytes_recv ) )
    {
        // sync write out the message we just received, terminated by a newline.
        auto buffer = asio::buffer( output.data(), output.size() );
        asio::write( m_stdout, m_packer.buffer() );
    }

    listen_on_socket(); // read more bytes
}

asio client console callback

void posix_chat_client::cb_read_input( const error_code& ec, std::size_t length )
{
    if( error ) ; // handle it and return

    // pack up user's string

    auto buffer = asio::buffer( m_packer.data(), m_packer.size() );
    auto handler = boost::bind( &posix_chat_client::cb_write_socket, this, asio::placeholders::error, asio::placeholders::bytes_transferred );
    asio::async_write( m_socket, buffer, handler );
}

asio client write callback

void posix_chat_client::cb_write_socket( const error_code& ec, std::size_t length )
{
    if( error ) ; // handle it and return
    listen_on_input();
}

asio server session

// equiv to txchat.ChatSession
class chat_session : public std::enable_shared_from_this<chat_session>
{
public:
    typedef std::shared_ptr<chat_session> pointer;

    chat_session( tcp::socket socket, chat_room& room );
    ~chat_session();

    void deliver( const chat_message& msg );

private:
    void do_read();
    void do_write();

    tcp::socket m_socket;
    // buffers, packer and unpacker
};

asio server session manager

// equiv to "global txchat.clients"
class chat_room
{
public:

    chat_room( std::string name );

    void join( chat_session::pointer member );
    void leave( chat_session::pointer member );
    void deliver( chat_session::pointer sender, const chat_message& msg );

private:
    typedef std::set<chat_session::pointer> member_set;
    member_set      m_members;
    std::string     m_name;
};

asio server acceptor

// equiv to txchat.ChatFactory
class chat_server
{
public:
    chat_server( boost::asio::io_service& io_service,
                 const tcp::endpoint& endpoint );

private:
    void do_accept();

    tcp::acceptor m_acceptor;
    tcp::socket   m_socket;
    chat_room     m_room;
};

asio server acceptor

void chat_server::do_accept()
{
    m_acceptor.async_accept( m_socket,
    // this is a C++11 lambda (aka anonymous function)
    [this]( error_code ec )
    {
        if( ec )
        {
            TL_S_ERROR << "accept error: " << ec.message();
        }
        else
        {
            TL_S_INFO << "accepted connection from: " << m_socket.remote_endpoint();
            auto session = std::make_shared<chat_session>( std::move( m_socket ), m_room );
            session->do_read();
        }

        do_accept();
    } );
}

asio server read from socket

void chat_session::do_read()
{
    auto self( shared_from_this() );
    auto buffer = boost::asio::buffer( m_read_buff.data(), 1024 ),

    m_socket.async_read_some( buffer,
    [this, self]( error_code ec, std::size_t length )
    {
        if( ec ) ; // handle it and exit, breaking the listen chain

        // feed incoming bytes to unpacker
        // actual code is different, but this is close enough for a demo
        if( m_unpacker.feed( m_read_buff, length ) )
        {
            m_unpacker.convert( &m_msg );
            m_room.deliver( self, m_msg ); // send to other clients
        }

        do_read();
    } );
}

asio server write to socket

void chat_session::do_write()
{
    msgpack::pack( m_packer, m_msg ); // pack our msg

    auto self( shared_from_this() );
    auto buffer = boost::asio::buffer( m_packer.data(), m_packer.size() );

    boost::asio::async_write( m_socket, buffer,
    [this, self]( error_code ec, std::size_t length )
    {
        if( ec ) ; // handle it and close
        // log and increment counters
    } );
}

asio server close & cleanup

void chat_session::close()
{
    error_code ec;
    m_socket.cancel(ec);

    // this is why we have the goofy creation order
    // we need need to tell our parent to erase us
    auto self( shared_from_this() );
    m_room.leave( self );
}

asio server main

int main( int argc, char** argv )
{
    // parse command line with boost::program_options
    // setup signal handler to catch ctrl-c

    try
    {
        boost::asio::io_service ios;

        for( auto port : opts["ports"].as< std::vector<unsigned> >() )
        {
            tcp::endpoint endpoint( tcp::v4(), port );
            servers.emplace_back( ios, endpoint );
        }

        ios.run(); // this blocks "forever"
    }
    catch( std::exception& e )
    {
        cerr << "there was a catastrophic failure: " << e.what() << endl;
        return 1;
    }

    return 0;
}

C++ "extras"

  • using boost::logging for... logging
  • using boost::program_options to parse command line
  • msgpack example code
  • boost::asio with C++11 example code
  • hammer client mixes threads and events

Performance

server 8-core Xeon E3 @3.4GHz w/16GiB RAMz
clients 32
send delay 1ms
msg size 21 bytes
total messages ≈56M
C++ msg/sec ≈1M
PyPy msg/sec ≈1M

Demo

Twisted server
thula.meejah.ca:10001
C++ server
polyglot.burgundywall.com:10001
# Twisted client
$ txchat client --nick meejah
$ txchat client --nick meejah --endpoint tcp:polyglot.burgundywall.com:10001

# C++ client
$ client kurt thula.meejah.ca 10001
$ client kurt polyglot.burgundywall.com 10001

        
      

THE END

Thanks in part to..

You Guys!

(and polyglot)