Diag-Client-Lib
udp_client.cpp
Go to the documentation of this file.
1 /* Diagnostic Client library
2  * Copyright (C) 2024 Avijit Dey
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7  */
8 
10 
11 #include "common/logger.h"
12 
13 namespace boost_support {
14 namespace socket {
15 namespace udp {
16 
17 UdpClientSocket::UdpClientSocket(std::string_view local_ip_address, std::uint16_t local_port_num, PortType port_type,
18  UdpHandlerRead udp_handler_read)
19  : local_ip_address_{local_ip_address},
20  local_port_num_{local_port_num},
21  io_context_{},
22  udp_socket_{io_context_},
23  exit_request_{false},
24  running_{false},
25  cond_var_{},
26  mutex_{},
27  port_type_{port_type},
28  udp_handler_read_{std::move(udp_handler_read)},
29  rx_buffer_{} {
30  // Start thread to receive messages
31  thread_ = std::thread([this]() {
32  std::unique_lock<std::mutex> lck(mutex_);
33  while (!exit_request_) {
34  if (!running_) {
35  cond_var_.wait(lck, [this]() { return exit_request_ || running_; });
36  }
37  if (!exit_request_) {
38  if (running_) {
39  io_context_.restart();
40  io_context_.run();
41  }
42  }
43  }
44  });
45 }
46 
48  exit_request_ = true;
49  running_ = false;
50  cond_var_.notify_all();
51  thread_.join();
52 }
53 
56  UdpErrorCodeType ec{};
57 
58  // Open the socket
59  udp_socket_.open(Udp::v4(), ec);
60  if (ec.value() == boost::system::errc::success) {
61  // set broadcast option
62  boost::asio::socket_base::broadcast broadcast_option(true);
63  udp_socket_.set_option(broadcast_option);
64  // reuse address
65  boost::asio::socket_base::reuse_address reuse_address_option(true);
66  udp_socket_.set_option(reuse_address_option);
67 
69  // Todo : change the hardcoded value of port number 13400
70  udp_socket_.bind(Udp::endpoint(boost::asio::ip::address_v4::any(), 13400), ec);
71  } else {
72  //bind to local address and random port
73  udp_socket_.bind(Udp::endpoint(UdpIpAddress::from_string(local_ip_address_), local_port_num_), ec);
74  }
75 
76  if (ec.value() == boost::system::errc::success) {
77  Udp::endpoint endpoint{udp_socket_.local_endpoint()};
79  __FILE__, __LINE__, __func__, [endpoint](std::stringstream &msg) {
80  msg << "Udp Socket Opened and bound to "
81  << "<" << endpoint.address().to_string() << "," << endpoint.port() << ">";
82  });
83  // Update the port number with new one
84  local_port_num_ = udp_socket_.local_endpoint().port();
85  { // start reading
86  std::lock_guard<std::mutex> lock{mutex_};
87  running_ = true;
88  cond_var_.notify_all();
89  }
90  // start async receive
91  udp_socket_.async_receive_from(
92  boost::asio::buffer(rx_buffer_), remote_endpoint_,
93  [this](const UdpErrorCodeType &error, std::size_t bytes_recvd) { HandleMessage(error, bytes_recvd); });
94  result.EmplaceValue();
95  } else {
96  // Socket binding failed
98  __FILE__, __LINE__, __func__,
99  [ec](std::stringstream &msg) { msg << "Udp Socket Bind failed with message: " << ec.message(); });
100  }
101  } else {
103  __FILE__, __LINE__, __func__,
104  [ec](std::stringstream &msg) { msg << "Udp Socket Opening failed with error: " << ec.message(); });
105  }
106  return result;
107 }
108 
111  try {
112  // Transmit to remote endpoints
113  std::size_t send_size{udp_socket_.send_to(
114  boost::asio::buffer(udp_message->GetTxBuffer(), std::size_t(udp_message->GetTxBuffer().size())),
115  Udp::endpoint{UdpIpAddress::from_string(std::string{udp_message->GetHostIpAddress()}),
116  udp_message->GetHostPortNumber()})};
117  // Check for error
118  if (send_size == udp_message->GetTxBuffer().size()) {
119  // successful
120  Udp::endpoint endpoint{udp_socket_.local_endpoint()};
122  __FILE__, __LINE__, __func__, [&udp_message, endpoint](std::stringstream &msg) {
123  msg << "Udp message sent : "
124  << "<" << endpoint.address().to_string() << "," << endpoint.port() << ">"
125  << " -> "
126  << "<" << udp_message->GetHostIpAddress() << "," << udp_message->GetHostPortNumber() << ">";
127  });
128  result.EmplaceValue();
129  // start async receive
130  udp_socket_.async_receive_from(
131  boost::asio::buffer(rx_buffer_), remote_endpoint_,
132  [this](const UdpErrorCodeType &error, std::size_t bytes_received) { HandleMessage(error, bytes_received); });
133  }
134  } catch (boost::system::system_error const &ec) {
135  UdpErrorCodeType error = ec.code();
136  std::cerr << error.message() << "\n";
138  __FILE__, __LINE__, __func__, [error, &udp_message](std::stringstream &msg) {
139  msg << "Udp message sending to "
140  << "<" << udp_message->GetHostIpAddress() << "> "
141  << "failed with error: " << error.message();
142  });
143  }
144  return result;
145 }
146 
148  core_type::Result<void, UdpErrorCode> result{UdpErrorCode::kGenericError};
149  // destroy the socket
150  udp_socket_.close();
151  running_ = false;
152  io_context_.stop();
153  result.EmplaceValue();
154  return result;
155 }
156 
157 // function invoked when datagram is received
158 void UdpClientSocket::HandleMessage(const UdpErrorCodeType &error, std::size_t total_bytes_received) {
159  // Check for error
160  if (error.value() == boost::system::errc::success) {
161  if (local_ip_address_ != remote_endpoint_.address().to_string()) {
162  UdpMessage::BufferType received_data{};
163  received_data.reserve(total_bytes_received);
164  // copy the received bytes into local buffer
165  received_data.insert(received_data.begin(), rx_buffer_.begin(), rx_buffer_.begin() + total_bytes_received);
166 
167  UdpMessagePtr udp_rx_message{std::make_unique<UdpMessage>(remote_endpoint_.address().to_string(),
168  remote_endpoint_.port(), std::move(received_data))};
169 
171  __FILE__, __LINE__, __func__, [this, &udp_rx_message](std::stringstream &msg) {
172  msg << "Udp Message received: "
173  << "<" << udp_rx_message->GetHostIpAddress() << "," << udp_rx_message->GetHostPortNumber() << ">"
174  << " -> "
175  << "<" << local_ip_address_ << "," << local_port_num_ << ">";
176  });
177 
178  // send data to upper layer
179  udp_handler_read_(std::move(udp_rx_message));
180  // start async receive
181  udp_socket_.async_receive_from(boost::asio::buffer(rx_buffer_), remote_endpoint_,
182  [this](const UdpErrorCodeType &error_received, std::size_t bytes_received) {
183  HandleMessage(error_received, bytes_received);
184  });
185  } else {
186  Udp::endpoint endpoint_{remote_endpoint_};
187  common::logger::LibBoostLogger::GetLibBoostLogger().GetLogger().LogVerbose(
188  __FILE__, __LINE__, __func__, [endpoint_, this](std::stringstream &msg) {
189  msg << "Udp Message received from "
190  << "<" << endpoint_.address().to_string() << "," << endpoint_.port() << ">"
191  << " ignored as received by self ip"
192  << " <" << local_ip_address_ << ">";
193  });
194  }
195  } else {
196  if (error.value() != boost::asio::error::operation_aborted) {
198  __FILE__, __LINE__, __func__, [error, this](std::stringstream &msg) {
199  msg << "<" << local_ip_address_ << ">: "
200  << "Remote Disconnected with undefined error: " << error.message();
201  });
202  }
203  }
204 }
205 
206 } // namespace udp
207 } // namespace socket
208 } // namespace boost_support
static auto GetLibBoostLogger() noexcept -> LibBoostLogger &
Definition: logger.h:20
std::atomic_bool running_
Flag to start the thread.
Definition: udp_client.h:133
std::atomic_bool exit_request_
Flag to terminate the thread.
Definition: udp_client.h:128
virtual ~UdpClientSocket()
Destruct an instance of UdpClientSocket.
Definition: udp_client.cpp:47
void HandleMessage(const UdpErrorCodeType &error, std::size_t bytes_received)
Function to handle the reception of tcp message.
Definition: udp_client.cpp:158
Udp::endpoint remote_endpoint_
Store the remote endpoint.
Definition: udp_client.h:153
std::function< void(UdpMessagePtr)> UdpHandlerRead
Udp function template used for reception.
Definition: udp_client.h:42
PortType port_type_
Store the port type - broadcast / unicast.
Definition: udp_client.h:158
PortType
Type of udp port to be used underneath.
Definition: udp_client.h:37
std::string local_ip_address_
Store local ip address.
Definition: udp_client.h:108
std::condition_variable cond_var_
Conditional variable to block the thread.
Definition: udp_client.h:138
boost::system::error_code UdpErrorCodeType
Type alias for udp error codes.
Definition: udp_client.h:103
UdpSocket udp_socket_
Store tcp socket.
Definition: udp_client.h:123
core_type::Result< void, UdpErrorCode > Transmit(UdpMessageConstPtr udp_message)
Function to trigger transmission.
Definition: udp_client.cpp:109
std::thread thread_
The thread itself.
Definition: udp_client.h:143
std::uint16_t local_port_num_
Store local port number.
Definition: udp_client.h:113
boost::asio::io_context io_context_
boost io context
Definition: udp_client.h:118
std::array< std::uint8_t, kDoipUdpResSize > rx_buffer_
Reception buffer needed for async reception of udp data.
Definition: udp_client.h:168
std::mutex mutex_
mutex to lock critical section
Definition: udp_client.h:148
core_type::Result< void, UdpErrorCode > Open()
Function to Open the socket.
Definition: udp_client.cpp:54
UdpClientSocket(std::string_view local_ip_address, std::uint16_t local_port_num, PortType port_type, UdpHandlerRead udp_handler_read)
Constructs an instance of UdpClientSocket.
Definition: udp_client.cpp:17
std::vector< uint8_t > BufferType
Type alias for underlying buffer.
Definition: udp_message.h:36
Class type to contains a value (of type ValueType), or an error (of type ErrorType)
Definition: result.h:29
void EmplaceValue(Args &&...args) noexcept
Put a new value into this instance, constructed in-place from the given arguments.
Definition: result.h:187
std::unique_ptr< const UdpMessage > UdpMessageConstPtr
The unique pointer to const UdpMessage.
Definition: udp_message.h:134
std::unique_ptr< UdpMessage > UdpMessagePtr
The unique pointer to UdpMessage.
Definition: udp_message.h:139