I'm struggling with two errors with Boost.Asio.
The first occurs when I try to receive data on a socket:
char reply[1024];
boost::system::error_code error;
size_t reply_length = s.receive(boost::asio::buffer(reply, 1024), 0, error);
if (error) cout << error.message() << endl; //outputs "End of file"
The second occurs when I try to create an ip::tcp::socket from a (valid!) native socket:
boost::asio::io_service ioserv;
boost::asio::ip::tcp::socket s(ioserv);
boost::system::error_code error;
s.assign(boost::asio::ip::tcp::v4(), nativeSocket, error);
if (error) cout << error.message() << endl; //outputs "The parameter is incorrect"
With all these troubles an no documentation to turn to, I am tempted to go back to BSD sockets, but I'm having my own problems there...so if anyone can help, I'd really appreciate it.
EDIT: Regarding number 2, nativeSocket is declared thusly:
SOCKET nativeSocket = INVALID_SOCKET;
nativeSocket = accept(svr_sock, (struct sockaddr*)&sin, &size);
After that, a few other things are done to the socket -- namely, setting it as non-blocking using ioctlsocket, and using setsockopt for SO_LINGER and SO_OOBINLINE.
This is not a complete solution to your second problem by any means. Any errors that it generates should be mapped into a boost::system::error_code, but I don't find anything like it in boost/system/error_code.hpp, so I'm at a loss as to what exactly it is supposed to mean.
But, after looking through the code for boost 1.39, assign is eventually handed off to either detail::reactive_socket_service< Protocol, Reactor >.assign (or detail::win_iocp_socket_service<Protocol>, if you're using windows). It can only be producing an error in two places in boost/asio/detail/reactive_socket_service.hpp:
if (is_open(impl))
{
ec = boost::asio::error::already_open;
return ec;
}
or
if (int err = reactor_.register_descriptor(
native_socket, impl.reactor_data_))
{
ec = boost::system::error_code(err,
boost::asio::error::get_system_category());
return ec;
}
Since, you're not getting an already_open error, the error must from the second bit of code. The reactor type comes from a sequence of ifdef/elif pairs in boost/asio/stream_socket_service.hpp, and of those available only the register_descriptor function in epoll_reactor can throw any error (and of course detail::win_iocp_socket_service<Protocol>.assign can, also). The error in epoll_reactor comes from sys/epoll.h, specifically:
int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev);
if (result != 0)
return errno;
In the windows implementation, the related code is
if (iocp_service_.register_handle(native_socket.as_handle(), ec))
return ec;
I think this is the origin of your error, but honestly, I can't trace it past this point.
Related
I'm working on a vision-application, which have two modes:
1) parameter setting
2) automatic
The problem is in 2), when my app waits for a signal via TCP/IP. The program is freezing while accept()-methode is called. I want to provide the possibility on a GUI to change the mode. So if the mode is changing, it's provided by another signal (message_queue). So I want to interrupt the accept state.
Is there a simple possibility to interrupt the accept?
std::cout << "TCPIP " << std::endl;
client = accept(slisten, (struct sockaddr*)&clientinfo, &clientinfolen);
if (client != SOCKET_ERROR)
cout << "client accepted: " << inet_ntoa(clientinfo.sin_addr) << ":"
<< ntohs(clientinfo.sin_port) << endl;
//receive the message from client
//recv returns the number of bytes received!!
//buf contains the data received
int rec = recv(client, buf, sizeof(buf), 0);
cout << "Message: " << rec << " bytes and the message " << buf << endl;
I read about select() but I have no clue how to use it. Could anybody give me a hint how to implement for example select() in my code?
Thanks.
Best regards,
T
The solution is to call accept() only when there is an incoming connection request. You do that by polling on the listen socket, where you can also add other file descriptors, use a timeout etc.
You did not mention your platform. On Linux, see epoll(), UNIX see poll()/select(), Windows I don't know.
A general way would be to use a local TCP connection by which the UI thread could interrupt the select call. The general architecture would use:
a dedicated thread waiting with select on both slisten and the local TCP connection
a TCP connection (Unix domain socket on a Unix or Unix-like system, or 127.0.0.1 on Windows) between the UI thread and the waiting one
various synchronizations/messages between both threads as required
Just declare that select should read slisten and the local socket. It will return as soon as one is ready, and you will be able to know which one is ready.
As you haven't specified your platform, and networking, especially async, is platform-specific, I suppose you need a cross-platform solution. Boost.Asio fits perfectly here: http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/reference/basic_socket_acceptor/async_accept/overload1.html
Example from the link:
void accept_handler(const boost::system::error_code& error)
{
if (!error)
{
// Accept succeeded.
}
}
...
boost::asio::ip::tcp::acceptor acceptor(io_service);
...
boost::asio::ip::tcp::socket socket(io_service);
acceptor.async_accept(socket, accept_handler);
If Boost is a problem, Asio can be a header-only lib and used w/o Boost: http://think-async.com/Asio/AsioAndBoostAsio.
One way would be to run select in a loop with a timeout.
Put slisten into nonblocking mode (this isn't strictly necessary but sometimes accept blocks even when select says otherwise) and then:
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(slisten, &read_fds);
struct timeval timeout;
timeout.tv_sec = 1; // 1s timeout
timeout.tv_usec = 0;
int select_status;
while (true) {
select_status = select(slisten+1, &read_fds, NULL, NULL, &timeout);
if (select_status == -1) {
// ERROR: do something
} else if (select_status > 0) {
break; // we have data, we can accept now
}
// otherwise (i.e. select_status==0) timeout, continue
}
client = accept(slisten, ...);
This will allow you to catch signals once per second. More info here:
http://man7.org/linux/man-pages/man2/select.2.html
and Windows version (pretty much the same):
https://msdn.microsoft.com/pl-pl/library/windows/desktop/ms740141(v=vs.85).aspx
I am converting an app which had a very simple heartbeat / status monitoring connection between two services. As that now needs to be made to run on linux in addition to windows, I thought I'd use boost (v1.51, and I cannot upgrade - linux compilers are too old and windows compiler is visual studio 2005) to accomplish the task of making it platform agnostic (considering, I really would prefer not to either have two code files, one for each OS, or a littering of #defines throughout the code, when boost offers the possibility of being pleasant to read (6mos after I've checked in and forgotten this code!)
My problem now, is the connection is timing out. Actually, it's not really working at all.
First time through, the 'status' message is sent, it's received by the server end which sends back an appropriate response. Server end then goes back to waiting on the socket for another message. Client end (this code), sends the 'status' message again... but this time, the server never receives it and the read_some() call blocks until the socket times out. I find it really strange that
The server end has not changed. The only thing that's changed, is my having altered the client code from basic winsock2 sockets, to this code. Previously, it connected and just looped through send / recv calls until the program was aborted or the 'lockdown' message was received.
Why would subsequent calls (to send) silently fail to send anything on the socket and, what do I need to adjust in order to restore the simple send / recv flow?
#include <boost/signals2/signal.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
using boost::asio::ip::tcp;
using namespace std;
boost::system::error_code ServiceMonitorThread::ConnectToPeer(
tcp::socket &socket,
tcp::resolver::iterator endpoint_iterator)
{
boost::system::error_code error;
int tries = 0;
for (; tries < maxTriesBeforeAbort; tries++)
{
boost::asio::connect(socket, endpoint_iterator, error);
if (!error)
{
break;
}
else if (error != make_error_code(boost::system::errc::success))
{
// Error connecting to service... may not be running?
cerr << error.message() << endl;
boost::this_thread::sleep_for(boost::chrono::milliseconds(200));
}
}
if (tries == maxTriesBeforeAbort)
{
error = make_error_code(boost::system::errc::host_unreachable);
}
return error;
}
// Main thread-loop routine.
void ServiceMonitorThread::run()
{
boost::system::error_code error;
tcp::resolver resolver(io_service);
tcp::resolver::query query(hostnameOrAddress, to_string(port));
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::socket socket(io_service);
error = ConnectToPeer(socket, endpoint_iterator);
if (error && error == boost::system::errc::host_unreachable)
{
TerminateProgram();
}
boost::asio::streambuf command;
std::ostream command_stream(&command);
command_stream << "status\n";
boost::array<char, 10> response;
int retry = 0;
while (retry < maxTriesBeforeAbort)
{
// A 1s request interval is more than sufficient for status checking.
boost::this_thread::sleep_for(boost::chrono::seconds(1));
// Send the command to the network monitor server service.
boost::asio::write(socket, command, error);
if (error)
{
// Error sending to socket
cerr << error.message() << endl;
retry++;
continue;
}
// Clear the response buffer, then read the network monitor status.
response.assign(0);
/* size_t bytes_read = */ socket.read_some(boost::asio::buffer(response), error);
if (error)
{
if (error == make_error_code(boost::asio::error::eof))
{
// Connection was dropped, re-connect to the service.
error = ConnectToPeer(socket, endpoint_iterator);
if (error && error == make_error_code(boost::system::errc::host_unreachable))
{
TerminateProgram();
}
continue;
}
else
{
cerr << error.message() << endl;
retry++;
continue;
}
}
// Examine the response message.
if (strncmp(response.data(), "normal", 6) != 0)
{
retry++;
// If we received the lockdown response, then terminate.
if (strncmp(response.data(), "lockdown", 8) == 0)
{
break;
}
// Not an expected response, potential error, retry to see if it was merely an aberration.
continue;
}
// If we arrived here, the exchange was successful; reset the retry count.
if (retry > 0)
{
retry = 0;
}
}
// If retry count was incremented, then we have likely encountered an issue; shut things down.
if (retry != 0)
{
TerminateProgram();
}
}
When a streambuf is provided directly to an I/O operation as the buffer, then the I/O operation will manage the input sequence appropriately by either commiting read data or consuming written data. Hence, in the following code, command is empty after the first iteration:
boost::asio::streambuf command;
std::ostream command_stream(&command);
command_stream << "status\n";
// `command`'s input sequence contains "status\n".
while (retry < maxTriesBeforeAbort)
{
...
// write all of `command`'s input sequence to the socket.
boost::asio::write(socket, command, error);
// `command.size()` is 0, as the write operation will consume the data.
// Subsequent write operations with `command` will be no-ops.
...
}
One solution would be to use std::string as the buffer:
std::string command("status\n");
while (retry < maxTriesBeforeAbort)
{
...
boost::asio::write(socket, boost::asio::buffer(command), error);
...
}
For more details on streambuf usage, consider reading this answer.
I'm new to ASIO and I'm trying to get a relatively simple UDP broadcast to work and I'm not seeing any packets leave my PC in Wireshark. Is there a configuraton on the io_service or socket that I'm missing? Here's my complete code:
#include <iostream>
#include <boost/asio.hpp>
#include <array>
using boost::asio::ip::udp;
namespace asio = boost::asio;
const char* idnMsg = "*IDN?;";
int main(int argc, char* argv[])
{
try
{
asio::io_service serv;
boost::system::error_code err;
udp::socket socket(serv);
socket.open(asio::ip::udp::v4(), err);
if (!err)
{
socket.set_option(udp::socket::reuse_address(true));
socket.set_option(asio::socket_base::broadcast(true));
asio::ip::udp::endpoint senderEndpoint(asio::ip::address_v4::broadcast(), 7777);
socket.send_to(asio::buffer(idnMsg, 6), senderEndpoint);
//socket.close(err);
std::array<char, 128> buf;
while (socket.available())
{
asio::ip::udp::endpoint remote;
size_t len = socket.receive_from(asio::buffer(buf), remote);
std::cout << "Received: ";
std::cout.write(buf.data(), len);
buf.fill(0);
}
}
else
std::cerr << "Error connecting: " << err.message() << std::endl;
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
You may notice that it bears a striking resemblance to a combination of an asio example and another SO answer.
I'm using Boost.Asio from NuGet in Visual Studio 2015 in Windows 7 x64. If switching to manually-compiled standalone Asio will help, I will. I plan on doing that in the future anyway, as I have no need for the rest of boost in my current plans.
As I said, I saw no packets leave my PC in Wireshark, and I even have several devices on my network that would respond to that packet, and I saw no responses.
EDIT: This code with standalone ASIO in Linux works fine. But I'm going to need ASIO working on Windows if I'm eventually going to be shipping code with it; cross-platform is great and I'm aiming for that, but Windows is the biggest market.
It looks to me that the best you can expect is one small packet. You should check the return from the send_to call, which will tell you how many bytes were sent.
if( socket.send_to(asio::buffer(idnMsg, 6), senderEndpoint) != 6 )
{ std::cerr << "send_to failed" << std::endl; exit(1); }
else
{ std::cerr << "one packet sent" << std::endl; }
I compiled and ran your code: it works just fine -
Perhaps you missed this single packet in a flood of others?
Answering my own question to provide information to others who may have the same issue. I determined that the culprit was the "VirtualBox Host-Only Network" adapter added by VirtualBox. It looks like it was swallowing up all UDP broadcast packets sent from my machine. Disabling that adapter allowed my Asio code to work, and didn't event interfere with my VMs set to Bridged Adapter.
I have a C++ and Qt application which part of it implements a C socket client. Some time ago by app crashed because something happened with the server; the only thing I got from that crash was a message in Qt Creator's Application Output stating
recv_from_client: Connection reset by peer
I did some research on the web about this "connection reset by peer" error and while some threads here in SO and other places did managed to explain what is going on, none of them tells how to handle it - that is, how can I "catch" the error and continue my application without a crash (particularly the method where I read from the server is inside a while loop, so I'ld like to stop the while loop and enter in another place of my code that will try to re-establish the connection).
So how can I catch this error to handle it appropriately? Don't forget that my code is actually C++ with Qt - the C part is a library which calls the socket methods.
EDIT
Btw, the probable method from which the crash originated (given the "recv_from_client" part of the error message above) was:
int hal_socket_read_from_client(socket_t *obj, u_int8_t *buffer, int size)
{
struct s_socket_private * const socket_obj = (struct s_socket_private *)obj;
int retval = recv(socket_obj->client_fd, buffer, size, MSG_DONTWAIT); //last = 0
if (retval < 0)
perror("recv_from_client");
return retval;
}
Note: I'm not sure if by the time this error occurred, the recv configuration was with MSG_DONTWAIT or with 0.
Just examine errno when read() returns a negative result.
There is normally no crash involved.
while (...) {
ssize_t amt = read(sock, buf, size);
if (amt > 0) {
// success
} else if (amt == 0) {
// remote shutdown (EOF)
} else {
// error
// Interrupted by signal, try again
if (errno == EINTR)
continue;
// This is fatal... you have to close the socket and reconnect
// handle errno == ECONNRESET here
// If you use non-blocking sockets, you also have to handle
// EWOULDBLOCK / EAGAIN here
return;
}
}
It isn't an exception or a signal. You can't catch it. Instead, you get an error which tells you that the connection has been resetted when trying to work on that socket.
int rc = recv(fd, ..., ..., ..., ...);
if (rc == -1)
{
if (errno == ECONNRESET)
/* handle it; there isn't much to do, though.*/
else
perror("Error while reading");
}
As I've written, there isn't much you can do. If you're using some I/O multiplexer, you may want to remove that file descriptor from further monitoring.
I've been using boost asio for networking for some time, but never for SSL sockets. Now i'm required to use SSL sockets and they work pretty fine. But i am not able to find out when a sockets get closed (I usually did this as I did with regular sockets - checking the error value when using boost::asio::async_read_until() in the callback function.
Here's some relevant code snippets:
boost::asio::streambuf streambuf;
boost::asio::ssl::context sslctx(io_service, boost::asio::ssl::context::tlsv1);
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> sock(io_service, sslctx);
void DoAsyncRead()
{
boost::asio::async_read_until(sock, streambuf, "\n", MyReadHandler);
}
void MyReadHandler(const boost::system::error_code& error, size_t bytes_transferred)
{
if (error) {
std::cout << "Read error: " << error.message() << std::endl;
} else {
// ...
}
}
The error condition is never true, even if I kill the server, or drop the client connection. How can I track if the connection is closed?
EOS is not an error condition in most APIs. It is a sentinel value returned instead of a byte count, typically zero (Unix) or -1 (Java).