spdlog
Loading...
Searching...
No Matches
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
22namespace spdlog {
23namespace details {
24class tcp_client
25{
26 int socket_ = -1;
27
28public:
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 auto msg = fmt::format("::getaddrinfo failed: {}", gai_strerror(rv));
71 throw_spdlog_ex(msg);
72 }
73
74 // Try each address until we successfully connect(2).
75 int last_errno = 0;
76 for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
77 {
78#if defined(SOCK_CLOEXEC)
79 const int flags = SOCK_CLOEXEC;
80#else
81 const int flags = 0;
82#endif
83 socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol);
84 if (socket_ == -1)
85 {
86 last_errno = errno;
87 continue;
88 }
89 rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen);
90 if (rv == 0)
91 {
92 break;
93 }
94 last_errno = errno;
96 socket_ = -1;
97 }
98 ::freeaddrinfo(addrinfo_result);
99 if (socket_ == -1)
100 {
101 throw_spdlog_ex("::connect failed", last_errno);
102 }
103
104 // set TCP_NODELAY
105 int enable_flag = 1;
106 ::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
107
108 // prevent sigpipe on systems where MSG_NOSIGNAL is not available
109#if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
110 ::setsockopt(socket_, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
111#endif
112
113#if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
114# error "tcp_sink would raise SIGPIPE since niether SO_NOSIGPIPE nor MSG_NOSIGNAL are available"
115#endif
116 }
117
118 // Send exactly n_bytes of the given data.
119 // On error close the connection and throw.
120 void send(const char *data, size_t n_bytes)
121 {
122 size_t bytes_sent = 0;
123 while (bytes_sent < n_bytes)
124 {
125#if defined(MSG_NOSIGNAL)
126 const int send_flags = MSG_NOSIGNAL;
127#else
128 const int send_flags = 0;
129#endif
130 auto write_result = ::send(socket_, data + bytes_sent, n_bytes - bytes_sent, send_flags);
131 if (write_result < 0)
132 {
133 close();
134 throw_spdlog_ex("write(2) failed", errno);
135 }
136
137 if (write_result == 0) // (probably should not happen but in any case..)
138 {
139 break;
140 }
141 bytes_sent += static_cast<size_t>(write_result);
142 }
143 }
144};
145} // namespace details
146} // namespace spdlog
T c_str(T... args)
void connect(const std::string &host, int port)
Definition tcp_client.h:54
void send(const char *data, size_t n_bytes)
Definition tcp_client.h:120
Definition async.h:25
SPDLOG_INLINE void throw_spdlog_ex(const std::string &msg, int last_errno)
Definition common-inl.h:68
Definition format.h:897
T to_string(T... args)