I'm not able to succeed about boost-asio multithread program.
Since there is not any good example or documentation about this,
I want your help :)
Simply, I think this code do listen, but when I want to 'cout' buffer data,
it does not print anything or listening once and stopped.
My code is:
void Worker::startThread(int clientNumber) {
cout << "listening: "<< clients[clientNumber]->port << endl;
boost::asio::io_service io_service;
tcp::acceptor acc(io_service, tcp::endpoint(tcp::v4(),portNumber[clientNumber]));
socket_ptr sock(new tcp::socket(io_service));
acc.accept(*sock);
try
{
for (;;) {
char data[max_length];
boost::system::error_code error;
cout << "message?" << endl;
size_t length = sock->read_some(boost::asio::buffer(data), error);
cout << "message :)" << endl;
cout << data << endl;
if(error == boost::asio::error::eof)
break; // Connection closed cleanly by peer.
else if (error)
throw boost::system::system_error(error); // Some other error.
}
}
catch (std::exception& e)
{
std::cerr << "Exception in thread: " << e.what() << "\n";
}
}
void Worker::start() {
cout << "Starting thread server" << endl;
for(int i=0; i<clients.size(); i++) {
boost::thread t(boost::bind(&Worker::startThread, this, i));
}
for(;;);
}
You haven't looked at the documentation very long if you don't see the multi-threaded examples
HTTP Server 3
An HTTP server using a single
io_service and a thread pool calling
io_service::run().
HTTP Server 2
An HTTP server using an
io_service-per-CPU design.
Keep in mind these examples use asynchronous methods, which is where the Boost.Asio library really shines.
You've basically copied the Blocking TCP Echo Server example yet you're unable to find a good example or documentation?
Anyway, I see some problems with your code:
Your saying your listening on clients[clientNumber]->port but the actual port you're listening on is portNumber[clientNumber];
You need to zero-terminate your data after read_some and before printing it;
As soon as the error == boost::asio::error::eof condition is true (the client disconnected) the thread will exit and therefore you'll not be able to (re)connect another client on that port;
You're only accepting the first connection / client, any other clients connecting on the same port will not have their messages handled in any way.
Related
I'm converting an application from using Juce asynchronous i/o to asio. The first part is to rewrite the code that receives traffic from another application on the same machine (it's a Lightroom Lua plugin that sends \n delimited messages on port 58764). Whenever I successfully connect to that port with my C++ program, I get a series of error codes, all the same:
An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full.
Can someone point out my error? I can see that the socket is successfully opened. I've reduced this from my full program to a minimal example. I also tried it with connect instead of async_connect and had the same problem.
#include <iostream>
#include "asio.hpp"
asio::io_context io_context_;
asio::ip::tcp::socket socket_{io_context_};
void loop_me()
{
asio::streambuf streambuf{};
while (true) {
if (!socket_.is_open()) {
return;
}
else {
asio::async_read_until(socket_, streambuf, '\n',
[&streambuf](const asio::error_code& error_code, std::size_t bytes_transferred) {
if (error_code) {
std::cerr << "Socket error " << error_code.message() << std::endl;
return;
}
// Extract up to the first delimiter.
std::string command{buffers_begin(streambuf.data()),
buffers_begin(streambuf.data()) + bytes_transferred};
std::cout << command << std::endl;
streambuf.consume(bytes_transferred);
});
}
}
}
int main()
{
auto work_{asio::make_work_guard(io_context_)};
std::thread io_thread_;
std::thread run_thread_;
io_thread_ = std::thread([] { io_context_.run(); });
socket_.async_connect(asio::ip::tcp::endpoint(asio::ip::address_v4::loopback(), 58764),
[&run_thread_](const asio::error_code& error) {
if (!error) {
std::cout << "Socket connected in LR_IPC_In\n";
run_thread_ = std::thread(loop_me);
}
else {
std::cerr << "LR_IPC_In socket connect failed " << error.message() << std::endl;
}
});
std::this_thread::sleep_for(std::chrono::seconds(1));
socket_.close();
io_context_.stop();
if (io_thread_.joinable())
io_thread_.join();
if (run_thread_.joinable())
run_thread_.join();
}
You are trying to start an infinite number of asynchronous read operations at the same time. You shouldn't start a new asynchronous read until the previous one finished.
async_read_until returns immediately, even though the data hasn't been received yet. That's the point of "async".
I am learning about boost and was messing around with its server and client communication to make a simple chat server, where anything that a client sends, is just displayed on the server. The server itself doesn't send anything and starts the receiving part. It is pretty straight-forward.
Server code:
#include <boost\asio\placeholders.hpp>
#include <boost\bind.hpp>
#include <boost\asio\ip\tcp.hpp>
#include <boost\asio\io_context.hpp>
#include <iostream>
class Server
{
private :
boost::asio::ip::tcp::socket server_socket;
boost::asio::ip::tcp::endpoint server_endpoint;
boost::asio::ip::tcp::acceptor acceptor;
std::string msg;
public :
Server(boost::asio::io_context &io) :
server_socket(io),
server_endpoint(boost::asio::ip::make_address("127.0.0.1"), 27015),
acceptor(io, server_endpoint)
{
acceptor.async_accept(server_socket,
boost::bind(&Server::async_acceptor_handler, this,
boost::asio::placeholders::error));
}
void async_acceptor_handler(const boost::system::error_code &ec)
{
if (!ec)
{
std::cout << "One client connected...\n";
server_socket.async_read_some(boost::asio::buffer(msg),
boost::bind(&Server::async_read_some_handler, this,
boost::asio::placeholders::error));
}
else
{
std::cout << "async_acceptor failed with error code : " << ec.value() << std::endl;
std::cout << "Error description : " << ec.message() << std::endl;
}
}
void async_read_some_handler(const boost::system::error_code &ec)
{
if (!ec)
{
std::cout << msg << std::endl;
server_socket.async_read_some(boost::asio::buffer(msg),
boost::bind(&Server::async_read_some_handler, this,
boost::asio::placeholders::error));
}
else
{
std::cout << "async_acceptor failed with error code : " << ec.value() << std::endl;
std::cout << "Error description : " << ec.message() << std::endl;
}
}
};
int main()
{
boost::asio::io_context io;
Server s(io);
io.run();
return 0;
}
In the client part, it is again a pretty straight-forward code, simply connects to the server and starts taking input from user and sends to server.
Client code:
#include <boost\asio\placeholders.hpp>
#include <boost\bind.hpp>
#include <boost\asio\ip\tcp.hpp>
#include <boost\asio\io_context.hpp>
#include <iostream>
class Client
{
private :
boost::asio::ip::tcp::socket client_socket;
boost::asio::ip::tcp::endpoint server_endpoint;
std::string msg;
public :
Client(boost::asio::io_context &iocontext) :
client_socket(iocontext),
server_endpoint(boost::asio::ip::make_address("127.0.0.1"), 27015)
{
//connect to server endpoint
client_socket.async_connect(server_endpoint,
boost::bind(&Client::async_connect_handler, this,
boost::asio::placeholders::error));
}
void async_connect_handler(const boost::system::error_code &ec)
{
if (!ec)
{
std::cout << "Connected to chat server...\n";
//wait for user input
std::cin >> msg;
std::cout << "\rC : " << msg << std::endl;
client_socket.async_write_some(boost::asio::buffer(msg),
boost::bind(&Client::async_write_some_handler, this,
boost::asio::placeholders::error));
}
else
{
std::cout << "async_connect failed with error code : " << ec.value() << std::endl;
std::cout << "Error description : " << ec.message() << std::endl;
}
}
void async_write_some_handler(const boost::system::error_code &ec)
{
//wait for user input
std::cin >> msg;
std::cout << "\rC : " << msg << std::endl;
client_socket.async_write_some(boost::asio::buffer(msg),
boost::bind(&Client::async_write_some_handler, this,
boost::asio::placeholders::error));
}
};
int main()
{
boost::asio::io_context io;
Client c(io);
io.run();
return 0;
}
Now the problem:
It works fine, and connects to the server too. I get the proper "Connected to chat server..." in client and "One client connected..." in server. The problem arises after that :
In the server console, after the "One client" message, it just starts printing nothing and goes on and on.
The messages sent by the client are never showed in the server console.
Problem 1 can be a issue on my part as I am yet to check the wait functions and other calls which make the server wait. If you can guide me on that, it will be more than amazing. But the major problem is the part 2 of the problem, since, I have no idea why the server is always receiving nothing from client.
PS: This is an incomplete code and I plan to play a bit more with it, so, if there are some major flaws, please tell me so... :)
PPS: Before you say check other questions similar to this, I went through all the similar questions. For ex: this and this, but this are not relevant.
What is the size of string msg in the server side? It is 0. So the server reads always 0 bytes.
When you want to read to string and you call buffer::asio::buffer string must have some size, for example 10. It means you want to read 10 bytes into msg. You can call msg.resize(10) (before reading operation is initiated), then some data will be read into msg by async_read_some (it could be 1,2 bytes, whatever - it is how async_read_some works, but the maximum read characters is 10). But it is poor solution.
You are sending text, so you may consider using read data into streambuf instead of string, when you don't know how many bytes can come from the client side. Then you can call async_read_until with delimiter - it can be for example new line character.
Another solution is to use dynamic buffer. Where data is appened into string and you don't care about the initial size of string buffer. But dynamic buffer doesn't work with member functions of socket like async_read_some, it could be used with async_read as free function.
Update: I can run the server and client perfect locally when they are on the same host. The below scenario is when they are on separate machines(more specifically one is running on ARM64 Petalinux and other on Ubuntu). Perhaps I need to set some options on server side etc
I have a poco websocket server written over the standard sample provided by Poco and a javascript client. While trying to openwebsocket it will get connected in first go sometimes after trying 2-3 times and so on. Now once connected, it might send the message or just gets disconnected on the client side. While on the server side there is no error and I am pretty sure its still open because when i try to open another connection from the client side it will get connected on different client port.
Now the interesting thing is I wrote a Poco websocket client and sending/receiving message continuously without delay in a loop, the client remains active for sometime and then both server and client says Exception connection reset by peer. Next when i put a delay in the loop while sending/receiving messages on client side (say 5s), the client will send/receive for say 3-4 times and then the client encounters Exception connection reset by peer and now this time no such message on server side. Its really boggling me since I couldn't find the solution no matter what i try.
PS: I have timeout of 10 days for websocket server
The server side request handling is as follow:
std::cout << "Request Received" << std::endl;
Poco::Util::Application& app = Poco::Util::Application::instance();
try
{
std::cout << "Trying to connect..." << std::endl;
Poco::Net::WebSocket ws(request, response);
ws.setReceiveTimeout(Poco::Timespan(10, 0, 0, 0, 0)); //Timeout of 10 days
app.logger().information("WebSocket connection established!");
int flags, n;
do
{
char buffer[1024] = {'\0'};
std::cout << "Waiting for incoming frame" << std::endl;
n = ws.receiveFrame(buffer, sizeof(buffer), flags);
strncpy(buffer, "Hello Client!!", 1024);
ws.sendFrame(buffer, strlen(buffer), Poco::Net::WebSocket::FRAME_TEXT);
}
while (n > 0 && (flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) != Poco::Net::WebSocket::FRAME_OP_CLOSE);
std::cout << "Websocket Closed" << std::endl;
app.logger().information("WebSocket connection closed.");
}
catch (std::exception& e)
{
std::cout << "Exception " << e.what() << std::endl;
}
(As one can see, server will post a message when a websocket is closed but nothing is displayed when the client says connection reset by peer)
The poco websocket client side is:
HTTPClientSession cs("IP", port);
HTTPRequest request(HTTPRequest::HTTP_GET, "/?encoding=text",HTTPMessage::HTTP_1_1);
request.set("origin", "http://localhost");
HTTPResponse response;
bool run = true;
try {
WebSocket* m_psock = new WebSocket(cs, request, response);
char buffer[1024] = {'\0'};
std::string str = "Hello Server!!";
do {
char receiveBuff[256];
strncpy(buffer, str.c_str(), 1024);
int len=m_psock->sendFrame(buffer,strlen(buffer),WebSocket::FRAME_TEXT);
std::cout << "Sent bytes " << len << std::endl;
std::cout << "Sent Message: " << buffer << std::endl;
int flags=0;
int rlen=m_psock->receiveFrame(receiveBuff,256,flags);
std::cout << "Received bytes " << rlen << std::endl;
std::cout << receiveBuff << std::endl;
//std::cin >> str;
sleep(5);
} while (1);
m_psock->close();
}
catch (std::exception &e)
{
std::cout << "Exception " << e.what();
}
FYI: I can't process the connection reset by peer, because it directly goes to exception without printing received bytes as 0
I am learning some network programming and was recommended to use boost-asio. I did Daytime Tutorials 1&2 on: http://www.boost.org/doc/libs/1_64_0/doc/html/boost_asio/tutorial.html and wanted to modify it so that the server reacts to a client sending a serialized object then sends back results. I imagined using something like the following sequence with the intention that the client would sit in the handleRead loop waiting for the server to finish:
Server:
accept --> handleRead --> process_read --> perform action --> handleWrite
Client:
connect --> handleWrite --> handleRead --> process_read
However, when I do this, both the server and the client somehow get stuck in the read loop I have set up. This is expected on the client side, but the client should be writing and sending the data prior to getting to the read loop. When I break the connection on the client side or add a socket.close() to the end of the write function I wrote, all the rest of the server steps take place with the appropriate data having been sent from the client.
I originally thought this issue had to do with Nagle's algorithm being enabled, but adding
boost::asio::ip::tcp::no_delay option(true);
socket.set_option(option);
Didn't help at all. Am I missing something that wasn't in the tutorial as to how to get this data to send such as flushing the socket?
void myClient::handle_read()
{
boost::system::error_code e;
try
{
for (;;)
{
size_t len = boost::asio::read(socket, boost::asio::buffer(inBuffer), e);
std::cout << "Client Received: ";
if (e == boost::asio::error::eof)
{
break;
}
else if (e)
{
throw boost::system::system_error(e);
}
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
std::cout << e.what() << std::endl;
}
}
template <typename T>
void handleWrite(T& t)
{
std::ostringstream archive_stream;
std::string tempString;
boost::archive::text_oarchive archive(archive_stream);
archive << t;
tempString = archive_stream.str();
outBuffer.assign(tempString.begin(), tempString.end());
boost::system::error_code ignored_error;
size_t sent = boost::asio::write(socket, boost::asio::buffer(outBuffer), ignored_error);
std::cout << sent << std::endl;
}
I'm fairly new to c++ and network programming so any additional documentation is also appreciated. Thanks!
Of course you're stuck in your read loop. It doesn't exit until end of steam, and end of stream only happens when the peer closes the socket.
I'm using the boost daytime examples as a starter for a project that requires 2 way communication between machines and now need to launch the asio io_service in its own thread so I can pass data across seperatley. Here is the basis of my code: http://www.boost.org/doc/libs/1_35_0/doc/html/boost_asio/tutorial/tutdaytime7/src.html
It all works fine if I invoke the service in main with
io_service.run()
However, if I try to create a thread group and launch there like so:
int main()
{
boost::thread_group tgroup;
try
{
boost::asio::io_service io_service;
tcp_server server1(io_service);
udp_server server2(io_service);
tgroup.create_thread(boost::bind(&boost::asio::io_service::run, &io_service));
std::cout << "Server running on TCP port " << tcpport << std::endl << "Server running on UDP port " << udpport << std::endl;
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
tgroup.join_all();
return 0;
}
The code compiles and runs, the port numbers are quoted by cout correctly but it doesn't seem to open a listening port as the client gets a connection refused, although the server program seems to be ticking away awaiting connections.
What is happening here please?
My guess is that your code will work, if you put tgroup.join_all(); before catch:
...
try {
boost::asio::io_service io_service;
tcp_server server1(io_service);
udp_server server2(io_service);
tgroup.create_thread(boost::bind(&boost::asio::io_service::run, &io_service));
std::cout << "Server running on TCP port " << tcpport << std::endl << "Server running on UDP port " << udpport << std::endl;
tgroup.join_all();
}
...
io_service and server objects go out of scope here and get destroyed, possibly before thread from thread group even start running.