I have a UdpClient class written as a convenience wrapper around an Asio UDP socket, and in it I'm trying to use async_receive_from to push received messages onto a std::stack<std::string>.
The UdpClient.h header shown here:
#pragma once
#define ASIO_STANDALONE
#define ASIO_HAS_STD_ADDRESSOF
#define ASIO_HAS_STD_ARRAY
#define ASIO_HAS_CSTDINT
#define ASIO_HAS_STD_SHARED_PTR
#define ASIO_HAS_STD_TYPE_TRAITS
#define ASIO_HAS_STD_ATOMIC
#if !defined(WINVER) && defined(_WIN32)
#define WINVER 0x0A00
#define _WIN32_WINNT 0x0A00
#endif
#include <iostream>
#include <asio.hpp>
#include <array>
#include <functional>
#include <stack>
#include <mutex>
typedef uint8_t byte;
typedef unsigned short ushort;
using asio::ip::udp;
class UdpClient
{
private:
udp::socket _sock;
asio::error_code _err;
std::array<char, 1024> inbuf;
udp::endpoint _remote_end;
std::stack<std::string> messages;
std::mutex msg_mutex;
/**
* Handler, called when UDP data is received.
*
* @param errcode The error code.
* @param len The length of data received.
*/
void rx_handler(const asio::error_code& errcode, std::size_t len);
public:
/** Starts listening for connections. */
void startListening();
/**
* Constructor.
*
* @param [in] serv The IO service to use.
*/
UdpClient(asio::io_service& serv);
/**
* Writes the given message.
*
* @param msg The message to write.
*/
size_t write(std::string msg);
/**
* Writes a broadcast message.
*
* @param msg The message.
* @param port The port on which the broadcast is sent.
*
* @return The number of bytes transmitted.
*/
size_t writeBroadcast(std::string msg, ushort port = 7777);
/**
* Connects to the specified endpoint (DCS).
*
* @param ipstr The IP address.
* @param port The port to use.
*/
void connect(std::string ipstr, unsigned short port = 7777);
/**
* Gets the topmost string on the message stack.
*
* @return A std::string.
*/
std::string read();
/**
* Gets the number of available messages.
*
* @return An int.
*/
int availableMessages();
std::string errmsg() const;
};
UdpClient.cpp is this:
#if !defined(WINVER) && defined(_WIN32)
#define WINVER 0x0A00
#define _WIN32_WINNT 0x0A00
#endif // _WIN32
#include "UdpClient.h"
void UdpClient::startListening()
{
_sock.async_receive_from(asio::buffer(inbuf), _remote_end, std::bind(&UdpClient::rx_handler, this, std::placeholders::_1, std::placeholders::_2));
}
void UdpClient::rx_handler(const asio::error_code & errcode, std::size_t len)
{
if (len == 0)
{
startListening();
return;
}
msg_mutex.lock();
messages.push(std::string(inbuf.data()));
msg_mutex.unlock();
startListening();
}
UdpClient::UdpClient(asio::io_service & serv) : _sock(serv)
{
_sock.open(asio::ip::udp::v4(), _err);
_sock.set_option(asio::ip::udp::socket::reuse_address(true));
_sock.set_option(asio::ip::udp::socket::broadcast(true));
startListening();
}
size_t UdpClient::write(std::string msg)
{
return _sock.send(asio::buffer(msg.c_str(), msg.length()));
}
size_t UdpClient::writeBroadcast(std::string msg, ushort port)
{
auto target = asio::ip::udp::endpoint(asio::ip::address_v4::broadcast(), port);
//_sock.set_option(asio::socket_base::broadcast(true));
return _sock.send_to(asio::buffer(msg.c_str(), msg.length()), target);
}
void UdpClient::connect(std::string ipstr, unsigned short port)
{
auto ip = asio::ip::address_v4::from_string(ipstr);
_sock.connect(asio::ip::udp::endpoint(ip, port));
}
std::string UdpClient::read()
{
msg_mutex.lock();
std::string temp;
if (!messages.empty())
{
temp = messages.top();
messages.pop();
}
msg_mutex.unlock();
return temp;
}
int UdpClient::availableMessages()
{
msg_mutex.lock();
int ret = messages.size();
msg_mutex.unlock();
return ret;
}
std::string UdpClient::errmsg() const
{
return _err.message();
}
I have a test program defined as such:
#include "stdafx.h"
#include "../DCS_100_Native/UdpClient.h"
#include "../DCS_100_Native/DCS100.h"
#include "asio.hpp"
#include <iostream>
int main()
{
asio::io_service service;
UdpClient client(service);
service.run();
auto N = client.writeBroadcast("*IDN?;");
std::cout << "Sent " << N << " bytes." << std::endl;
while (client.availableMessages() == 0);
std::cout << "Got response: " << client.read() << std::endl;
return 0;
}
The code above compiles with no errors, and I have confirmed that the UDP packet sent by the code is being sent, and that a response from a device I have is coming back. The rx_handler function is being called, but with no data in the buffer and a len of 0. I've also noticed that if I have another startListening() call in rx_handler, the handler gets called over and over (indefinitely) with no data and 0 length. I'm guessing that the issue lies somewhere with std::bind or possibly how/where I'm calling io_service.run.
What I'd like to happen is that rx_handler gets called with a non-zero len parameter and the inbuf gets the UDP response data put into it.
Aucun commentaire:
Enregistrer un commentaire