Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 10:24:43

0001 // Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
0002 // Distributed under the MIT License (http://opensource.org/licenses/MIT)
0003 
0004 #pragma once
0005 
0006 #ifdef _WIN32
0007 #    error include tcp_client-windows.h instead
0008 #endif
0009 
0010 // tcp client helper
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     // try to connect or throw on failure
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;       // IPv4
0061         hints.ai_socktype = SOCK_STREAM; // TCP
0062         hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
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         // Try each address until we successfully connect(2).
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         // set TCP_NODELAY
0104         int enable_flag = 1;
0105         ::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
0106 
0107         // prevent sigpipe on systems where MSG_NOSIGNAL is not available
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     // Send exactly n_bytes of the given data.
0118     // On error close the connection and throw.
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) // (probably should not happen but in any case..)
0137             {
0138                 break;
0139             }
0140             bytes_sent += static_cast<size_t>(write_result);
0141         }
0142     }
0143 };
0144 } // namespace details
0145 } // namespace spdlog