[Guide] Network programming with boost - quickstart

08/13/2012 08:29 KraHen#1
This will be a really really short article from me, as I saw that some of you have problems using the networking library that boost::asio offers. I wouldn`t use this in an actual server, but it should be a somewhat good starting point for anyone who wants to get into the C++/ASIO combination.

For those of you who don`t know what boost::asio or boost is, I strongly recommend you to google it, download it, and use it whenever possible, as it has very efficient stuff in it. :) C++0x is going to implement boost anyway, so why not use it now?

Okay, so boost::asio is THE networking and I/O programming module. I have tried other libraries, which were praised by fellow game developers, for example ENet, but quickly realized that boost can do so much more than networking which I am going to need anyway, that I`d better use it for that as well. Also, boost implements sockets in a cross-platform way, using the best multiplexing method on the OS, for example, as far as I know, on Win32 it uses overlapped I/O. I might be wrong on this one.

So, let`s get started! If you still haven`t downloaded and configured boost, shame on you. Your first task in this case is to read and follow this article.

[Only registered and activated users can see links. Click Here To Register...]

Done? Great!

First of all, let me quote [Only registered and activated users can see links. Click Here To Register...] great article from gamedev (which you should definitely read if you want to go beyond this simple example) :
Quote:
The core object of boost::asio is io_service. This object is like the brain and the heart of the library. We will start out with a simple example to get acquainted with it. In this example, we will be calling the run member function. If we check the function's documentation, "the run() function blocks until all work has finished and there are no more handlers to be dispatched, or until the io_service has been stopped.
This is basically how my example works. The main routine creates this io_service, binds the listener to this service, then runs the service. Since it always has something to do, it shouldn`t exit, and your main thread will be dedicated to the server. Yep. After calling io_service.run() you can`t really do anything else in the main function.

Enough with the jibberish! Let`s see the actual code :

SocketLayer.h
Quote:
PHP Code:
#ifndef CSOCKETLAYER_H_
#define CSOCKETLAYER_H_

#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ip/tcp.hpp>

#include "AuthClient.h"

using boost::asio::ip::tcp;
#define MAX_LENGTH 1024

class NetworkSession
{
private:
    
tcp::socket m_Socket;
    
char m_Data[MAX_LENGTH];

    
void handleRead(const boost::system::error_codeerrorsize_t bytes_transferred)
    {
        if (!
error)
        { 
            
this->Wrapper->HandleData(m_Databytes_transferred);
        }
        else
        {
            
delete this;
        }
    }
    
void handle_write(const boost::system::error_codeerror)
    {
        if (!
error)
        { 
            
        }
        else
        {
            
delete this;
        }
    }
public:
    
CAuthClient Wrapper;
    
NetworkSession(boost::asio::io_service io_s)
        : 
m_Socket(io_s) { }

    
tcp::socketSocket()
    {
        return 
m_Socket;
    }

    
void Enable()
    {
        
m_Socket.set_option(tcp::no_delay(true)); // VERY IMPORTANT!
        
m_Socket.async_read_some(boost::asio::buffer(m_DataMAX_LENGTH),
            
boost::bind(&NetworkSession::handleReadthis,
            
boost::asio::placeholders::error,
            
boost::asio::placeholders::bytes_transferred));
    }
};

class 
TCPServer
{
private:
    
boost::asio::io_serviceio_service_;
    
tcp::acceptor acceptor_
    
void handleAccept(NetworkSessionnew_session, const boost::system::error_codeerror
    {
        if (!
error)
        { 
            
new_session->Enable();
            
new_session = new NetworkSession(io_service_);

            
CAuthClient new_client = new CAuthClient(new_session);
            
new_session->Wrapper new_client;

            
acceptor_.async_accept(new_session->Socket(), boost::bind(&TCPServer::handleAcceptthis,
                
new_sessionboost::asio::placeholders::error));
        }
        else
        {
            
delete new_session;
        }
    }
public:
    
TCPServer(boost::asio::io_serviceio_serviceshort port): io_service_(io_service),
      
acceptor_(io_servicetcp::endpoint(tcp::v4(), port))
    {
        
NetworkSessiontemp = new NetworkSession(io_service_);
        
acceptor_.async_accept(temp->Socket(),
            
boost::bind(&TCPServer::handleAcceptthistempboost::asio::placeholders::error));
    }
}; 
That actually is simpler IMO than C#`s async socket usage! (say what??)

Here`s how you would start the server (ofc. you need the right includes as well) :

PHP Code:
try
    {
        
boost::asio::io_service io_service;
        
TCPServer s(io_service9958);
        
io_service.run();
    }
    catch (
std::exceptione)
    {
        
std::cerr << "Exception: " << e.what() << "\n";
    } 
Now, if you go through the code, you can see that CAuthClient represents an auth client implementation for Conquer, also if you examine the code you can see where the receive events and accept events happen, you could add delegates there, I don`t use that method since I have my nice little UML diagram of my classes and I don`t get lost in them. (hint)

When the disconnects happen, nothing happens, at least nothing visual. You can easily see where you should add the disconnect event(s) though. :)

If you figured out how the server works, you might wanna check the boost/cstdin.hpp, which includes fixed length variables, such as boost::uint8_t (=byte), boost::uint16_t(=ushort/UInt16), etc.

Feel free to ask any questions, but if you don`t just copy and paste, but using the code implement the thing yourself, everything should be pretty obvious.
08/13/2012 08:52 InfamousNoone#2
Gash, this is why I hate C++. In C++ standards, you could say that's "elegant" code, but compared to everything else I just want to vomit at how unclean it looks. A well written guide though (from the perspective of someone who has little experience with boost it was very easy to understand).

Also, isn't that a bit of a conflict -- i.e. boost::uint8_t, and then there's stdint.h's uint8_t, etc.
^ Another reason I dislike C++, too many types for the same purpose.
08/13/2012 10:37 KraHen#3
Including boost/cstdint.h includes the default cstdint if the compiler suite has it, opposed to VC++ in some cases, so there isn`t really an ambiguity. :)

Btw, I still favor the C++ style opposed to the Java one, which for me feels even dirtier. C# rocks though.
08/27/2012 09:24 xmen01235#4
I'm not a fluent c++ programmer so I will try to make a dll for this in c++ and try to implement that dll in the C sharp environment? I have some problem also on my socket wrapper which I think can be resolved by this library.
08/28/2012 00:37 bone-you#5
Very nice guide.
08/28/2012 02:13 CptSky#6
Quote:
Originally Posted by KraHen View Post
Including boost/cstdint.h includes the default cstdint if the compiler suite has it, opposed to VC++ in some cases, so there isn`t really an ambiguity. :)

