Diag-Client-Lib
tcp_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 <utility>
12 
13 #include "common/logger.h"
14 
15 namespace boost_support {
16 namespace socket {
17 namespace tcp {
18 
19 TcpClientSocket::TcpClientSocket(std::string_view local_ip_address, std::uint16_t local_port_num,
20  TcpHandlerRead tcp_handler_read)
21  : local_ip_address_{local_ip_address},
22  local_port_num_{local_port_num},
23  io_context_{},
24  tcp_socket_{io_context_},
25  exit_request_{false},
26  running_{false},
27  cond_var_{},
28  mutex_{},
29  tcp_handler_read_{std::move(tcp_handler_read)} {
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_.load()) {
38  if (running_) {
39  lck.unlock();
40  HandleMessage();
41  lck.lock();
42  }
43  }
44  }
45  });
46 }
47 
49  exit_request_ = true;
50  running_ = false;
51  cond_var_.notify_all();
52  thread_.join();
53 }
54 
57  TcpErrorCodeType ec{};
58 
59  // Open the socket
60  tcp_socket_.open(Tcp::v4(), ec);
61  if (ec.value() == boost::system::errc::success) {
62  // reuse address
63  tcp_socket_.set_option(boost::asio::socket_base::reuse_address{true});
64  // Set socket to non blocking
65  tcp_socket_.non_blocking(false);
66  // Bind to local ip address and random port
67  tcp_socket_.bind(Tcp::endpoint(TcpIpAddress::from_string(local_ip_address_), local_port_num_), ec);
68 
69  if (ec.value() == boost::system::errc::success) {
70  // Socket binding success
72  __FILE__, __LINE__, __func__, [this](std::stringstream &msg) {
73  Tcp::endpoint const endpoint_{tcp_socket_.local_endpoint()};
74  msg << "Tcp Socket opened and bound to "
75  << "<" << endpoint_.address().to_string() << "," << endpoint_.port() << ">";
76  });
77  result.EmplaceValue();
78  } else {
79  // Socket binding failed
81  __FILE__, __LINE__, __func__,
82  [ec](std::stringstream &msg) { msg << "Tcp Socket binding failed with message: " << ec.message(); });
83  result.EmplaceError(TcpErrorCode::kBindingFailed);
84  }
85  } else {
87  __FILE__, __LINE__, __func__,
88  [ec](std::stringstream &msg) { msg << "Tcp Socket opening failed with error: " << ec.message(); });
89  result.EmplaceError(TcpErrorCode::kOpenFailed);
90  }
91  return result;
92 }
93 
95  std::uint16_t host_port_num) {
97  TcpErrorCodeType ec{};
98 
99  // connect to provided ipAddress
100  tcp_socket_.connect(Tcp::endpoint(TcpIpAddress::from_string(std::string{host_ip_address}), host_port_num), ec);
101  if (ec.value() == boost::system::errc::success) {
103  __FILE__, __LINE__, __func__, [this](std::stringstream &msg) {
104  Tcp::endpoint const endpoint_{tcp_socket_.remote_endpoint()};
105  msg << "Tcp Socket connected to host "
106  << "<" << endpoint_.address().to_string() << "," << endpoint_.port() << ">";
107  });
108  { // start reading
109  std::lock_guard<std::mutex> lock{mutex_};
110  running_ = true;
111  cond_var_.notify_all();
112  }
113  result.EmplaceValue();
114  } else {
116  __FILE__, __LINE__, __func__,
117  [ec](std::stringstream &msg) { msg << "Tcp Socket connect to host failed with error: " << ec.message(); });
118  }
119  return result;
120 }
121 
124  TcpErrorCodeType ec{};
125 
126  // Graceful shutdown
127  tcp_socket_.shutdown(TcpSocket::shutdown_both, ec);
128  if (ec.value() == boost::system::errc::success) {
129  // stop reading
130  running_ = false;
131  // Socket shutdown success
132  result.EmplaceValue();
133  } else {
135  __FILE__, __LINE__, __func__, [ec](std::stringstream &msg) {
136  msg << "Tcp Socket disconnection from host failed with error: " << ec.message();
137  });
138  }
139  return result;
140 }
141 
144  TcpErrorCodeType ec{};
145 
146  boost::asio::write(tcp_socket_, boost::asio::buffer(tcp_message->GetTxBuffer(), tcp_message->GetTxBuffer().size()),
147  ec);
148  // Check for error
149  if (ec.value() == boost::system::errc::success) {
151  __FILE__, __LINE__, __func__, [this](std::stringstream &msg) {
152  Tcp::endpoint const endpoint_{tcp_socket_.remote_endpoint()};
153  msg << "Tcp message sent to "
154  << "<" << endpoint_.address().to_string() << "," << endpoint_.port() << ">";
155  });
156  result.EmplaceValue();
157  } else {
159  __FILE__, __LINE__, __func__,
160  [ec](std::stringstream &msg) { msg << "Tcp message sending failed with error: " << ec.message(); });
161  }
162  return result;
163 }
164 
167  // destroy the socket
168  tcp_socket_.close();
169  result.EmplaceValue();
170  return result;
171 }
172 
174  TcpErrorCodeType ec{};
175  // create and reserve the buffer
176  TcpMessage::BufferType rx_buffer{};
177  rx_buffer.resize(kDoipheadrSize);
178  // start blocking read to read Header first
179  boost::asio::read(tcp_socket_, boost::asio::buffer(&rx_buffer[0u], kDoipheadrSize), ec);
180  // Check for error
181  if (ec.value() == boost::system::errc::success) {
182  // read the next bytes to read
183  std::uint32_t const read_next_bytes = [&rx_buffer]() noexcept -> std::uint32_t {
184  return static_cast<std::uint32_t>((static_cast<std::uint32_t>(rx_buffer[4u] << 24u) & 0xFF000000) |
185  (static_cast<std::uint32_t>(rx_buffer[5u] << 16u) & 0x00FF0000) |
186  (static_cast<std::uint32_t>(rx_buffer[6u] << 8u) & 0x0000FF00) |
187  (static_cast<std::uint32_t>(rx_buffer[7u] & 0x000000FF)));
188  }();
189  // reserve the buffer
190  rx_buffer.resize(kDoipheadrSize + std::size_t(read_next_bytes));
191  boost::asio::read(tcp_socket_, boost::asio::buffer(&rx_buffer[kDoipheadrSize], read_next_bytes), ec);
192 
193  // all message received, transfer to upper layer
194  Tcp::endpoint const endpoint_{tcp_socket_.remote_endpoint()};
195  TcpMessagePtr tcp_rx_message{
196  std::make_unique<TcpMessage>(endpoint_.address().to_string(), endpoint_.port(), std::move(rx_buffer))};
198  __FILE__, __LINE__, __func__, [endpoint_](std::stringstream &msg) {
199  msg << "Tcp Message received from "
200  << "<" << endpoint_.address().to_string() << "," << endpoint_.port() << ">";
201  });
202  // notify upper layer about received message
203  tcp_handler_read_(std::move(tcp_rx_message));
204  } else if (ec.value() == boost::asio::error::eof) {
205  running_ = false;
207  __FILE__, __LINE__, __func__,
208  [ec](std::stringstream &msg) { msg << "Remote Disconnected with: " << ec.message(); });
209  } else {
210  running_ = false;
212  __FILE__, __LINE__, __func__,
213  [ec](std::stringstream &msg) { msg << "Remote Disconnected with undefined error: " << ec.message(); });
214  }
215 }
216 } // namespace tcp
217 } // namespace socket
218 } // namespace boost_support
static auto GetLibBoostLogger() noexcept -> LibBoostLogger &
Definition: logger.h:20
boost::system::error_code TcpErrorCodeType
Type alias for tcp error codes.
Definition: tcp_client.h:110
std::uint16_t local_port_num_
Store local port number.
Definition: tcp_client.h:120
std::function< void(TcpMessagePtr)> TcpHandlerRead
Tcp function template used for reception.
Definition: tcp_client.h:36
std::string local_ip_address_
Store local ip address.
Definition: tcp_client.h:115
TcpSocket tcp_socket_
Store tcp socket.
Definition: tcp_client.h:130
std::mutex mutex_
mutex to lock critical section
Definition: tcp_client.h:155
core_type::Result< void, TcpErrorCode > DisconnectFromHost()
Function to Disconnect from host.
Definition: tcp_client.cpp:122
TcpHandlerRead tcp_handler_read_
Store the handler.
Definition: tcp_client.h:160
core_type::Result< void, TcpErrorCode > Destroy()
Function to destroy the socket.
Definition: tcp_client.cpp:165
core_type::Result< void, TcpErrorCode > Open()
Function to Open the socket.
Definition: tcp_client.cpp:55
std::atomic_bool exit_request_
Flag to terminate the thread.
Definition: tcp_client.h:135
std::atomic_bool running_
Flag to start the thread.
Definition: tcp_client.h:140
std::condition_variable cond_var_
Conditional variable to block the thread.
Definition: tcp_client.h:145
~TcpClientSocket()
Destruct an instance of TcpClientSocket.
Definition: tcp_client.cpp:48
core_type::Result< void, TcpErrorCode > Transmit(TcpMessageConstPtr tcp_message)
Function to trigger transmission.
Definition: tcp_client.cpp:142
void HandleMessage()
Function to handle the reception of tcp message.
Definition: tcp_client.cpp:173
core_type::Result< void, TcpErrorCode > ConnectToHost(std::string_view host_ip_address, std::uint16_t host_port_num)
Function to connect to remote ip address and port number.
Definition: tcp_client.cpp:94
TcpClientSocket(std::string_view local_ip_address, std::uint16_t local_port_num, TcpHandlerRead tcp_handler_read)
Constructs an instance of TcpClientSocket.
Definition: tcp_client.cpp:19
std::thread thread_
The thread itself.
Definition: tcp_client.h:150
std::vector< uint8_t > BufferType
Type alias for underlying buffer.
Definition: tcp_message.h:48
Class type to contains a value (of type ValueType), or an error (of type ErrorType)
Definition: result.h:29
std::unique_ptr< const TcpMessage > TcpMessageConstPtr
The unique pointer to const TcpMessage.
Definition: tcp_message.h:172
constexpr std::uint8_t kDoipheadrSize
Doip HeaderSize.
Definition: tcp_message.h:182
std::unique_ptr< TcpMessage > TcpMessagePtr
The unique pointer to TcpMessage.
Definition: tcp_message.h:177