I have client and server app. They use udp socket to communicate.
I can send some message from client side and receive it at server. But I can't send message in other direction.
I modify client and server boost example.
My server:
#include <cstdlib>
#include <iostream>
#include <string>
#include <boost/asio.hpp>
using boost::asio::ip::udp;
enum { max_length = 1024 };
int main(int argc, char* argv[]) {
try {
if (argc != 2) {
std::cerr << "<port>\n";
return 1;
}
boost::asio::io_context io_context;
int port = std::atoi(argv[1]);
udp::endpoint endpoint(udp::v4(), port);
udp::socket *socket = new udp::socket(io_context, endpoint);
std::cout << "starting loop\n";
while(true) {
#ifdef GOOD_RIRECTION
char data[max_length];
size_t len = socket->receive(boost::asio::buffer(data, max_length));
std::cout << "Recieved: "<< std::string(data, len) << '\n';
#else
std::cout << "Enter message: ";
char request[max_length];
std::cin.getline(request, max_length);
size_t request_length = std::strlen(request);
socket->send(boost::asio::buffer(request, request_length));
#endif
}
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
My client:
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <boost/asio.hpp>
using boost::asio::ip::udp;
enum { max_length = 1024 };
int main(int argc, char* argv[]) {
try {
if (argc != 3) {
std::cerr << "<host> <port>\n";
return 1;
}
boost::asio::io_context io_context;
udp::socket *socket = new udp::socket(io_context);
udp::resolver resolver(io_context);
udp::resolver::results_type endpoints = resolver.resolve(udp::v4(), argv[1], argv[2]);
socket->connect(*endpoints.begin());
std::cout << "starting loop\n";
while (true) {
#ifdef GOOD_RIRECTION
std::cout << "Enter message: ";
char request[max_length];
std::cin.getline(request, max_length);
size_t request_length = std::strlen(request);
socket->send(boost::asio::buffer(request, request_length));
#else
char data[max_length];
size_t len = socket->receive(boost::asio::buffer(data, max_length));
std::cout << "Recieved: "<< std::string(data, len) << '\n';
#endif
}
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
where GOOD_RIRECTION is defined everything works fine.
When it is not - I got send: Destination address required exception on first send.
As far as I understand sockets are direction independent so I do something during establish connection. But I can't understand what exactly.
update:
From comments I understand that there is no "connection" in udp.
I build my client twice (with and without GOOD_RIRECTION define).I tried use one to send message and another to receive. In such situation I didn't receive first message and got send: Connection refused on sending second message.
Related
Able to send UDP message to a particular IP port using Poco Lib socket communication, But unable to receive the UDP message as it is getting stuck at receiveFrom API of DatagramSocket as in below code.
I am sending message every second and also have to receive acknowledgement every second, for that i have timer , Client and Server Threads running parallelly. The problem here is I am unable to receive the UDP packets which are being captured on wireshark. It is getting stuck at receiveFrom.
Please find below Client Server and main files.
` Server.hpp
#pragma once
#include "Poco/Net/StreamSocket.h"
#include "Poco/Net/DatagramSocket.h"
#include "Poco/Net/SocketAddress.h"
#include "Poco/Net/MulticastSocket.h"
#include "Poco/RunnableAdapter.h"
#include "Poco/Thread.h"
#include <cstring>
#include <iostream>
using namespace std;
using namespace Poco;
using namespace Poco::Net;
struct Server
{
int bufferSize;
SocketAddress sockets;
static bool debugModeEnabled;
Server() :
bufferSize(1024) { //sockets = SocketAddress(10000);
}
Server(const UInt16& port, const int& bufferSize)
{
sockets = SocketAddress(port);
this->bufferSize = bufferSize;
}
void receiveMessages()
{
char buffer[bufferSize];
try
{
Poco::Net::DatagramSocket datagram(sockets);//(socket);
datagram.bind(sockets);
cout << "Server started socket" << endl;
while (!datagram.available())
{
SocketAddress sender;
cout << "Server started socket 2" << endl;
int size = datagram.receiveFrom(buffer, bufferSize, sender);
//int size = datagram.receiveBytes(buffer, bufferSize);
cout << "received bytes size" << size << endl;
buffer[size] = '\0';
//std::string str(buffer);
//cout << (debugModeEnabled ? (sender.toString() + ": ") : "- ") << buffer << endl;
cout << "received: " << size << buffer << endl;
//cout << buffer << "Server adasdasd" << endl;
if (string(buffer) == "\\end")
{
//cerr << "\nUser: " << sender.toString() << " ended connection" << endl;
datagram.close(); // Closes the server
}
}
}
catch (const Poco::Exception& exc)
{
std::cerr << exc.displayText() << std::endl;
}
}
};
bool Server::debugModeEnabled = false;
`
`Client.hpp
#pragma once
#include "Poco/Net/DatagramSocket.h"
#include "Poco/Net/SocketAddress.h"
#include "Poco/RunnableAdapter.h"
#include "Poco/Thread.h"
#include <iostream>
#include <string>
using namespace std;
using namespace Poco;
using namespace Poco::Net;
struct Client
{
SocketAddress socket;
string str;
// By default the client connects to itself
Client() { socket = SocketAddress("127.0.0.1", 10000); }
Client(const Poco::Net::IPAddress& IP, const UInt16& port, const string& val) :
str(val)
{
socket = SocketAddress(IP, port);
}
void sendMessages()
{
DatagramSocket datagram;
datagram.connect(socket);
string message = str;
//cout << "sending: " << hex << hexify(message) << endl;
unsigned int bytes_sent = 0;
while (!datagram.available())
{
//getline(cin, message);
//bytes_sent = datagram.sendBytes(message.data(), static_cast<int>(message.size()));
bytes_sent = datagram.sendTo(message.data(), static_cast<int>(message.size()),socket);
cout << "number of bytes sent: " << std::dec << bytes_sent << endl;
if (bytes_sent >= message.size())
{
datagram.close();
}
}
}
string IP() { return socket.host().toString(); }
UInt16 port() { return socket.port(); }
static void sendMessage(const Poco::Net::IPAddress& IP, const UInt16& port, const string& message)
{
SocketAddress socket(IP, port);
DatagramSocket datagram;
datagram.connect(socket);
datagram.sendBytes(message.data(), int(message.size()));
}
};
`
` main.cpp
int bufferSize = 1024;
int exit_status = 0;
Client client(IP, ciPort, str);
Server server(mdilPort, bufferSize);
RunnableAdapter<Client> clientRunnable(client, &Client::sendMessages);
RunnableAdapter<Server> serverRunnable(server, &Server::receiveMessages);
Thread clientThread, serverThread;
// Client::sendMessage(IP, ciPort, "hello!!");
try
{
Timer t = Timer();
t.setInterval([&]() {
cout << "client Tick" << endl;
// pApp->SendIndications();
clientThread.start(clientRunnable);
clientThread.join();
},
1000);
t.setInterval([&]() {
cout<< "server Tick" << endl;
serverThread.start(serverRunnable);
serverThread.join();
},
1000);
t.setTimeout([&]() {
std::cout << "Hey.. After 30s. But I will stop the timer!" << std::endl;
t.stop();
exit(exit_status);
},
30000);
std::cout << "I am Timer" << std::endl;
while (true); // Keep main thread active
}
catch (...)
{
std::cout << "catched exception" << std::endl;
//return -1;
}
`
I tried the conventional Socket Programming API's to receive the UDP packets but there also it is getting stuck at receiveFrom API. also tried running both client and server on different process to make sure there is no issue with the multi threading synchronization, but both the approach didnt help. I am able to capture the response at Wireshark but unable to receive on the application side using Poco Lib socket API's. Also allowed visual studio code through firewall as well
When I run:
nc -ul <ip address> <port>
I receive messages from the udp server. However when I try to reproduce the equivalent using the below C++ program with boost it just freezes on receive_from:
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::udp;
int main(int argc, char *argv[]) {
try {
if (argc != 3) {
std::cerr << "Usage: client <host> <port>" << std::endl;
return 1;
}
boost::asio::io_context ioContext;
udp::resolver resolver(ioContext);
udp::resolver::query query(udp::v4(), argv[1], argv[2]);
udp::endpoint endpoint = *resolver.resolve(query);
udp::socket socket(ioContext);
socket.open(udp::v4());
// boost::array<char, 1> sendBuf = {0};
// socket.send_to(boost::asio::buffer(sendBuf), endpoint);
char letter = 0;
udp::endpoint sender_endpoint;
while(socket.receive_from(boost::asio::buffer(&letter, 1), endpoint)) {
std::cout << letter;
}
}
catch (std::exception &e) {
std::cerr << e.what() << std::endl;
}
return 0;
}
Additionally I'm just modeling the above code off of the boost udp client code in their tutorial seen here:
//
// client.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::udp;
int main(int argc, char* argv[])
{
try
{
if (argc != 2)
{
std::cerr << "Usage: client <host>" << std::endl;
return 1;
}
boost::asio::io_context io_context;
udp::resolver resolver(io_context);
udp::endpoint receiver_endpoint =
*resolver.resolve(udp::v4(), argv[1], "daytime").begin();
udp::socket socket(io_context);
socket.open(udp::v4());
boost::array<char, 1> send_buf = {{ 0 }};
socket.send_to(boost::asio::buffer(send_buf), receiver_endpoint);
boost::array<char, 128> recv_buf;
udp::endpoint sender_endpoint;
size_t len = socket.receive_from(
boost::asio::buffer(recv_buf), sender_endpoint);
std::cout.write(recv_buf.data(), len);
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
I have a suspicion that the tutorial code just doesn't work for my case? Not sure what's wrong or what's the difference. What's the right way to make a standard udp echo client?
I tested the following code successfully receives message. udp::resolver in your code is not necessary since you're not sending any message, but receiving.
When opening a socket in your case, you should designate a port number in which UDP is expecting data. This can be done by passing a second argument to udp::socket. The second example in your post, however, does not require to do so, because it is reusing its endpoint - sender_endpoint.
receive_from() takes its second argument to store sender's endpoint. You should not pass your peer's endpoint, but a variable to keep where the message came from.
int main(int argc, char* argv[]) {
try {
if (argc != 3) {
std::cerr << "Usage: client <host> <port>" << std::endl;
return 1;
}
boost::asio::io_context ioContext;
udp::socket socket(ioContext, udp::endpoint(udp::v4(), 6078)); // designated port
char letter = 0;
udp::endpoint ep_sender;
while (socket.receive_from(boost::asio::buffer(&letter, 1), ep_sender)) {
std::cout << letter;
//std::cout << ep_sender.address().to_string() << endl; // sender's address.
}
return 0;
}
catch (std::exception& e) {
std::cerr << e.what() << std::endl;
}
}
Im trying to make the client and server using UDP. I need two threeds in client: one for sending one message every 50 milliseconds and second for sending some managing messages. After around 5-6 minutes of work, one of them stops. But i dont know why? Im using sniffer (Wireshark) to control the packets, and didnt notice anything unusual.
Here is my code client:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <chrono>
#include <thread>
using boost::asio::ip::udp;
class UDP_Client
{
private:
boost::asio::io_service service;
udp::endpoint endPoint;
std::string message;
public:
UDP_Client() {}
UDP_Client(std::string host, int port)
{
endPoint = udp::endpoint(boost::asio::ip::address::from_string(host), port);
}
~UDP_Client() {}
static std::string transformToCharCodes(std::string msg)
{
std::string outMess;
for(int i = 0; i < msg.length(); i++)
{
char letter = msg[i];
outMess += static_cast<char>(atoi(&letter));
}
return outMess;
}
void cyclicInterrogation()
{
int const PORT = 50000;
try
{
while(true) {
std::string messageBuffer = message;
udp::socket socket(service, udp::endpoint(udp::v4(), PORT));
std::string outMess = transformToCharCodes(messageBuffer);
socket.send_to(boost::asio::buffer(outMess), endPoint);
char recievedBuffer[1024];
udp::endpoint sender_endpoint;
boost::system::error_code error;
int lenght = socket.receive_from(boost::asio::buffer(recievedBuffer), sender_endpoint, 0, error);
if(error && error != boost::asio::error::message_size) {
throw boost::system::system_error(error);
}
std::string copy(recievedBuffer, lenght);
std::cout << "Recieved: ";
for(int i = 0; i < lenght; i++)
{
std::cout << static_cast<int>(recievedBuffer[i]) << " ";
}
std::cout << std::endl;
socket.close();
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
}
catch(std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
system("pause");
}
}
void autoSending()
{
int const PORT = 50001;
while(true) {
std::string msgArray[4] = {"456", "5", "", "5"};
for(int i = 0; i < sizeof(msgArray) / sizeof(msgArray[0]); i++)
{
sendMessage(msgArray[i], PORT);
boost::this_thread::sleep(boost::posix_time::millisec(500));
}
}
}
void userInputSendnig()
{
int const PORT = 50002;
while(true)
{
getline(std::cin, message);
sendMessage(message, PORT);
}
}
void sendMessage(std::string msg, int port)
{
std::string messageBuffer;
char recievedBuffer[1024];
message = messageBuffer = msg;
udp::socket sock(service, udp::endpoint(udp::v4(), port));
std::string outMess = transformToCharCodes(messageBuffer);
sock.send_to(boost::asio::buffer(outMess), endPoint);
udp::endpoint sender_ep;
boost::system::error_code error;
sock.receive_from(boost::asio::buffer(recievedBuffer), sender_ep, 0, error);
if(error && error != boost::asio::error::message_size) {
throw boost::system::system_error(error);
}
sock.close();
}
};
int main(int argc, char* argv[])
{
setlocale(LC_ALL, "Russian");
std::string const IP_ADDRESS = "192.168.0.20";
int const PORT = 2000;
UDP_Client client(IP_ADDRESS, PORT);
boost::thread thread1(boost::bind(&UDP_Client::cyclicInterrogation, &client));
boost::thread thread2(boost::bind(&UDP_Client::autoSending, &client));
boost::thread thread3(boost::bind(&UDP_Client::userInputSendnig, &client));
thread1.join();
thread2.join();
thread3.join();
}
Code of server:
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/udp.hpp>
#include <iostream>
#include <locale.h>
using boost::asio::ip::udp;
boost::asio::io_service service;
void handle_connections()
{
try
{
char buff[1024];
udp::socket sock(service, udp::endpoint(udp::v4(), 2000));
while(true)
{
udp::endpoint sender_ep;
//sender_ep.address(boost::asio::ip::address::from_string("192.168.0.20"));
int length = sock.receive_from(boost::asio::buffer(buff), sender_ep);
std::string msg(buff, length);
sock.send_to(boost::asio::buffer(msg), sender_ep);
std::cout << "Recieved from client: " << msg << std::endl;
}
}
catch(std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
system("pause");
}
}
int main(int argc, char* argv[])
{
setlocale(LC_ALL, "rus");
handle_connections();
}
I don't know what i did wrong ...
I have implemented some connection classes, using Boost ASIO, to replace some low level C code in an application, and everything is working great, except for one problem.
Basically, I have a UdpConnection class that does synchronous read and write, but it uses async methods to handle time-outs as per the boost examples. The problem is I can't figure out how to make it threadsafe.
I have tried adding strands to the event handlers to make this class threadsafe (code below), but that isn't working. I suspect it is because of the way timeout is implemented. I have included my code in 4 classes in pastebin.
Single threaded is working fine. I also have TcpConnection and UnixSocketConnection classes that don't need to be shared amongst multiple threads and they work fine. However, I can't get multi-threaded UDP code to work.
Am I missing something?
Connection.h && AsioConnection.h http://pastebin.com/Cbbw37gL
UdpConnection.h && UdpConnection.cpp http://pastebin.com/VLnHBnPs
EDIT Attaching code as suggested:
AsioConnection.h
/*
* AsioConnection.h
*
* Created on: 2011-04-08
* Author: cdunphy
*
* All classes that want to use the ASIO io_service
* and deadline timers will want to subclass this.
*/
#ifndef ASIOCONNECTION_H_
#define ASIOCONNECTION_H_
#include "Connection.h"
#include <boost/shared_ptr.hpp>
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <boost/bind.hpp>
namespace shaw_rsc
{
/*
* This exception throws if there is a timeout when connecting
* to a remote socket.
*/
struct SocketTimeoutException : public std::runtime_error
{
SocketTimeoutException(const std::string& msg) : std::runtime_error(msg)
{ }
}
;
/*
* This is the root class of every Connection
* class that wants to make use of boost asio.
*/
class AsioConnection : public Connection
{
public:
AsioConnection(
int c_timeout,
int r_timeout
) : Connection(),
conn_timeout_int(c_timeout),
read_timeout_int(r_timeout),
conn_timeout(c_timeout),
read_timeout(r_timeout),
io_service(),
strand(io_service),
deadline(strand.get_io_service()),
error()
{
reset_deadline();
}
const boost::system::error_code& getError() const
{
return error;
}
int get_read_timeout() const
{
return read_timeout_int;
}
int get_conn_timeout() const
{
return conn_timeout_int;
}
/*
* These are the callback handlers for our asynchronous
* IO operations.
*/
void handle_write(const boost::system::error_code& ec,
std::size_t len,
boost::system::error_code* out_ec,
std::size_t* out_len)
{
*out_ec = ec;
*out_len = len;
}
/*
* These are the callback handlers for our asynchronous
* IO operations.
*/
void handle_send(const boost::system::error_code& ec,
std::size_t len,
boost::system::error_code* out_ec,
std::size_t* out_len)
{
*out_ec = ec;
*out_len = len;
}
void handle_read(const boost::system::error_code& ec,
std::size_t len,
boost::system::error_code* out_ec,
std::size_t* out_len)
{
*out_ec = ec;
*out_len = len;
}
void handle_receive(const boost::system::error_code& ec,
std::size_t len,
boost::system::error_code* out_ec,
std::size_t* out_len)
{
*out_ec = ec;
*out_len = len;
}
void handle_connect(const boost::system::error_code& ec,
boost::system::error_code* out_ec)
{
*out_ec = ec;
}
protected:
int conn_timeout_int;
int read_timeout_int;
boost::posix_time::seconds conn_timeout;
boost::posix_time::seconds read_timeout;
boost::asio::io_service io_service;
boost::asio::strand strand;
boost::asio::deadline_timer deadline;
boost::system::error_code error;
void reset_deadline()
{
// No deadline is required until the first socket operation is started. We
// set the deadline to positive infinity so that the actor takes no action
// until a specific deadline is set.
deadline.expires_at(boost::posix_time::pos_infin);
}
};
}
#endif /* ASIOCONNECTION_H_ */
Connection.h
/*
* Connection.h
*
* Created on: 2011-02-25
* Author: cdunphy
*/
#ifndef CONNECTION_H_
#define CONNECTION_H_
#include <vector>
#include <string>
#include <sstream>
#include <stdexcept>
#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>
namespace shaw_rsc
{
class Connection;
const std::size_t BUF_SIZE = 128;
/*
* This is the type of reference we will
* provide to the clients.
*/
typedef boost::shared_ptr<Connection> ConnPtr;
typedef std::vector<char> DataBuffer;
typedef DataBuffer::iterator DB_Iter;
typedef DataBuffer::const_iterator DB_CIter;
// This is the mode we are using for the connection
enum Mode {
CLIENT,
SERVER
};
/*
* This is a generic class that allows data to be read or
* written to using a connection. This is quite abstract
* and it can be used both for file operations and for
* network operations.
*/
class Connection
{
public:
Connection() { }
virtual ~Connection() { }
/*
* This method writes the current contents of the data buffer
* to the connected resource. Be sure to set the right data
* in the buffer by calling the setData method first.
*
* The number of bytes written is returned.
*/
virtual std::size_t write(const DataBuffer& data) = 0;
/*
* This method reads data from the connected resource and stores
* it in our data buffer which we pass in by reference.
* Please note that it clears whatever data was in the buffer prior to
* reading.
*
* The number of bytes read is returned.
*/
virtual std::size_t read(DataBuffer& data) = 0;
virtual const std::string str() const = 0;
};
inline std::vector<unsigned char> convert_data_to_unsigned(const DataBuffer& data)
{
return std::vector<unsigned char>(data.begin(), data.end());
}
inline std::string dataBufferToStr(const DataBuffer& data)
{
return std::string(data.begin(), data.end());
}
}
#endif /* CONNECTION_H_ */
UdpConnection.h
/*
* UdpConnection.h
*
* Created on: 2011-02-25
* Author: cdunphy
*/
// Portions Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef DATAGRAMCONNECTION_H_
#define DATAGRAMCONNECTION_H_
#include "AsioConnection.h"
#include <boost/lexical_cast.hpp>
namespace shaw_rsc
{
struct UdpException: public std::runtime_error
{
UdpException(const std::string& msg) : std::runtime_error(msg) { }
};
/*
* This is the concrete class that manages UDP connections.
*/
class UdpConnection: public AsioConnection
{
public:
/*
* Use this constructor for clients (connecting to a remote socket).
*/
UdpConnection(
const std::string& _host,
const std::string& _port,
int r_timeout,
Mode mode
) : AsioConnection(0, r_timeout),
socket(strand.get_io_service()),
remote_endpoint(),
host(_host),
port(_port)
{
check_deadline();
connect(mode);
}
std::size_t write(const DataBuffer& data);
std::size_t read(DataBuffer& data);
const std::string str() const;
private:
void connect(Mode mode);
void check_deadline();
boost::asio::ip::udp::socket socket;
boost::asio::ip::udp::endpoint remote_endpoint;
std::string host;
std::string port;
};
}
#endif /* DATAGRAMCONNECTION_H_ */
UdpConnection.cpp
/*
* UdpConnection.cpp
*
* Created on: 2011-02-25
* Author: cdunphy
*/
#include "UdpConnection.h"
using std::string;
using std::endl;
using std::stringstream;
using std::exception;
using boost::asio::buffer;
using boost::asio::ip::udp;
using boost::system::error_code;
using boost::system::system_error;
using boost::asio::deadline_timer;
using boost::bind;
using boost::lexical_cast;
namespace shaw_rsc
{
size_t UdpConnection::write(const DataBuffer& data)
{
size_t bytes_written = 0;
/*
* Check to see if the socket is bad before writing
*/
if (error &&
error.value() != boost::asio::error::operation_aborted &&
error.value() != boost::asio::error::timed_out &&
error != boost::asio::error::try_again)
throw UdpException(error.message());
socket.async_send_to(buffer(data), remote_endpoint,
strand.wrap(bind(&AsioConnection::handle_send, this, _1, _2,
&error, &bytes_written)));
do
{
strand.get_io_service().run_one();
}
while (error == boost::asio::error::would_block
|| error == boost::asio::error::try_again || bytes_written == 0);
if (error)
{
if (error.value() == boost::asio::error::operation_aborted
|| error.value() == boost::asio::error::timed_out)
throw SocketTimeoutException(error.message());
else
throw UdpException(error.message());
}
reset_deadline();
return bytes_written;
}
size_t UdpConnection::read(DataBuffer& data)
{
/*
* Check to see if the socket is bad before writing
*/
if (error &&
error.value() != boost::asio::error::operation_aborted &&
error.value() != boost::asio::error::timed_out &&
error != boost::asio::error::try_again)
throw UdpException(error.message());
data.clear();
/*
* Reset the deadline timer to expire according
* to the configured read timeout.
*/
deadline.expires_from_now(read_timeout);
size_t bytes_read = 0;
boost::array<char, BUF_SIZE> buff;
error = boost::asio::error::would_block;
socket.async_receive_from(buffer(buff), remote_endpoint,
strand.wrap(boost::bind(&AsioConnection::handle_receive, this, _1, _2, &error,
&bytes_read)));
do
{
strand.get_io_service().run_one();
}
while (error == boost::asio::error::would_block ||
error == boost::asio::error::try_again || bytes_read == 0);
/*
* Check for errors after the read.
*/
if (error)
{
if (error.value() == boost::asio::error::operation_aborted
|| error.value() == boost::asio::error::timed_out)
throw SocketTimeoutException(error.message());
else
throw UdpException(error.message());
}
else
data.insert(data.end(), buff.begin(), buff.begin() + bytes_read);
// Reset the deadline timer so we can leave this socket open as long
// as we want.
reset_deadline();
return bytes_read;
}
void UdpConnection::connect(Mode mode)
{
socket.open(boost::asio::ip::udp::v4());
if (mode == SERVER)
{
socket.bind(
udp::endpoint(udp::v4(),
lexical_cast<int>(port)), error);
}
else if (mode == CLIENT)
{
udp::resolver resolver(strand.get_io_service());
udp::resolver::query query(udp::v4(), host, port);
remote_endpoint = *resolver.resolve(query, error);
}
}
void UdpConnection::check_deadline()
{
// Check whether the deadline has passed. We compare the deadline against
// the current time since a new asynchronous operation may have moved the
// deadline before this actor had a chance to run.
if (deadline.expires_at() <= deadline_timer::traits_type::now())
{
// The deadline has passed. The outstanding asynchronous operation needs
// to be cancelled so that the blocked receive() function will return.
//
// Please note that cancel() has portability issues on some versions of
// Microsoft Windows, and it may be necessary to use close() instead.
// Consult the documentation for cancel() for further information.
socket.cancel();
// There is no longer an active deadline. The expiry is set to positive
// infinity so that the actor takes no action until a new deadline is set.
reset_deadline();
}
// Put the actor back to sleep.
deadline.async_wait(strand.wrap(boost::bind(&UdpConnection::check_deadline, this)));
}
/*
* This member function is good for diagnostic purposes
*/
const string UdpConnection::str() const
{
stringstream sstr;
sstr << "Host: " << host << endl;
sstr << "Port: " << port << endl;
sstr << "Read timeout: " << read_timeout_int << endl;
sstr << "Remote Endpoint Address: " << remote_endpoint.address().to_string()
<< endl;
sstr << "Remote Endpoint Port: " << remote_endpoint.port() << endl;
try
{
sstr << "Socket Remote Endpoint Address: "
<< socket.remote_endpoint().address().to_string() << endl;
sstr << "Socket Remote Endpoint Port: "
<< socket.remote_endpoint().port() << endl;
}
catch (exception& e)
{ }
try
{
sstr << "Socket Local Endpoint Address: "
<< socket.local_endpoint().address().to_string() << endl;
sstr << "Socket Local Endpoint Port: " << socket.local_endpoint().port()
<< endl;
}
catch (exception& e)
{ }
return sstr.str();
}
}
EDIT2:
Here is the test code I am trying to get working:
Server that replies in C++. All tests are working EXCEPT the threaded Udp test:
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE TestLibRSCAsio
#include <cstdio>
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <iostream>
#include <string>
#include <exception>
#include <sstream>
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <boost/date_time.hpp>
#include <boost/thread.hpp>
#include <boost/lexical_cast.hpp>
#include "boost/date_time/posix_time/posix_time.hpp"
#include <rsc_asio/Connection.h>
#include <rsc_asio/TcpConnection.h>
#include <rsc_asio/UdpConnection.h>
#include <rsc_asio/UnixSocketConnection.h>
#include "Util.h"
#include "sha1/Sha1Calc.h"
#include "servers/TestTcpServer.h"
#include <boost/test/unit_test.hpp>
using std::vector;
using std::string;
using std::size_t;
using std::cerr;
using std::cout;
using std::endl;
using std::flush;
using std::exception;
using std::time;
using std::stringstream;
using boost::lexical_cast;
using boost::thread;
using boost::mutex;
using boost::unique_lock;
using namespace shaw_rsc;
const size_t TCP_BYTE_SZ = 1000000;
const size_t UDP_BYTE_SZ = 64;
const std::string FILE_SOCKET = "/tmp/rofl";
const std::string SERVER_HOST = "0.0.0.0";
const std::string SERVER_PORT = "10999";
const std::string EXPECTED_UDP_REQUEST = "GOT_ANSWER?";
const int TIMEOUT = 3;
const int THREAD_TIMEOUT = 10;
DataBuffer write_data(ConnPtr client, Sha1Calc& sha1calc, size_t size, size_t iter)
{
unique_lock<mutex>(global_mutex);
cout << "Iter: " << iter << endl;
DataBuffer data = getRandomData(size);
sha1calc.calc_client_digest(data);
size_t bytes_written = client->write(data);
cout << "Wrote " << bytes_written << " -> " << dataBufferToStr(data) << " to socket: " << endl << client->str() << endl;
return data;
}
void write_data_threaded(ConnPtr client, Sha1Calc& sha1calc, size_t size, size_t iter)
{
cout << "Iter: " << iter << endl;
DataBuffer data = getRandomData(size);
sha1calc.calc_client_digest(data);
size_t bytes_written = client->write(data);
cout << "Wrote " << bytes_written << " -> " << dataBufferToStr(data) << " to socket: " << endl << client->str() << endl;
}
DataBuffer read_data(ConnPtr server, Sha1Calc& sha1calc, size_t iter)
{
cout << "Iter: " << iter << endl;
DataBuffer data;
size_t bytes_read = server->read(data);
cout << "Read " << bytes_read << " -> " << dataBufferToStr(data) << " from socket: " << endl << server->str() << endl;
sha1calc.calc_server_digest(data);
return data;
}
/*
* This is a suite of tests to provide unit tests
* for the RRE.
*/
BOOST_AUTO_TEST_SUITE(TestLibRSCAsioSuite)
BOOST_AUTO_TEST_CASE(TcpTest)
{
boost::asio::io_service io_service;
cout << endl << "**** TCP Test ****" << endl;
Sha1Calc sha1calc;
cout << endl << "Generating " << TCP_BYTE_SZ << " bytes of data to serve up." << endl;
DataBuffer dataToServe = getRandomData(TCP_BYTE_SZ);
sha1calc.calc_server_digest(dataToServe);
cout << "SHA1 hash of server data: " <<
sha1_to_str(sha1calc.get_server_digest()) << endl;
SrvPtr server(new TestTcpServer(std::atoi(SERVER_PORT.c_str()), dataToServe, io_service));
server->start();
try
{
// Fire up a basic TCP client for testing
cout << "Firing up TCP client on port: " << SERVER_PORT << endl;
DataBuffer clientData;
ConnPtr client(new TcpConnection(SERVER_HOST, SERVER_PORT, TIMEOUT, TIMEOUT, io_service));
size_t bytesRead = client->read(clientData);
BOOST_REQUIRE( bytesRead == TCP_BYTE_SZ );
BOOST_REQUIRE( clientData.size() == TCP_BYTE_SZ );
sha1calc.calc_client_digest(clientData);
BOOST_REQUIRE( sha1calc.compare() );// SHA-1 hashes better matctype filter texth
}
catch (SocketTimeoutException& e)
{
cerr << "Socket timeout: " << e.what() << endl;
BOOST_FAIL("Socket Timeout");
}
catch (const TcpException& e)
{
cerr << "TCP Error: " << e.what() << endl;
BOOST_FAIL("TCP Exception");
}
catch (const exception& e)
{
cerr << "Other Error: " << e.what() << endl;
BOOST_FAIL("Unknown Exception");
}
}
BOOST_AUTO_TEST_CASE(UdpTest)
{
boost::asio::io_service io_service;
std::stringstream error;
try
{
cout << endl << "**** UDP Test ****" << endl;
ConnPtr client(new UdpConnection(SERVER_HOST, SERVER_PORT, TIMEOUT, CLIENT, io_service));
ConnPtr server(new UdpConnection(SERVER_HOST, SERVER_PORT, TIMEOUT, SERVER, io_service));
for (int i = 0; i != 10; ++i)
{
Sha1Calc sha1calc;
// Write the data to the client
DataBuffer clientData = write_data(client, sha1calc, UDP_BYTE_SZ, i);
// Read the data from the server
DataBuffer serverData = read_data(server, sha1calc, i);
// Make sure the client data matches the server data
BOOST_REQUIRE( sha1calc.compare() );
cout << endl; // new-line
}
}
catch (const SocketTimeoutException& e)
{
error << "Socket timeout: " << e.what() << endl;
BOOST_FAIL(error.str());
}
catch (const UdpException& e)
{
error << "UDP Exception: " << e.what() << endl;
BOOST_FAIL(error.str());
}
catch (const exception& e)
{
error << "Other Error: " << e.what() << endl;
BOOST_FAIL(error.str());
}
}
BOOST_AUTO_TEST_CASE(UdpThreadTest)
{
boost::asio::io_service io_service;
std::stringstream error;
try
{
cout << endl << "**** UDP Multi-thread Test ****" << endl;
ConnPtr server(new UdpConnection(SERVER_HOST, SERVER_PORT, THREAD_TIMEOUT, SERVER, io_service));
Sha1Calc sha1calc;
for (int i = 0; i != 10; ++i)
{
// Read the data from the server, make sure it matches
// the expected request?
DataBuffer serverData = read_data(server, sha1calc, i);
BOOST_REQUIRE(dataBufferToStr(serverData) == EXPECTED_UDP_REQUEST);
// Repply on the remote socket
thread t1(bind(&write_data_threaded, server, sha1calc, UDP_BYTE_SZ, i));
cout << endl; // new-line
}
}
catch (const SocketTimeoutException& e)
{
error << "Socket timeout: " << e.what() << endl;
BOOST_FAIL(error.str());
}
catch (const UdpException& e)
{
error << "UDP Exception: " << e.what() << endl;
BOOST_FAIL(error.str());
}
catch (const exception& e)
{
error << "Other Error: " << e.what() << endl;
BOOST_FAIL(error.str());
}
}
BOOST_AUTO_TEST_CASE(UnixSocketTest)
{
boost::asio::io_service io_service;
try
{
cout << endl << "**** UNIX Socket Test ****" << endl;
std::remove(FILE_SOCKET.c_str());
ConnPtr server(new UnixSocketConnection(FILE_SOCKET, TIMEOUT, SERVER, io_service));
ConnPtr client(new UnixSocketConnection(FILE_SOCKET, TIMEOUT, CLIENT, io_service));
Sha1Calc sha1calc;
DataBuffer clientData = write_data(client, sha1calc, UDP_BYTE_SZ, 0);
cout << "Wrote the data to the Unix Socket client:" << dataBufferToStr(clientData) << endl;
DataBuffer serverData = read_data(server, sha1calc, 0);
cout << "Read from UDP Server: " << dataBufferToStr(serverData) << endl;
BOOST_REQUIRE( sha1calc.compare() );
cout << sha1_to_str(sha1calc.get_server_digest()) << endl;
}
catch (const SocketTimeoutException& e)
{
cerr << "Socket timeout: " << e.what() << endl;
BOOST_FAIL("Socket Timeout");
}
catch (const UnixSocketException& e)
{
cerr << "UNIX Socket Error: " << e.what() << endl;
BOOST_FAIL("UNIXSocket Exception");
}
catch (const exception& e)
{
cerr << "Other Error: " << e.what() << endl;
BOOST_FAIL("Unknown Exception");
}
std::remove(FILE_SOCKET.c_str());
}
BOOST_AUTO_TEST_SUITE_END()
}
Client written in Java:
package com.shaw.udp.sender;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
public class Client {
public static void main(String[] args) throws Exception {
DatagramChannel channel = DatagramChannel.open();
SocketAddress address = new InetSocketAddress(0);
SocketAddress client = new InetSocketAddress(SERVER_HOST, 10999);
DatagramSocket socket = channel.socket();
// This is the local socket
socket.setSoTimeout(5000);
socket.bind(address);
for (int i = 0; i != 10; ++i) {
// Send the data to the remote server
ByteBuffer buffer = ByteBuffer.wrap("GOT_ANSWER?".getBytes());
channel.send(buffer, client);
System.out.println("Iter: " + i + " => Sent request: "
+ new String(buffer.array()));
// Listen for reply from the server
buffer = ByteBuffer.allocate(64);
channel.receive(buffer);
System.out.println("Iter: " + i + " => Got reply: "
+ new String(buffer.array()));
}
}
}
I learned C++ and now I would like to move on and learn some network programming. I decided to use boost::asio because it's multiplatform. I wrote this simple program:
client:
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
enum { max_length = 1000000 };
int main(int argc, char* argv[])
{
while(1)
{
try
{
if (argc != 3)
{
std::cerr << "Usage: blocking_tcp_echo_client <host> <port>\n";
return 1;
}
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(tcp::v4(), argv[1], argv[2]);
tcp::resolver::iterator iterator = resolver.resolve(query);
tcp::socket s(io_service);
s.connect(*iterator);
using namespace std; // For strlen.
std::cout << "Enter message: ";
char request[max_length];
std::cin.getline(request, max_length);
if (request == "\n")
continue;
size_t request_length = strlen(request);
boost::asio::write(s, boost::asio::buffer(request, request_length));
char reply[max_length];
boost::system::error_code error;
size_t reply_length = s.read_some(boost::asio::buffer(reply), error);
if (error == boost::asio::error::eof)
break; // Connection closed cleanly by peer.
else if (error)
throw boost::system::system_error(error); // Some other error.
std::cout << "Reply is: ";
std::cout.write(reply, reply_length);
std::cout << "\n";
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
exit(1);
}
}
return 0;
}
server:
#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/smart_ptr.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/regex.hpp>
#include <boost/lexical_cast.hpp>
#include <string>
using boost::asio::ip::tcp;
const int max_length = 1000000;
std::string user_array[100];
typedef boost::shared_ptr<tcp::socket> socket_ptr;
unsigned short analyze_user_request(std::string& user_request, short unsigned* ID, std::string* request_value)
{
// function returns:
// 0: if user request is incorrect
// 1: if user requests "PUT" operation
// 2: if user requests "GET" operation
// Furthermore, if request is correct, its value (i.e. ID number and/or string) is saved to short unsigned and string values passed by pointers.
boost::regex exp("^[[:space:]]*(PUT|GET)[[:space:]]+([[:digit:]]{1,2})(?:[[:space:]]+(.*))?$");
boost::smatch what;
if (regex_match(user_request, what, exp, boost::match_extra))
{
short unsigned id_number = boost::lexical_cast<short unsigned>(what[2]);
if (what[1] == "PUT")
{
boost::regex exp1("^[a-zA-Z0-9]+$");
std::string value = boost::lexical_cast<std::string>(what[3]);
if (value.length() > 4095)
return 0;
if (!regex_match(value, exp1))
return 0;
else
{
*request_value = value;
*ID = id_number;
return 1;
}
}
if (what[1] == "GET")
{
*ID = id_number;
return 2;
}
}
if (!regex_match(user_request, what, exp, boost::match_extra))
return 0;
}
void session(socket_ptr sock)
{
try
{
for (;;)
{
char data[max_length];
boost::system::error_code error;
size_t length = sock->read_some(boost::asio::buffer(data), error);
if (error == boost::asio::error::eof)
break; // Connection closed cleanly by peer.
else if (error)
throw boost::system::system_error(error); // Some other error.
// convert buffer data to string for further procession
std::string line(boost::asio::buffers_begin(boost::asio::buffer(data)), boost::asio::buffers_begin(boost::asio::buffer(data)) + length);
std::string reply; // will be "QK", "INVALID", or "OK <value>"
unsigned short vID;
unsigned short* ID = &vID;
std::string vrequest_value;
std::string* request_value = &vrequest_value;
unsigned short output = analyze_user_request(line, ID, request_value);
if (output == 1)
{
// PUT
reply = "OK";
user_array[*ID] = *request_value;
}
else if (output == 2)
{
// GET
reply = user_array[*ID];
if (reply == "")
reply = "EMPTY";
}
else
reply = "INVALID";
boost::system::error_code ignored_error;
size_t ans_len=reply.length();
boost::asio::write(*sock, boost::asio::buffer(reply));
}
}
catch (std::exception& e)
{
std::cerr << "Exception in thread: " << e.what() << "\n";
}
}
void server(boost::asio::io_service& io_service, short port)
{
tcp::acceptor a(io_service, tcp::endpoint(tcp::v4(), port));
for (;;)
{
socket_ptr sock(new tcp::socket(io_service));
a.accept(*sock);
boost::thread t(boost::bind(session, sock));
}
}
int main(int argc, char* argv[])
{
try
{
if (argc != 2)
{
std::cerr << "Usage: blocking_tcp_echo_server <port>\n";
return 1;
}
boost::asio::io_service io_service;
using namespace std; // For atoi.
server(io_service, atoi(argv[1]));
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
Basically, it's an application that allows user to store data on server. User can insert new data using PUT command followed by ID number and data value, and retrieve data using GET command followed by ID. User requests are processed in analyze_user_request function and are subsequently written to or read from array. The problem is that now all clients are using the same global arry. That means that if one client saves something under particular ID all other clients can read it, because they access the same array. I wonder, how can I associate array with different clients, and create a new array when a new client connects?
What about encapsulating session data into a class and create separate session object for each connection. Approximately it can look like this:
Session class definition:
class Session {
public:
// logic from your session function
void handleRequests(socket_ptr sock);
private:
// session data here
}
typedef boost::shared_ptr<Session> SessionPtr;
In "server" function in accept loop create new object and pass it to new thread:
SessionPtr newSession(new Session());
boost::thread acceptThread(boost::bind(&Session::handleRequests, newSession, sock));
Sorry for possible mistakes in code, I am far from my development environment it can not test it.
For more elegant solution for handling several connections separatly see boost::asio example "Chat server": http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/example/chat/chat_server.cpp