Btw, I still favor the C++ style opposed to the Java one, which for me feels even dirtier. C# rocks though.
Generic typedef that will use stdint if available, else use compiler's fixed-length type, else try to find the good one. I prefer it over using boost. Else, it looks nice. ( It was what I asked you long time ago? :P )

PHP Code:
/**
 * ****** BARLab - Open Source *****
 * Copyright (C) 2011-2012 BARLab
 * Copyright (C) 2011-2012 Jean-Philippe Boivin
 *
 * Please read the WARNING, DISCLAIMER and PATENTS
 * sections in the LICENSE file.
 */

#ifndef _ORG_BARLAB_TYPEDEF_H_
#define _ORG_BARLAB_TYPEDEF_H_

// Define NULL, size_t
#include <stddef.h>

/*
*****************************************************
* nullptr definition
****************************************************
*/

#if !defined(nullptr) // C++11 defines nullptr
#define nullptr NULL
#endif // !defined(nullptr)

/*
*****************************************************
* Fixed-width integer definitions
****************************************************
*/

#if defined(BL_HAS_STDINT_H)
#include <stdint.h> // Will define intN_t
#else

#if _MSC_VER >= 1400 // MSVS 2005 and higher have fixed-width integers

typedef __int8            int8_t;
typedef unsigned __int8   uint8_t;
typedef __int16           int16_t;
typedef unsigned __int16  uint16_t;
typedef __int32           int32_t;
typedef unsigned __int32  uint32_t;
typedef __int64           int64_t;
typedef unsigned __int64  uint64_t;

#else

// char          is 8-bit
// short int     is at least 16-bit
// int           is at least 16-bit
// long int      is at least 32-bit
// long long int is at least 64-bit (C++11)

#if 1 == sizeof(char)
typedef signed char             int8_t;
typedef unsigned char           uint8_t;
#else
#error "8-bit integers not found!"
#endif // 1 == sizeof(char)

#if 2 == sizeof(short) // At least 16-bit
typedef signed short int        int16_t;
typedef unsigned short int      uint16_t;
#elif 2 == sizeof(int) // At least 16-bit
typedef signed int              int16_t;
typedef unsigned int            uint16_t;
#else
#error "16-bit integers not found!"
#endif // 2 == sizeof(short)

#if 4 == sizeof(int) // At least 16-bit
typedef signed int              int32_t;
typedef unsigned int            uint32_t;
#elif 4 == sizeof(long int) // At least 32-bit
typedef signed long int         int32_t;
typedef unsigned long int       uint32_t;
#else
#error "32-bit integers not found!"
#endif // 4 == sizeof(int)

#if 8 == sizeof(long int) // At least 32-bit
typedef signed long int         int64_t;
typedef unsigned long int       uint64_t;
#elseif defined(BL_HAS_LONG_LONG_INT)
#if 8 == sizeof(long long int) // At least 64-bit
typedef signed long long int    int64_t;
typedef unsigned long long int  uint64_t;
#endif // 8 == sizeof(long long int)
#else
#error "64-bit integers not found!"
#endif // 8 == sizeof(long int)

#endif // _MSC_VER >= 1400

#endif // BL_HAS_STDINT_H

#endif // _ORG_BARLAB_TYPEDEF_H 

Quote:
Originally Posted by xmen01235 View Post
I'm not a fluent c++ programmer so I will try to make a dll for this in c++ and try to implement that dll in the C sharp environment? I have some problem also on my socket wrapper which I think can be resolved by this library.
A C++ socket system through DLLImport will have a big overhead. You should use C# network libraries.
08/28/2012 07:22 I don't have a username#7
This is great bro :) Thanks
08/28/2012 20:27 KraHen#8
@CptSky : Let`s say this was it and I just found it, although that version was a bit longer. Never managed to add it to the repo lol. :)