simple-socket library 1.5.0
Loading...
Searching...
No Matches
SimpleSocket.cpp
1#include "SimpleSocket.h"
2#include "SockAddress.h"
3#include "TempFailure.h"
4
5#include <poll.h>
6#include <unistd.h>
7#include <cstring>
8#include <cerrno>
9
10using namespace NET;
11
12SocketException::SocketException( std::string_view message, bool inclSysMsg /* = true */)
13: m_message(message)
14, m_errorcode(0)
15{
16 if(inclSysMsg)
17 {
18 m_errorcode = errno;
19 m_message += ": ";
20 m_message += strerror(m_errorcode);
21 }
22}
23
24SimpleSocket::SimpleSocket( int domain, int type, int protocol)
25: m_peerDisconnected(false)
26, m_connectionOriented(type == SOCK_STREAM)
27{
28#ifdef SOCK_CLOEXEC
29 // prevent leaking socket descriptor on exec
30 type |= SOCK_CLOEXEC;
31#endif
32 if( (m_socket = ::socket( domain, type, protocol)) < 0)
33 throw SocketException("Socket creation failed (socket)");
34}
35
36SimpleSocket::SimpleSocket( int sockfd)
37: m_socket(sockfd)
38, m_peerDisconnected(false)
39, m_connectionOriented(true) // accepted sockets have connections
40{
41 if(sockfd < 0)
42 throw SocketException("Tried to initialize Socket with invalid Handle", false);
43}
44
45SimpleSocket::~SimpleSocket()
46{
47 if( m_socket >= 0) ::close(m_socket);
48}
49
50int SimpleSocket::send( const void* buffer, size_t len)
51{
52 int flags = 0;
53#ifdef MSG_NOSIGNAL
54 // do not generate SIGPIPE on closed connection
55 flags |= MSG_NOSIGNAL;
56#endif
57 int sent = TEMP_FAILURE_RETRY (::send( m_socket, (const raw_type*) buffer, len, flags));
58 if( sent < 0)
59 {
60 if( errno == ECONNRESET || errno == ECONNREFUSED || errno == EPIPE)
61 {
62 m_peerDisconnected = true;
63 return 0;
64 }
65 throw SocketException("Send failed (send)");
66 }
67 return sent;
68}
69
70int SimpleSocket::receive( void* buffer, size_t len)
71{
72 int ret = TEMP_FAILURE_RETRY (::recv( m_socket, (raw_type*) buffer, len, 0));
73 if( ret < 0)
74 {
75 if( errno == ECONNRESET && m_connectionOriented)
76 {
77 m_peerDisconnected = true;
78 return 0;
79 }
80 throw SocketException("Received failed (receive)");
81 }
82
83 if( ret == 0 && len != 0 && m_connectionOriented)
84 m_peerDisconnected = true;
85
86 return ret;
87}
88
89int SimpleSocket::timedReceive( void* buffer, size_t len, int timeout)
90{
91 struct pollfd poll;
92 poll.fd = m_socket;
93 poll.events = POLLIN | POLLPRI | POLLRDHUP;
94
95 int ret = TEMP_FAILURE_RETRY (::poll( &poll, 1, timeout));
96
97 if( ret == 0) return 0;
98 if( ret < 0) throw SocketException("timedReceive failed (poll)");
99
100 if( poll.revents & POLLRDHUP || poll.revents & POLLHUP)
101 m_peerDisconnected = true;
102
103 if( poll.revents & POLLIN || poll.revents & POLLPRI)
104 return receive( buffer, len);
105
106 if( poll.revents & (POLLERR | POLLNVAL))
107 throw SocketException("timedReceive failed (poll)");
108
109 return 0;
110}
111
113{
114 sockaddr addr;
115 std::memset( &addr, 0, sizeof(addr));
116 addr.sa_family = AF_UNSPEC;
118 if( ::connect( m_socket, &addr, sizeof(addr)) < 0)
119 {
120 if( errno != ECONNRESET)
121 throw SocketException("Disconnect failed (connect)");
122 }
123 m_peerDisconnected = false;
124}
125
127{
128 if( ::shutdown( m_socket, type) < 0)
129 throw SocketException("Shutdown failed (shutdown)");
130}
131
133{
134 return m_peerDisconnected;
135}
136
137void SimpleSocket::listen( int backlog)
138{
139 int ret = ::listen( m_socket, backlog);
140 if( ret < 0)
141 throw SocketException("Listen failed (listen)");
142}
143
145{
146#ifdef SOCK_CLOEXEC
147 // prevent leaking socket descriptor on exec
148 int ret = TEMP_FAILURE_RETRY (::accept4( m_socket, nullptr, nullptr, SOCK_CLOEXEC));
149#else
150 int ret = TEMP_FAILURE_RETRY (::accept( m_socket, nullptr, nullptr));
151#endif
152 if( ret < 0)
153 throw SocketException("Accept failed (accept)");
154
155 return ret;
156}
157
158int SimpleSocket::timedAccept( int timeout) const
159{
160 struct pollfd poll;
161 poll.fd = m_socket;
162 poll.events = POLLIN;
163
164 int ret = TEMP_FAILURE_RETRY (::poll( &poll, 1, timeout));
165 if( ret == 0) return -1; // invalid handle
166 if( ret < 0)
167 throw SocketException("timedAccept failed (poll)");
168
169 if( poll.revents & (POLLERR | POLLNVAL))
170 throw SocketException("timedAccept failed (poll)");
171
172 return SimpleSocket::accept();
173}
174
175int SimpleSocket::sendAll( const void* buffer, size_t len)
176{
177 size_t sent = 0;
178 while( sent != len)
179 {
180 const char* buf = static_cast<const char*>(buffer) + sent;
181 auto ret = send( buf, len - sent);
182 sent += static_cast<size_t>(ret);
183 if( m_peerDisconnected) break;
184 }
185 return sent;
186}
187
188int SimpleSocket::receiveAll( void* buffer, size_t len)
189{
190 size_t received = 0;
191 while( received != len)
192 {
193 char* buf = static_cast<char*>(buffer) + received;
194 auto ret = receive( buf, len - received);
195 if( ret == 0) break;
196 received += static_cast<size_t>(ret);
197 }
198 return received;
199}
int timedReceive(void *buffer, size_t len, int timeout)
receive data from a bound socket, return after the given timespan
void disconnect()
Disconnect and unset any foreign addresses.
int accept() const
allows stream sockets to accept connections into Handles
int receiveAll(void *buffer, size_t len)
receive exactly the requested amount unless the peer disconnects
void listen(int backlog=5)
listen for incoming connections
void shutdown(ShutdownDirection type)
shutdown the connection in the specified direction
int sendAll(const void *buffer, size_t len)
send data through a connected socket
int send(const void *buffer, size_t len)
send data through a connected socket
int receive(void *buffer, size_t len)
receive data from a bound socket
bool peerDisconnected() const noexcept
returns whether a currently associated peer disconnected
int timedAccept(int timeout) const
allows stream sockets timed accept
Signals a problem with the execution of a socket call.
SocketException(std::string_view message, bool inclSysMsg=true)