VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gsocket.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2017 Graeme Walker
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gsocket.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gnet.h"
23 #include "gtest.h"
24 #include "gsleep.h"
25 #include "gassert.h"
26 #include "gsocket.h"
27 #include "gmsg.h"
28 #include "gcleanup.h"
29 #include "gstr.h"
30 #include "gdebug.h"
31 
32 GNet::Socket::Socket( int domain , int type , int protocol ) :
33  m_reason(0) ,
34  m_domain(domain)
35 {
36  m_socket = Descriptor( ::socket( domain , type , protocol ) ) ;
37  if( !m_socket.valid() )
38  {
39  saveReason() ;
40  throw SocketError( "cannot create socket" , m_reason_string ) ;
41  }
42  prepare() ;
43 }
44 
46  m_reason(0) ,
47  m_socket(s)
48 {
49  prepare() ;
50 }
51 
52 void GNet::Socket::prepare()
53 {
54  G::Cleanup::init() ; // ignore SIGPIPE
55  if( ! setNonBlock() )
56  {
57  saveReason() ;
58  doClose() ;
59  G_WARNING( "GNet::Socket::open: cannot make socket non-blocking (" << m_reason_string << ")" ) ;
60  }
61 }
62 
64 {
65  try
66  {
67  drop() ; // first
68  doClose() ;
69  }
70  catch(...) // dtor
71  {
72  }
73 }
74 
75 void GNet::Socket::drop()
76 {
77  dropReadHandler() ;
78  dropWriteHandler() ;
79  dropExceptionHandler() ;
80 }
81 
82 void GNet::Socket::saveReason()
83 {
84  m_reason = reason() ;
85  m_reason_string = reasonStringImp( m_reason ) ;
86 }
87 
88 void GNet::Socket::saveReason() const
89 {
90  const_cast<GNet::Socket*>(this)->saveReason() ;
91 }
92 
93 void GNet::Socket::bind( const Address & local_address )
94 {
95  G_DEBUG( "Socket::bind: binding " << local_address.displayString() << " on fd " << m_socket ) ;
96 
97  if( local_address.domain() != m_domain )
98  throw SocketBindError( "address family does not match the socket domain" ) ;
99 
100  setOptionReuse() ; // allow us to rebind another socket's (eg. time-wait zombie's) address
101  //setOptionExclusive() ; // don't allow anyone else to bind our address
102  setOptionPureV6( local_address.family() == Address::Family::ipv6() ) ;
103 
104  int rc = ::bind( m_socket.fd() , local_address.address() , local_address.length() ) ;
105  if( error(rc) )
106  {
107  saveReason() ;
108  throw SocketBindError( local_address.displayString() , m_reason_string ) ;
109  }
110 }
111 
112 bool GNet::Socket::bind( const Address & local_address , NoThrow )
113 {
114  G_DEBUG( "Socket::bind: binding " << local_address.displayString() << " on fd " << m_socket ) ;
115  if( local_address.domain() != m_domain ) return false ;
116 
117  setOptionReuse() ; // allow us to rebind another socket's (eg. time-wait zombie's) address
118  //setOptionExclusive() ; // don't allow anyone else to bind our address
119  setOptionPureV6( local_address.family() == Address::Family::ipv6() ) ;
120 
121  int rc = ::bind( m_socket.fd() , local_address.address() , local_address.length() ) ;
122  return ! error(rc) ;
123 }
124 
125 bool GNet::Socket::connect( const Address & address , bool *done )
126 {
127  G_DEBUG( "GNet::Socket::connect: connecting to " << address.displayString() ) ;
128  if( address.domain() != m_domain )
129  {
130  G_WARNING( "GNet::Socket::connect: cannot connect: address family does not match "
131  "the socket domain (" << address.domain() << "," << m_domain << ")" ) ;
132  return false ;
133  }
134 
135  setOptionPureV6( address.family() == Address::Family::ipv6() , NoThrow() ) ; // ignore errors - may fail if already bound
136 
137  int rc = ::connect( m_socket.fd() , address.address() , address.length() ) ;
138  if( error(rc) )
139  {
140  saveReason() ;
141 
142  if( G::Test::enabled( "slow-connect" ) )
143  sleep( 1 ) ;
144 
145  if( eInProgress() )
146  {
147  G_DEBUG( "GNet::Socket::connect: connection in progress" ) ;
148  if( done != nullptr ) *done = false ;
149  return true ;
150  }
151 
152  G_DEBUG( "GNet::Socket::connect: synchronous connect failure: " << m_reason ) ;
153  return false;
154  }
155 
156  if( done != nullptr ) *done = true ;
157  return true ;
158 }
159 
160 GNet::Socket::ssize_type GNet::Socket::write( const char * buf , size_type len )
161 {
162  if( static_cast<ssize_type>(len) < 0 )
163  G_WARNING( "GNet::Socket::write: too big" ) ; // should get EMSGSIZE from ::send()
164 
165  ssize_type nsent = ::send( m_socket.fd() , buf , len , MSG_NOSIGNAL ) ; // no SIGPIPE
166  if( sizeError(nsent) ) // if -1
167  {
168  saveReason() ;
169  G_DEBUG( "GNet::Socket::write: write error " << m_reason ) ;
170  return -1 ;
171  }
172  else if( nsent < 0 || static_cast<size_type>(nsent) < len )
173  {
174  saveReason() ;
175  }
176  return nsent;
177 }
178 
179 void GNet::Socket::listen( int backlog )
180 {
181  int rc = ::listen( m_socket.fd() , backlog ) ;
182  if( error(rc) )
183  {
184  saveReason() ;
185  throw SocketError( "cannot listen on socket" , m_reason_string ) ;
186  }
187 }
188 
189 std::pair<bool,GNet::Address> GNet::Socket::getAddress( bool local ) const
190 {
191  std::pair<bool,Address> error_pair( false , Address::defaultAddress() ) ;
192  AddressStorage address_storage ;
193  int rc =
194  local ?
195  ::getsockname( m_socket.fd() , address_storage.p1() , address_storage.p2() ) :
196  ::getpeername( m_socket.fd() , address_storage.p1() , address_storage.p2() ) ;
197 
198  if( error(rc) )
199  {
200  saveReason() ;
201  return error_pair ;
202  }
203 
204  return std::pair<bool,Address>( true , Address(address_storage) ) ;
205 }
206 
207 std::pair<bool,GNet::Address> GNet::Socket::getLocalAddress() const
208 {
209  return getAddress( true ) ;
210 }
211 
212 std::pair<bool,GNet::Address> GNet::Socket::getPeerAddress() const
213 {
214  return getAddress( false ) ;
215 }
216 
218 {
219  return getPeerAddress().first ;
220 }
221 
223 {
224  G_DEBUG( "GNet::Socket::addReadHandler: fd " << m_socket ) ;
225  EventLoop::instance().addRead( m_socket , handler ) ;
226 }
227 
229 {
230  EventLoop::instance().dropRead( m_socket ) ;
231 }
232 
234 {
235  G_DEBUG( "GNet::Socket::addWriteHandler: fd " << m_socket ) ;
236  EventLoop::instance().addWrite( m_socket , handler ) ;
237 }
238 
240 {
241  G_DEBUG( "GNet::Socket::addExceptionHandler: fd " << m_socket ) ;
242  EventLoop::instance().addException( m_socket , handler ) ;
243 }
244 
246 {
247  EventLoop::instance().dropWrite( m_socket ) ;
248 }
249 
251 {
252  EventLoop::instance().dropException( m_socket ) ;
253 }
254 
255 std::string GNet::Socket::asString() const
256 {
257  std::ostringstream ss ;
258  ss << m_socket ;
259  return ss.str() ;
260 }
261 
262 std::string GNet::Socket::reasonString() const
263 {
264  std::ostringstream ss ;
265  ss << m_reason ;
266  return ss.str() ;
267 }
268 
269 void GNet::Socket::shutdown( bool for_writing )
270 {
271  ::shutdown( m_socket.fd() , for_writing ? 1 : 0 ) ;
272 }
273 
274 SOCKET GNet::Socket::fd() const
275 {
276  return m_socket.fd() ;
277 }
278 
279 //==
280 
281 GNet::StreamSocket::StreamSocket( int address_domain ) :
282  Socket( address_domain , SOCK_STREAM , 0 )
283 {
284  setOptionNoLinger() ;
285  setOptionKeepAlive() ;
286 }
287 
289  Socket( s )
290 {
291 }
292 
294 {
295 }
296 
297 GNet::Socket::ssize_type GNet::StreamSocket::read( char * buf , size_type len )
298 {
299  if( len == 0 )
300  return 0 ;
301 
302  ssize_type nread = G::Msg::recv( m_socket.fd() , buf , len , 0 ) ;
303  if( sizeError(nread) )
304  {
305  saveReason() ;
306  G_DEBUG( "GNet::StreamSocket::read: cannot read from " << m_socket.fd() ) ;
307  return -1 ;
308  }
309  return nread ;
310 }
311 
312 GNet::Socket::ssize_type GNet::StreamSocket::write( const char * buf , size_type len )
313 {
314  return Socket::write( buf , len ) ;
315 }
316 
318 {
319  AddressStorage addr ;
320  Descriptor new_socket( ::accept(m_socket.fd(),addr.p1(),addr.p2()) ) ;
321  if( ! new_socket.valid() )
322  {
323  saveReason() ;
324  throw SocketError( "cannot accept on socket" , m_reason_string ) ;
325  }
326 
327  if( G::Test::enabled("accept-throws") )
328  throw SocketError( "testing" ) ;
329 
330  AcceptPair pair ;
331  pair.second = Address( addr.p() , addr.n() ) ;
332  G_DEBUG( "GNet::StreamSocket::accept: accepted from socket " << m_socket.fd()
333  << " onto socket " << new_socket << " with address " << pair.second.displayString() ) ;
334  pair.first.reset( new StreamSocket(new_socket) ) ;
335  pair.first.get()->setOptionNoLinger() ;
336  return pair ;
337 }
338 
339 //==
340 
341 GNet::DatagramSocket::DatagramSocket( int address_domain ) :
342  Socket( address_domain , SOCK_DGRAM , 0 )
343 {
344 }
345 
347 {
348 }
349 
351 {
352  int rc = ::connect( m_socket.fd() , 0 , 0 ) ;
353  if( error(rc) )
354  saveReason() ;
355 }
356 
357 GNet::Socket::ssize_type GNet::DatagramSocket::read( char * buf , size_type len )
358 {
359  if( len == 0 ) return 0 ;
360  sockaddr sender ; // not used
361  socklen_t sender_len = sizeof(sender) ;
362  ssize_type nread = G::Msg::recvfrom( m_socket.fd() , buf , len , 0 , &sender , &sender_len ) ;
363  if( sizeError(nread) )
364  {
365  saveReason() ;
366  return -1 ;
367  }
368  return nread ;
369 }
370 
371 GNet::Socket::ssize_type GNet::DatagramSocket::readfrom( char * buf , size_type len , Address & src_address )
372 {
373  if( len == 0 ) return 0 ;
374  sockaddr sender ;
375  socklen_t sender_len = sizeof(sender) ;
376  ssize_type nread = G::Msg::recvfrom( m_socket.fd() , buf , len , 0 , &sender , &sender_len ) ;
377  if( sizeError(nread) )
378  {
379  saveReason() ;
380  return -1 ;
381  }
382  src_address = Address( &sender , sender_len ) ;
383  return nread ;
384 }
385 
386 GNet::Socket::ssize_type GNet::DatagramSocket::writeto( const char * buf , size_type len , const Address & dst )
387 {
388  G_DEBUG( "GNet::DatagramSocket::write: sending " << len << " bytes to " << dst.displayString() ) ;
389 
390  ssize_type nsent = G::Msg::sendto( m_socket.fd() , buf , len , 0 , dst.address() , dst.length() ) ;
391  if( nsent < 0 )
392  {
393  saveReason() ;
394  G_DEBUG( "GNet::DatagramSocket::write: write error " << m_reason ) ;
395  return -1 ;
396  }
397  return nsent ;
398 }
399 
400 GNet::Socket::ssize_type GNet::DatagramSocket::write( const char * buf , size_type len )
401 {
402  return Socket::write( buf , len ) ;
403 }
404 
405 void GNet::Socket::setOptionKeepAlive()
406 {
407  setOption( SOL_SOCKET , "so_keepalive" , SO_KEEPALIVE , 1 ) ;
408 }
409 
410 void GNet::Socket::setOptionNoLinger()
411 {
412  struct linger options ;
413  options.l_onoff = 0 ;
414  options.l_linger = 0 ;
415  bool ok = setOptionImp( SOL_SOCKET , SO_LINGER , reinterpret_cast<char*>(&options) , sizeof(options) ) ;
416  if( !ok )
417  {
418  saveReason() ;
419  throw SocketError( "cannot set so_linger" , m_reason_string ) ;
420  }
421 }
422 
423 bool GNet::Socket::setOption( int level , const char * , int op , int arg , NoThrow )
424 {
425  const void * const vp = reinterpret_cast<const void*>(&arg) ;
426  bool ok = setOptionImp( level , op , vp , sizeof(int) ) ;
427  if( !ok )
428  saveReason() ;
429  return ok ;
430 }
431 
432 void GNet::Socket::setOption( int level , const char * opp , int op , int arg )
433 {
434  if( !setOption( level , opp , op , arg , NoThrow() ) )
435  throw SocketError( opp , m_reason_string ) ;
436 }
437 
438 /// \file gsocket.cpp
Family family() const
Returns the address family.
virtual ssize_type read(char *buffer, size_type len)
Override from Socket::read().
Definition: gsocket.cpp:357
std::pair< bool, Address > getLocalAddress() const
Retrieves local address of the socket.
Definition: gsocket.cpp:207
void listen(int backlog=1)
Starts the socket listening on the bound address for incoming connections or incoming datagrams...
Definition: gsocket.cpp:179
virtual void addWrite(Descriptor fd, EventHandler &handler)=0
Adds the given event source descriptor and associated handler to the write list.
bool connect(const Address &addr, bool *done=nullptr)
Initiates a connection to (or association with) the given address.
Definition: gsocket.cpp:125
void dropReadHandler()
Reverses addReadHandler().
Definition: gsocket.cpp:228
void addExceptionHandler(EventHandler &handler)
Adds this socket to the event source list so that the given handler receives exception events...
Definition: gsocket.cpp:239
void addWriteHandler(EventHandler &handler)
Adds this socket to the event source list so that the given handler receives write events when flow c...
Definition: gsocket.cpp:233
virtual ~DatagramSocket()
Destructor.
Definition: gsocket.cpp:346
and hiding the definition of sockaddr_storage.
Definition: gaddress.h:200
The GNet::Address class encapsulates a TCP/UDP transport address.
Definition: gaddress.h:55
The Socket class encapsulates a non-blocking Unix socket file descriptor or a Windows 'SOCKET' handle...
Definition: gsocket.h:56
socklen_t * p2()
Returns the length pointer for accept()/getsockname()/getpeername() to write into.
void addReadHandler(EventHandler &handler)
Adds this socket to the event source list so that the given handler receives read events...
Definition: gsocket.cpp:222
sockaddr * p1()
Returns the sockaddr pointer for accept()/getsockname()/getpeername() to write into.
virtual void dropRead(Descriptor fd)=0
Removes the given event source descriptor from the list of read sources.
StreamSocket(int address_domain)
Constructor with a hint of the bind()/connect() address to be used later.
Definition: gsocket.cpp:281
virtual SOCKET fd() const
Returns the socket descriptor. Override from G::ReadWrite.
Definition: gsocket.cpp:274
virtual ~StreamSocket()
Destructor.
Definition: gsocket.cpp:293
A class that encapsulates a network file descriptor and hides knowledge of its o/s-spefific error val...
Definition: gdescriptor.h:37
const sockaddr * address() const
Returns the sockaddr address.
static ssize_t sendto(int, const void *, size_t, int, const sockaddr *, socklen_t, int fd_to_send=-1)
A sendto() replacement using sendmsg().
Definition: gmsg.cpp:38
ssize_type writeto(const char *buffer, size_type len, const Address &dst)
Sends a datagram to the given address.
Definition: gsocket.cpp:386
DatagramSocket(int address_domain)
Constructor with a hint of a local address.
Definition: gsocket.cpp:341
void dropExceptionHandler()
Reverses addExceptionHandler().
Definition: gsocket.cpp:250
virtual void dropException(Descriptor fd)=0
Removes the given event source descriptor from the list of exception sources.
void dropWriteHandler()
Reverses addWriteHandler().
Definition: gsocket.cpp:245
void bind(const Address &)
Binds the socket with the given address.
Definition: gsocket.cpp:93
const sockaddr * p() const
Returns the pointer.
std::string reasonString() const
Returns the failure reason as a string.
Definition: gsocket.cpp:262
void disconnect()
Releases the association between two datagram endpoints reversing the effect of the previous Socket::...
Definition: gsocket.cpp:350
std::pair< bool, Address > getPeerAddress() const
Retrieves address of socket's peer.
Definition: gsocket.cpp:212
A derivation of GNet::Socket for a stream socket.
Definition: gsocket.h:245
virtual void addRead(Descriptor fd, EventHandler &handler)=0
Adds the given event source descriptor and associated handler to the read list.
static Address defaultAddress()
Returns a default address, being the IPv4 wildcard address with a zero port number.
static bool enabled()
Returns true if test features are enabled.
Definition: gtest.cpp:49
socklen_t length() const
Returns the size of the sockaddr address. See address().
bool valid() const
Returns true if the descriptor is valid.
virtual ~Socket()
Destructor.
Definition: gsocket.cpp:63
Socket(int domain, int type, int protocol)
Constructor used by derived classes.
Definition: gsocket.cpp:32
virtual void addException(Descriptor fd, EventHandler &handler)=0
Adds the given event source descriptor and associated handler to the exception list.
A class which is used to return a new()ed socket to calling code, together with associated informatio...
Definition: gsocket.h:232
static ssize_t recvfrom(int, void *, size_t, int, sockaddr *, socklen_t *, int *fd_received_p=nullptr)
A recvfrom() replacement using recvmsg().
Definition: gmsg.cpp:82
int domain() const
Returns the address 'domain', eg. PF_INET.
A base class for classes that handle asynchronous events from the event loop.
Definition: geventhandler.h:78
virtual ssize_type write(const char *buf, size_type len) override=0
Writes to the socket.
Definition: gsocket.cpp:160
bool hasPeer() const
Returns true if the socket has a valid peer.
Definition: gsocket.cpp:217
virtual ssize_type read(char *buffer, size_type buffer_length)
Override from Socket::read().
Definition: gsocket.cpp:297
virtual ssize_type write(const char *buf, size_type len)
Override from Socket::write().
Definition: gsocket.cpp:312
static void init()
An optional early-initialisation function. May be called more than once.
void shutdown(bool for_writing=true)
Shuts the socket for writing (or reading).
Definition: gsocket.cpp:269
AcceptPair accept()
Accepts an incoming connection, returning a new()ed socket and the peer address.
Definition: gsocket.cpp:317
Overload discriminator class for GNet::Socket.
Definition: gsocket.h:63
socklen_t n() const
Returns the length.
virtual ssize_type write(const char *buffer, size_type len)
Override from Socket::write().
Definition: gsocket.cpp:400
virtual void dropWrite(Descriptor fd)=0
Removes the given event source descriptor from the list of write sources.
std::string displayString() const
Returns a string which represents the transport address for debugging and diagnostics purposes...
static EventLoop & instance()
Returns a reference to an instance of the class, if any.
Definition: geventloop.cpp:43
ssize_type readfrom(char *buffer, size_type len, Address &src)
Reads a datagram and returns the sender's address by reference.
Definition: gsocket.cpp:371
std::string asString() const
Returns the socket handle as a string.
Definition: gsocket.cpp:255
static ssize_t recv(int, void *, size_t, int, int *fd_received_p=nullptr)
A recv() replacement using recvmsg().
Definition: gmsg.cpp:76