Reputation: 15010
I am new to C++ boost library.I have managed to implement a UDP server and client using boost asio library. Currently in my example program I start the UDP server and then attempt to connect using UDP client. Once the client connects and sends some data the server responds with an randomly generated string which I have converted to hex and printing it out.Once it receives the string the UDP client call the destruct-or and exits.
My code is given below udp_server.hpp and udp_client.hpp
#include "udp_server.hpp"
#include <iostream>
#include <exception>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <algorithm>
#include <sstream>
#include <iomanip>
const int ARG_COUNT = 2;
const int LOWEST_PORT = 1024;
const int HIGHEST_PORT = 65000;
static char message_array[8192];
void gen_random_string(char *s, const int len)
{
static const char alphanum[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
for (int i = 0; i < len; ++i) {
s[i] = alphanum[rand() % (sizeof(alphanum) - 1)];
}
s[len] = 0;
}
class udp_server
{
public:
udp_server(boost::asio::io_service& io_service,int port_number)
: socket_(io_service, boost::asio::ip::udp::udp::endpoint(boost::asio::ip::udp::udp::v4(), port_number))
{
std::cout << "UDP server listening on " << port_number << std::endl;
start_receive();
}
private:
void start_receive()
{
socket_.async_receive_from(
boost::asio::buffer(recv_buffer_), remote_endpoint_,
boost::bind(&udp_server::handle_receive, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_receive(const boost::system::error_code& error,
std::size_t /*bytes_transferred*/)
{
if (!error || error == boost::asio::error::message_size)
{
gen_random_string(message_array, 8192);
boost::shared_ptr<std::string> message(new std::string(message_array));
socket_.async_send_to(boost::asio::buffer(*message), remote_endpoint_,
boost::bind(&udp_server::handle_send, this, message,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
start_receive();
}
}
void handle_send(boost::shared_ptr<std::string> /*message*/,
const boost::system::error_code& /*error*/,
std::size_t /*bytes_transferred*/)
{
}
boost::asio::ip::udp::udp::socket socket_;
boost::asio::ip::udp::udp::endpoint remote_endpoint_;
boost::array<char, 1> recv_buffer_;
};
void runUDPServer( CmdLineOpts input )
{
try
{
boost::asio::io_service io_service;
udp_server server(io_service,input.port);
io_service.run();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
class udp_client
{
public:
udp_client(
boost::asio::io_service& io_service,
const std::string& host,
const std::string& port
) : io_service_(io_service), socket_(io_service, boost::asio::ip::udp::udp::endpoint(boost::asio::ip::udp::udp::v4(), 0)) {
boost::asio::ip::udp::udp::resolver resolver(io_service_);
boost::asio::ip::udp::udp::resolver::query query(boost::asio::ip::udp::udp::v4(), host, port);
boost::asio::ip::udp::udp::resolver::iterator iter = resolver.resolve(query);
endpoint_ = *iter;
}
~udp_client()
{
std::cout << "Calling UDP client destructor" << std::endl;
socket_.close();
}
void send() {
socket_.send_to(boost::asio::buffer(send_buf), endpoint_);
}
void recieve_from() {
/*Initialize our endpoint*/
boost::array<unsigned char, 8192> temp;
// boost::asio::buffer boost_buf(temp);
size_t len = socket_.receive_from(
boost::asio::buffer(temp), sender_endpoint);
std::ostringstream ss;
ss << std::hex << std::uppercase << std::setfill( '0' );
std::for_each( temp.cbegin(), temp.cend(), [&]( int c ) { ss << std::setw( 2 ) << c; } );
std::string result = ss.str();
std::cout << "Length of recieved message " << len << std::endl;
std::cout << result << std::endl;
}
private:
boost::asio::io_service& io_service_;
boost::asio::ip::udp::udp::socket socket_;
boost::asio::ip::udp::udp::endpoint endpoint_;
//boost::array<char, 2048> recv_buf;
std::vector<unsigned char> recv_buf;
boost::array<char, 1> send_buf = {{ 0 }};
boost::asio::ip::udp::endpoint sender_endpoint;
};
void runUDPClient(std::string portStr)
{
try
{
boost::asio::io_service io_service;
udp_client client(io_service, "localhost", portStr);
client.send();
client.recieve_from();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
void runClient( CmdLineOpts input )
{
runUDPClient(input.portStr);
}
void runServer( CmdLineOpts input )
{
runUDPServer(input);
}
/**
* Usage: client_server <protocol> <port> <num of packets>
*/
bool clarg_parse ( int argc, char *argv[], CmdLineOpts *input )
{
bool result = true;
if (argc - 1 == ARG_COUNT)
{
// arg 1: server or client
int arg1 = std::stoi(argv[1]);
if (arg1 == 0 || arg1 == 1)
{
input->servOrClient = arg1;
}
else
{
std::cout << "Invalid client server choice.\nUsage: client_server <client (0) or server(1)> <port>" << std::endl;
result = false;
}
// arg 2: port
int arg2 = std::stoi(argv[3]);
if (arg2 > LOWEST_PORT && arg2 < HIGHEST_PORT )
{
input->port = arg2;
input->portStr = argv[3];
}
else
{
std::cout << "Invalid port, must be between " << LOWEST_PORT << " and " << HIGHEST_PORT << std::endl;
std::cout << "Usage: client_server <client (0) or server(1)> <port>" << std::endl;
result = false;
}
}
else
{
std::cout << "Usage: client_server <client (0) or server(1)> <port>" << std::endl;
result = false;
}
return result;
}
int main ( int argc, char *argv[] )
{
CmdLineOpts input;
if (clarg_parse(argc, argv, &input))
{
if(input.servOrClient == 1)
{
runServer(input);
}
else if(input.servOrClient == 0)
{
runClient(input);
}
}
else
{
return 1;
}
return 0;
}
The header file udp_server.hpp
#ifndef UDP_SERVER_H_INCLUDED
#define UDP_SERVER_H_INCLUDED
#include <string>
struct CmdLineOpts
{
std::string portStr;
int port;
int servOrClient;
};
void runUDPServer ( CmdLineOpts input );
bool clarg_parse ( int argc, char *argv[], CmdLineOpts input );
#endif
Makefile to compile the above program
TARGET = udp_server
LIBS = -lboost_system -lpthread
CXX = g++
CXXFLAGS = -std=c++11 -g -Wall -pedantic
.PHONY: default all clean
default: $(TARGET)
all: default
OBJECTS = $(patsubst %.cpp, %.o, $(wildcard *.cpp))
HEADERS = $(wildcard *.hpp)
%.o: %.cpp $(HEADERS)
$(CXX) $(CXXFLAGS) -c $< -o $@
.PRECIOUS: $(TARGET) $(OBJECTS)
$(TARGET): $(OBJECTS)
$(CXX) $(OBJECTS) $(LIBS) -o $@
clean:
-rm -f *.o
-rm -f $(TARGET)
My question is as follows.
1) Is the design good for sending network packets.My concern is that it seems the packet is sent(i.e the server responds) when the client sends some data to the server. In other words the client needs to poll the server periodically to query the data. Is there some other model where the server informs the client that data is available ? Would that be a better design?
2) In the example I am assigning an array of 8192 bytes for both client and server. Is that required. As I understand the MTU for UDP (as well as for TCP is 1500 Bytes). Is there any reason to assign an array of more than 1500 bytes at both client and server.?
It would be really great if someone could answer the above questions.
Upvotes: 0
Views: 4046
Reputation: 51961
When designing the I/O layer for an application protocol, consider the expected network topography, the application protocol requirements, any service-level agreements, hardware requirements, etc. Design around satisfying those constraints, rather than putting too much effort in optimizing for small efficiencies. Without knowing many more details, it will be subjective to answer what design is good or if other designs are better. Nevertheless:
For a given layer, the maximum transmission unit (MTU) defines the maximum size of the protocol unit which may be passed onwards to the next layer. Higher-level layers and protocols may introduce fragmentation handling, allowing the max size of a given layer's protocol unit to exceed the lower layer's MTU. For instance, while the Ethernet frame's MTU is 1500 bytes, the max size for UDP datagram's (Layer 4: Transport) payload is 65507 bytes. This is possible because IP Layer packets (Layer 3: Network) may be constructed from one or more Ethernet frames (Layer 2: Data Link).
The size of the buffer one should use is often dependent on the application protocol. For example, the Asio Chat example uses buffers that are 516 bytes because the application protocol has a max length of 516 bytes. The fragmentation and reassemble of lower layer protocol units will be transparent to the application. However, as UDP provides neither acknowledgment nor retransmission, and the loss of part of a datagram will cause the entire datagram to be discarded, larger datagrams have a greater chance of being loss due to greater fragmentation.
Upvotes: 1