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