blob: b63c3a5a064980113ac2c519c10f85e709adf8fa [file] [log] [blame]
// Copyright (C) 2014 BMW Group
// Author: Lutz Bichler (lutz.bichler@bmw.de)
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <iomanip>
#include <sstream>
#include <boost/asio/ip/address.hpp>
#include <boost/asio/ip/multicast.hpp>
#include <vsomeip/logger.hpp>
#include "../include/endpoint_host.hpp"
#include "../include/udp_server_endpoint_impl.hpp"
#include "../../utility/include/byteorder.hpp"
#include "../../utility/include/utility.hpp"
namespace ip = boost::asio::ip;
namespace vsomeip {
udp_server_endpoint_impl::udp_server_endpoint_impl(
std::shared_ptr< endpoint_host > _host, endpoint_type _local, boost::asio::io_service &_io)
: server_endpoint_impl< ip::udp, VSOMEIP_MAX_UDP_MESSAGE_SIZE >(_host, _local, _io),
socket_(_io, _local) {
boost::asio::socket_base::broadcast option(true);
socket_.set_option(option);
}
udp_server_endpoint_impl::~udp_server_endpoint_impl() {
}
void udp_server_endpoint_impl::start() {
receive();
}
void udp_server_endpoint_impl::stop() {
if (socket_.is_open())
socket_.close();
}
void udp_server_endpoint_impl::receive() {
packet_buffer_ptr_t its_buffer
= std::make_shared< packet_buffer_t >();
socket_.async_receive_from(
boost::asio::buffer(*its_buffer),
remote_,
std::bind(
&udp_server_endpoint_impl::receive_cbk,
std::dynamic_pointer_cast<
udp_server_endpoint_impl >(shared_from_this()),
its_buffer,
std::placeholders::_1,
std::placeholders::_2
)
);
}
void udp_server_endpoint_impl::restart() {
receive();
}
bool udp_server_endpoint_impl::send_to(
const boost::asio::ip::address &_address, uint16_t _port,
const byte_t *_data, uint32_t _size, bool _flush) {
endpoint_type its_target(_address, _port);
return send_intern(its_target, _data, _size, _flush);
}
void udp_server_endpoint_impl::send_queued(endpoint_type _target, message_buffer_ptr_t _buffer) {
#if 0
std::stringstream msg;
msg << "usei::sq(" << _target.address().to_string() << ":" << _target.port() << "): ";
for (std::size_t i = 0; i < _buffer->size(); ++i)
msg << std::hex << std::setw(2) << std::setfill('0') << (int)(*_buffer)[i] << " ";
VSOMEIP_DEBUG << msg.str();
#endif
socket_.async_send_to(
boost::asio::buffer(*_buffer),
_target,
std::bind(
&udp_server_endpoint_base_impl::send_cbk,
shared_from_this(),
_buffer,
std::placeholders::_1,
std::placeholders::_2
)
);
}
udp_server_endpoint_impl::endpoint_type udp_server_endpoint_impl::get_remote() const {
return remote_;
}
bool udp_server_endpoint_impl::get_multicast(service_t _service, event_t _event,
udp_server_endpoint_impl::endpoint_type &_target) const {
bool is_valid(false);
auto find_service = multicasts_.find(_service);
if (find_service != multicasts_.end()) {
auto find_event = find_service->second.find(_event);
if (find_event != find_service->second.end()) {
_target = find_event->second;
is_valid = true;
}
}
return is_valid;
}
void udp_server_endpoint_impl::join(const std::string &_address) {
if (local_.address().is_v4()) {
try {
socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
socket_.set_option(boost::asio::ip::multicast::enable_loopback(false));
socket_.set_option(boost::asio::ip::multicast::join_group(
boost::asio::ip::address::from_string(_address).to_v4()));
}
catch (const std::exception &e) {
VSOMEIP_ERROR << e.what();
}
} else {
// TODO: support multicast for IPv6
}
}
void udp_server_endpoint_impl::leave(const std::string &_address) {
if (local_.address().is_v4()) {
try {
socket_.set_option(boost::asio::ip::multicast::leave_group(
boost::asio::ip::address::from_string(_address)));
}
catch (...) {
}
} else {
// TODO: support multicast for IPv6
}
}
void udp_server_endpoint_impl::add_multicast(service_t _service, instance_t _instance,
const std::string &_address, uint16_t _port) {
endpoint_type its_endpoint(boost::asio::ip::address::from_string(_address), _port);
multicasts_[_service][_instance] = its_endpoint;
}
void udp_server_endpoint_impl::remove_multicast(service_t _service, instance_t _instance) {
auto found_service = multicasts_.find(_service);
if (found_service != multicasts_.end()) {
auto found_instance = found_service->second.find(_instance);
if (found_instance != found_service->second.end()) {
found_service->second.erase(_instance);
}
}
}
unsigned short udp_server_endpoint_impl::get_port() const {
return socket_.local_endpoint().port();
}
// TODO: find a better way to structure the receive functions
void udp_server_endpoint_impl::receive_cbk(
packet_buffer_ptr_t _buffer,
boost::system::error_code const &_error, std::size_t _bytes) {
#if 0
std::stringstream msg;
msg << "usei::rcb(" << _error.message() << "): ";
for (std::size_t i = 0; i < _bytes; ++i)
msg << std::hex << std::setw(2) << std::setfill('0') << (int)(*_buffer)[i] << " ";
VSOMEIP_DEBUG << msg.str();
#endif
if (!_error && 0 < _bytes) {
message_.insert(message_.end(), _buffer->begin(), _buffer->begin() + _bytes);
bool has_full_message;
do {
uint32_t current_message_size = utility::get_message_size(message_);
has_full_message = (current_message_size > 0 && current_message_size <= message_.size());
if (has_full_message) {
if (utility::is_request(message_[VSOMEIP_MESSAGE_TYPE_POS])) {
client_t its_client;
std::memcpy(&its_client, &message_[VSOMEIP_CLIENT_POS_MIN], sizeof(client_t));
session_t its_session;
std::memcpy(&its_session, &message_[VSOMEIP_SESSION_POS_MIN], sizeof(session_t));
clients_[its_client][its_session] = remote_;
}
this->host_->on_message(&message_[0], current_message_size, this);
message_.erase(message_.begin(), message_.begin() + current_message_size);
}
} while (has_full_message);
restart();
} else {
receive();
}
}
} // namespace vsomeip