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) :
Enough with the jibberish! Let`s see the actual code :
SocketLayer.h
Here`s how you would start the server (ofc. you need the right includes as well) :
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.
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) :
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.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.
Enough with the jibberish! Let`s see the actual code :
SocketLayer.h
That actually is simpler IMO than C#`s async socket usage! (say what??)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_code& error, size_t bytes_transferred)
{
if (!error)
{
this->Wrapper->HandleData(m_Data, bytes_transferred);
}
else
{
delete this;
}
}
void handle_write(const boost::system::error_code& error)
{
if (!error)
{
}
else
{
delete this;
}
}
public:
CAuthClient * Wrapper;
NetworkSession(boost::asio::io_service & io_s)
: m_Socket(io_s) { }
tcp::socket& Socket()
{
return m_Socket;
}
void Enable()
{
m_Socket.set_option(tcp::no_delay(true)); // VERY IMPORTANT!
m_Socket.async_read_some(boost::asio::buffer(m_Data, MAX_LENGTH),
boost::bind(&NetworkSession::handleRead, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
};
class TCPServer
{
private:
boost::asio::io_service& io_service_;
tcp::acceptor acceptor_;
void handleAccept(NetworkSession* new_session, const boost::system::error_code& error)
{
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::handleAccept, this,
new_session, boost::asio::placeholders::error));
}
else
{
delete new_session;
}
}
public:
TCPServer(boost::asio::io_service& io_service, short port): io_service_(io_service),
acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
{
NetworkSession* temp = new NetworkSession(io_service_);
acceptor_.async_accept(temp->Socket(),
boost::bind(&TCPServer::handleAccept, this, temp, boost::asio::placeholders::error));
}
};
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_service, 9958);
io_service.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
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.