File indexing completed on 2025-01-30 10:24:43
0001
0002
0003
0004 #pragma once
0005
0006 #ifdef _WIN32
0007 # error include tcp_client-windows.h instead
0008 #endif
0009
0010
0011 #include <spdlog/common.h>
0012 #include <spdlog/details/os.h>
0013
0014 #include <sys/socket.h>
0015 #include <arpa/inet.h>
0016 #include <unistd.h>
0017 #include <netdb.h>
0018 #include <netinet/tcp.h>
0019
0020 #include <string>
0021
0022 namespace spdlog {
0023 namespace details {
0024 class tcp_client
0025 {
0026 int socket_ = -1;
0027
0028 public:
0029 bool is_connected() const
0030 {
0031 return socket_ != -1;
0032 }
0033
0034 void close()
0035 {
0036 if (is_connected())
0037 {
0038 ::close(socket_);
0039 socket_ = -1;
0040 }
0041 }
0042
0043 int fd() const
0044 {
0045 return socket_;
0046 }
0047
0048 ~tcp_client()
0049 {
0050 close();
0051 }
0052
0053
0054 void connect(const std::string &host, int port)
0055 {
0056 close();
0057 struct addrinfo hints
0058 {};
0059 memset(&hints, 0, sizeof(struct addrinfo));
0060 hints.ai_family = AF_INET;
0061 hints.ai_socktype = SOCK_STREAM;
0062 hints.ai_flags = AI_NUMERICSERV;
0063 hints.ai_protocol = 0;
0064
0065 auto port_str = std::to_string(port);
0066 struct addrinfo *addrinfo_result;
0067 auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
0068 if (rv != 0)
0069 {
0070 throw_spdlog_ex(fmt_lib::format("::getaddrinfo failed: {}", gai_strerror(rv)));
0071 }
0072
0073
0074 int last_errno = 0;
0075 for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
0076 {
0077 #if defined(SOCK_CLOEXEC)
0078 const int flags = SOCK_CLOEXEC;
0079 #else
0080 const int flags = 0;
0081 #endif
0082 socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol);
0083 if (socket_ == -1)
0084 {
0085 last_errno = errno;
0086 continue;
0087 }
0088 rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen);
0089 if (rv == 0)
0090 {
0091 break;
0092 }
0093 last_errno = errno;
0094 ::close(socket_);
0095 socket_ = -1;
0096 }
0097 ::freeaddrinfo(addrinfo_result);
0098 if (socket_ == -1)
0099 {
0100 throw_spdlog_ex("::connect failed", last_errno);
0101 }
0102
0103
0104 int enable_flag = 1;
0105 ::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
0106
0107
0108 #if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
0109 ::setsockopt(socket_, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
0110 #endif
0111
0112 #if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
0113 # error "tcp_sink would raise SIGPIPE since niether SO_NOSIGPIPE nor MSG_NOSIGNAL are available"
0114 #endif
0115 }
0116
0117
0118
0119 void send(const char *data, size_t n_bytes)
0120 {
0121 size_t bytes_sent = 0;
0122 while (bytes_sent < n_bytes)
0123 {
0124 #if defined(MSG_NOSIGNAL)
0125 const int send_flags = MSG_NOSIGNAL;
0126 #else
0127 const int send_flags = 0;
0128 #endif
0129 auto write_result = ::send(socket_, data + bytes_sent, n_bytes - bytes_sent, send_flags);
0130 if (write_result < 0)
0131 {
0132 close();
0133 throw_spdlog_ex("write(2) failed", errno);
0134 }
0135
0136 if (write_result == 0)
0137 {
0138 break;
0139 }
0140 bytes_sent += static_cast<size_t>(write_result);
0141 }
0142 }
0143 };
0144 }
0145 }