Agora  1.2.0
Agora project
tcp_client.h
Go to the documentation of this file.
1 // Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
2 // Distributed under the MIT License (http://opensource.org/licenses/MIT)
3 
4 #pragma once
5 
6 #ifdef _WIN32
7 # error include tcp_client-windows.h instead
8 #endif
9 
10 // tcp client helper
11 #include <spdlog/common.h>
12 #include <spdlog/details/os.h>
13 
14 #include <sys/socket.h>
15 #include <arpa/inet.h>
16 #include <unistd.h>
17 #include <netdb.h>
18 #include <netinet/tcp.h>
19 
20 #include <string>
21 
22 namespace spdlog {
23 namespace details {
24 class tcp_client
25 {
26  int socket_ = -1;
27 
28 public:
29  bool is_connected() const
30  {
31  return socket_ != -1;
32  }
33 
34  void close()
35  {
36  if (is_connected())
37  {
39  socket_ = -1;
40  }
41  }
42 
43  int fd() const
44  {
45  return socket_;
46  }
47 
49  {
50  close();
51  }
52 
53  // try to connect or throw on failure
54  void connect(const std::string &host, int port)
55  {
56  close();
57  struct addrinfo hints
58  {};
59  memset(&hints, 0, sizeof(struct addrinfo));
60  hints.ai_family = AF_INET; // IPv4
61  hints.ai_socktype = SOCK_STREAM; // TCP
62  hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
63  hints.ai_protocol = 0;
64 
65  auto port_str = std::to_string(port);
66  struct addrinfo *addrinfo_result;
67  auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
68  if (rv != 0)
69  {
70  throw_spdlog_ex(fmt_lib::format("::getaddrinfo failed: {}", gai_strerror(rv)));
71  }
72 
73  // Try each address until we successfully connect(2).
74  int last_errno = 0;
75  for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
76  {
77 #if defined(SOCK_CLOEXEC)
78  const int flags = SOCK_CLOEXEC;
79 #else
80  const int flags = 0;
81 #endif
82  socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol);
83  if (socket_ == -1)
84  {
85  last_errno = errno;
86  continue;
87  }
88  rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen);
89  if (rv == 0)
90  {
91  break;
92  }
93  last_errno = errno;
95  socket_ = -1;
96  }
97  ::freeaddrinfo(addrinfo_result);
98  if (socket_ == -1)
99  {
100  throw_spdlog_ex("::connect failed", last_errno);
101  }
102 
103  // set TCP_NODELAY
104  int enable_flag = 1;
105  ::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
106 
107  // prevent sigpipe on systems where MSG_NOSIGNAL is not available
108 #if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
109  ::setsockopt(socket_, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
110 #endif
111 
112 #if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
113 # error "tcp_sink would raise SIGPIPE since niether SO_NOSIGPIPE nor MSG_NOSIGNAL are available"
114 #endif
115  }
116 
117  // Send exactly n_bytes of the given data.
118  // On error close the connection and throw.
119  void send(const char *data, size_t n_bytes)
120  {
121  size_t bytes_sent = 0;
122  while (bytes_sent < n_bytes)
123  {
124 #if defined(MSG_NOSIGNAL)
125  const int send_flags = MSG_NOSIGNAL;
126 #else
127  const int send_flags = 0;
128 #endif
129  auto write_result = ::send(socket_, data + bytes_sent, n_bytes - bytes_sent, send_flags);
130  if (write_result < 0)
131  {
132  close();
133  throw_spdlog_ex("write(2) failed", errno);
134  }
135 
136  if (write_result == 0) // (probably should not happen but in any case..)
137  {
138  break;
139  }
140  bytes_sent += static_cast<size_t>(write_result);
141  }
142  }
143 };
144 } // namespace details
145 } // namespace spdlog
spdlog::details::tcp_client::send
void send(const char *data, size_t n_bytes)
Definition: tcp_client.h:119
flags
list flags
Definition: .ycm_extra_conf.py:39
spdlog
Definition: async.h:25
spdlog::throw_spdlog_ex
SPDLOG_INLINE void throw_spdlog_ex(const std::string &msg, int last_errno)
Definition: common-inl.h:72
spdlog::details::tcp_client::fd
int fd() const
Definition: tcp_client.h:43
spdlog::details::tcp_client::~tcp_client
~tcp_client()
Definition: tcp_client.h:48
os.h
spdlog::details::tcp_client::socket_
SOCKET socket_
Definition: tcp_client-windows.h:26
extract_version.data
dictionary data
Definition: extract_version.py:8
spdlog::details::tcp_client::connect
void connect(const std::string &host, int port)
Definition: tcp_client.h:54
common.h
to_string
std::string to_string() const
Definition: eth_common.h:64
spdlog::details::tcp_client::close
void close()
Definition: tcp_client.h:34
spdlog::details::tcp_client::is_connected
bool is_connected() const
Definition: tcp_client.h:29
utils::format
std::string format(const T &value)
Definition: utils.h:15