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;
}
}
Related
I am trying to connect to a secure websocket using asio.
This example will work for an ip address:
#include <iostream>
#include <asio.hpp>
int main() {
asio::error_code ec;
asio::io_context context;
asio::io_context::work idleWork(context);
asio::ip::tcp::endpoint endpoint(asio::ip::make_address("51.38.81.49", ec), 80);
asio::ip::tcp::socket socket(context);
socket.connect(endpoint, ec);
if (!ec) {
std::cout << "Connected!" << std::endl;
} else {
std::cout << "Failed to connect to address: \n" << ec.message() << std::endl;
}
return 0;
}
but how would I change it so I connect to "wss://api2.example.com"?
EDIT:
Thanks for your answer karastojko - it seems to get me some of the way. I would though like to know if I am actually connected to the server, so I have updated my example with your input, added a working WSS which I know will answer and created read and write.
#include <asio.hpp>
#include <asio/ts/buffer.hpp>
std::vector<char> vBuffer(1 * 1024);
// This should output the received data
void GrabSomeData(asio::ip::tcp::socket& socket) {
socket.async_read_some(asio::buffer(vBuffer.data(), vBuffer.size()),
[&](std::error_code ec, std::size_t lenght) {
if (!ec) {
std::cout << "\n\nRead " << lenght << " bytes\n\n";
for (int i = 0; i < lenght; i++)
std::cout << vBuffer[i];
GrabSomeData(socket);
}
}
);
}
int main() {
asio::error_code ec;
asio::io_context context;
asio::io_context::work idleWork(context);
std::thread thrContext = std::thread([&]() { context.run(); });
// I hope this is what you meant
asio::ip::tcp::resolver res(context);
asio::ip::tcp::socket socket(context);
asio::connect(socket, res.resolve("echo.websocket.org", std::to_string(443)));
// Check the socket is open
if (socket.is_open()) {
// Start to output incoming data
GrabSomeData(socket);
// Send data to the websocket, which should be sent back
std::string sRequest = "Echo";
socket.write_some(asio::buffer(sRequest.data(), sRequest.size()), ec);
// Wait some time, so the data is received
using namespace std::chrono_literals;
std::this_thread::sleep_for(20000ms);
context.stop();
if (thrContext.joinable()) thrContext.join();
}
return 0;
}
For that purpose use the resolver class:
tcp::resolver res(context);
asio::ip::tcp::socket socket(context);
boost::asio::connect(socket, res.resolve("api2.example.com", 80));
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.
I am following the Introduction to Sockets boost::asio tutorial here, called a A synchronous TCP daytime client. I have copied the code exactly, but then moved them into Server.cpp and Client.cpp.
Server.cpp
#include <ctime>
#include <iostream>
#include <string>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
std::string make_daytime_string()
{
std::time_t now = time(0);
return ctime(&now);
}
int main()
{
try {
std::cout << "Initiating server..." << std::endl;
boost::asio::io_service io;
tcp::acceptor acceptor (io, tcp::endpoint(tcp::v4(), 8889));
for (;;) {
tcp::socket socket (io);
acceptor.accept(socket);
std::string message = make_daytime_string();
boost::system::error_code ignored_error;
boost::asio::write(socket, boost::asio::buffer(message), ignored_error);
}
}
catch (std::exception & e) {
std::cerr << e.what() << std::endl;
}
return 0;
}
Client.cpp
#include <boost/array.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
int main(int argc, char * argv[])
{
boost::asio::io_service io;
// Daytime
try {
if (argc != 2) {
std::cerr << "Usage: client <host>" << std::endl;
return 1;
}
tcp::resolver resolver (io);
tcp::resolver::query query (argv[1], "daytime");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::socket socket (io);
boost::asio::connect(socket, endpoint_iterator);
for (;;) {
boost::array<char, 128> buffer;
boost::system::error_code error;
size_t len = socket.read_some(boost::asio::buffer(buffer), 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.write(buffer.data(), len);
}
}
catch (std::exception & e) {
std::cerr << e.what() << std::endl;
}
return 0;
}
First I run the server:
$ ./server
Initiating server...
Then I run the client:
$ ./client localhost
connect: Connection refused
Since I am brand new to sockets and boost, unfortunately I am stuck on finding a solution to this connection refused error message.
Your server is running on port 8889.
Your client connects on port 13 (a.k.a. "daytime").
This will not work. For the obvious reason.
Note if you do decide to run the server on port 13, you need administrative privileges for that.
I wonder how to implement a synchronous socket accept with boost which can be terminated.
To demonstrate my problem I slightly modified the synchonous tcp echo example.
Note: the provided code seems to be working on Windows platforms but i'm having problems on a Linux machine.
Let's say the server receives a quit message and now wants to terminate an endless loop which accepts new connections.
Most tutorials etc. recommend you to run acceptor->close() in this case. But as
this post states, the results might be undefined if close() is called from another thread.
If you do so, accept() won't terminate this time but when another client tries to connect it returnes an error (on Linux!)
So my question again: how do I correctly terminate a server which is based on boost::asio which continuously synchronously accepts connections?
Here the code:
#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/smart_ptr.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
using boost::asio::ip::tcp;
void session(boost::shared_ptr<tcp::socket> sock, tcp::acceptor *acceptor )
{
try {
for (;;) {
char data[ 1024 ];
boost::system::error_code error;
size_t length = sock->read_some(boost::asio::buffer(data), error);
if (error == boost::asio::error::eof) { break; }
else if (error) { throw boost::system::system_error(error); }
if( std::string("quit") == data ) { // TRY TO CANCEL THE ACCEPT
acceptor->close();
break;
}
boost::asio::write(*sock, boost::asio::buffer(data, length));
}
}
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 (;;) {
boost::shared_ptr<tcp::socket> sock(new tcp::socket(io_service));
boost::system::error_code error;
a.accept( *sock, error );
if( !error ) {
boost::thread t( boost::bind( session, sock, &a ) );
}
else {
std::cout << "acceptor canceled "<< error << std::endl;
break;
}
}
}
int main(int argc, char* argv[])
{
try{
// ..check args here...
boost::asio::io_service io_service;
server(io_service, std::atoi(argv[1]));
}
catch (std::exception& e) {std::cerr << "Exception: " << e.what() << "\n";}
return 0;
}
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