gsocket.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2013 Graeme Walker <graeme_walker@users.sourceforge.net>
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 "gcleanup.h"
28 #include "gmemory.h"
29 #include "gdebug.h"
30 
31 GNet::Socket::Socket( int domain , int type , int protocol ) :
32  m_reason( 0 )
33 {
34  m_socket = Descriptor( ::socket( domain , type , protocol ) ) ;
35  if( valid() )
36  {
37  prepare() ;
38  }
39  else
40  {
41  m_reason = reason() ;
42  G_DEBUG( "GNet::Socket::open: cannot open a socket (" << m_reason << ")" ) ;
43  }
44 }
45 
47  m_reason(0) ,
48  m_socket(s)
49 {
50  prepare() ;
51 }
52 
54 {
55  G::Cleanup::init() ; // ignore SIGPIPE
56  if( ! setNonBlock() )
57  {
58  m_reason = reason() ;
59  doClose() ;
60  G_ASSERT( !valid() ) ;
61  G_WARNING( "GNet::Socket::open: cannot make socket non-blocking (" << m_reason << ")" ) ;
62  }
63 }
64 
66 {
67  try
68  {
69  drop() ;
70  }
71  catch(...) // dtor
72  {
73  }
74 
75  try
76  {
77  if( valid() ) doClose() ;
78  G_ASSERT( !valid() ) ;
79  }
80  catch(...) // dtor
81  {
82  }
83 }
84 
85 void GNet::Socket::drop()
86 {
87  dropReadHandler() ;
88  dropWriteHandler() ;
89  dropExceptionHandler() ;
90 }
91 
92 bool GNet::Socket::valid() const
93 {
94  return valid( m_socket ) ;
95 }
96 
97 bool GNet::Socket::bind( const Address & local_address )
98 {
99  if( !valid() )
100  return false ;
101 
102  // optionally allow immediate re-use -- may cause problems
103  setReuse() ;
104 
105  G_DEBUG( "Socket::bind: binding " << local_address.displayString() << " on fd " << m_socket ) ;
106 
107  int rc = ::bind( m_socket.fd(), local_address.address() , local_address.length() ) ;
108  if( error(rc) )
109  {
110  m_reason = reason() ;
111  return false ;
112  }
113 
114  const bool debug = true ;
115  if( debug )
116  {
117  std::pair<bool,Address> pair = getLocalAddress() ;
118  if( ! pair.first )
119  {
120  G_WARNING( "GNet::Socket::bind: cannot get bound address" ) ;
121  return false ;
122  }
123 
124  G_DEBUG( "GNet::Socket::bind: bound " << pair.second.displayString() << " on fd " << m_socket ) ;
125  }
126 
127  return true ;
128 }
129 
130 bool GNet::Socket::connect( const Address & address , bool *done )
131 {
132  if( !valid() )
133  return false;
134 
135  G_DEBUG( "GNet::Socket::connect: connecting to " << address.displayString() ) ;
136  int rc = ::connect( m_socket.fd(), address.address(), address.length() ) ;
137  if( error(rc) )
138  {
139  m_reason = reason() ;
140 
141  if( G::Test::enabled( "slow-connect" ) )
142  sleep( 1 ) ;
143 
144  if( eInProgress() )
145  {
146  G_DEBUG( "GNet::Socket::connect: connection in progress" ) ;
147  if( done != NULL ) *done = false ;
148  return true ;
149  }
150 
151  G_DEBUG( "GNet::Socket::connect: synchronous connect failure: " << m_reason ) ;
152  return false;
153  }
154 
155  if( done != NULL ) *done = true ;
156  return true;
157 }
158 
160 {
161  if( static_cast<ssize_type>(len) < 0 )
162  G_WARNING( "GNet::Socket::write: too big" ) ; // EMSGSIZE from ::send() ?
163 
164  ssize_type nsent = ::send( m_socket.fd() , buf , len , 0 ) ;
165 
166  if( sizeError(nsent) ) // if -1
167  {
168  m_reason = reason() ;
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  m_reason = reason() ;
175  }
176  return nsent;
177 }
178 
180 {
181  struct linger options ;
182  options.l_onoff = 0 ;
183  options.l_linger = 0 ;
184  socklen_t sizeof_options = sizeof(options) ;
185  G_IGNORE_RETURN(int) ::setsockopt( m_socket.fd() , SOL_SOCKET ,
186  SO_LINGER , reinterpret_cast<char*>(&options) , sizeof_options ) ;
187 }
188 
190 {
191  int keep_alive = 1 ;
192  G_IGNORE_RETURN(int) ::setsockopt( m_socket.fd() , SOL_SOCKET ,
193  SO_KEEPALIVE , reinterpret_cast<char*>(&keep_alive) , sizeof(keep_alive) ) ;
194 }
195 
197 {
198  int on = 1 ;
199  G_IGNORE_RETURN(int) ::setsockopt( m_socket.fd() , SOL_SOCKET ,
200  SO_REUSEADDR , reinterpret_cast<char*>(&on) , sizeof(on) ) ;
201 }
202 
203 bool GNet::Socket::listen( int backlog )
204 {
205  int rc = ::listen( m_socket.fd() , backlog ) ;
206  if( error(rc) )
207  {
208  m_reason = reason() ;
209  return false ;
210  }
211  return true ;
212 }
213 
214 std::pair<bool,GNet::Address> GNet::Socket::getAddress( bool local ) const
215 {
216  std::pair<bool,Address> error_pair( false , Address::invalidAddress() ) ;
217 
218  if( !valid() )
219  return error_pair ;
220 
221  AddressStorage address_storage ;
222  int rc =
223  local ?
224  ::getsockname( m_socket.fd() , address_storage.p1() , address_storage.p2() ) :
225  ::getpeername( m_socket.fd() , address_storage.p1() , address_storage.p2() ) ;
226 
227  if( error(rc) )
228  {
229  const_cast<Socket*>(this)->m_reason = reason() ;
230  return error_pair ;
231  }
232 
233  return std::pair<bool,Address>( true , Address(address_storage) ) ;
234 }
235 
236 std::pair<bool,GNet::Address> GNet::Socket::getLocalAddress() const
237 {
238  return getAddress( true ) ;
239 }
240 
241 std::pair<bool,GNet::Address> GNet::Socket::getPeerAddress() const
242 {
243  return getAddress( false ) ;
244 }
245 
247 {
248  return getPeerAddress().first ;
249 }
250 
252 {
253  G_ASSERT( valid() ) ;
254  G_DEBUG( "GNet::Socket::addReadHandler: fd " << m_socket ) ;
255  EventLoop::instance().addRead( m_socket , handler ) ;
256 }
257 
259 {
260  EventLoop::instance().dropRead( m_socket ) ;
261 }
262 
264 {
265  G_ASSERT( valid() ) ;
266  G_DEBUG( "GNet::Socket::addWriteHandler: fd " << m_socket ) ;
267  EventLoop::instance().addWrite( m_socket , handler ) ;
268 }
269 
271 {
272  G_ASSERT( valid() ) ;
273  G_DEBUG( "GNet::Socket::addExceptionHandler: fd " << m_socket ) ;
274  EventLoop::instance().addException( m_socket , handler ) ;
275 }
276 
278 {
279  EventLoop::instance().dropWrite( m_socket ) ;
280 }
281 
283 {
284  EventLoop::instance().dropException( m_socket ) ;
285 }
286 
287 std::string GNet::Socket::asString() const
288 {
289  std::ostringstream ss ;
290  ss << m_socket ;
291  return ss.str() ;
292 }
293 
294 std::string GNet::Socket::reasonString() const
295 {
296  std::ostringstream ss ;
297  ss << m_reason ;
298  return ss.str() ;
299 }
300 
301 void GNet::Socket::shutdown( bool for_writing )
302 {
303  ::shutdown( m_socket.fd() , for_writing ? 1 : 0 ) ;
304 }
305 
307 {
308  return static_cast<int>( m_socket.fd() ) ;
309 }
310 
311 //==
312 
314  Socket( Address::defaultDomain() , SOCK_STREAM , 0 )
315 {
316  setNoLinger() ;
317  setKeepAlive() ;
318 }
319 
320 GNet::StreamSocket::StreamSocket( const Address & address_hint ) :
321  Socket( address_hint.domain() , SOCK_STREAM , 0 )
322 {
323  setNoLinger() ;
324  setKeepAlive() ;
325 }
326 
328  Socket( s )
329 {
330 }
331 
333 {
334 }
335 
337 {
338  if( len == 0 ) return 0 ;
339  G_ASSERT( valid() ) ;
340  ssize_type nread = ::recv( m_socket.fd() , buf , len , 0 ) ;
341  if( sizeError(nread) )
342  {
343  m_reason = reason() ;
344  G_DEBUG( "GNet::StreamSocket::read: fd " << m_socket << ": read error " << m_reason ) ;
345  return -1 ;
346  }
347  if( nread == 0 )
348  {
349  G_DEBUG( "GNet::StreamSocket::read: fd " << m_socket << ": read zero bytes" ) ;
350  }
351  return nread ;
352 }
353 
355 {
356  AcceptPair pair( NULL , Address::invalidAddress() ) ;
357 
358  sockaddr addr ;
359  socklen_t addr_length = sizeof(addr) ;
360  Descriptor new_socket( ::accept( m_socket.fd() , &addr, &addr_length ) ) ;
361  if( valid(new_socket) )
362  {
363  pair.second = Address( &addr , addr_length ) ;
364  G_DEBUG( "GNet::StreamSocket::accept: " << pair.second.displayString() ) ;
365  pair.first <<= new StreamSocket(new_socket) ;
366  pair.first.get()->setNoLinger() ;
367  }
368  else
369  {
370  m_reason = reason() ;
371  G_DEBUG( "GNet::StreamSocket::accept: failure" ) ;
372  }
373  return pair ;
374 }
375 
376 //==
377 
379  Socket( Address::defaultDomain() , SOCK_DGRAM , 0 )
380 {
381 }
382 
384  Socket( address_hint.domain() , SOCK_DGRAM , 0 )
385 {
386 }
387 
389 {
390 }
391 
393 {
394  int rc = ::connect( m_socket.fd() , 0 , 0 ) ;
395  if( error(rc) )
396  m_reason = reason() ;
397 }
398 
400 {
401  sockaddr sender ;
402  socklen_t sender_len = sizeof(sender) ;
403  ssize_type nread = ::recvfrom( m_socket.fd() , reinterpret_cast<char*>(buf) , len , 0 , &sender , &sender_len ) ;
404  if( sizeError(nread) )
405  {
406  m_reason = reason() ;
407  return -1 ;
408  }
409 
410  src_address = Address( &sender , sender_len ) ;
411  return nread ;
412 }
413 
415 {
416  G_DEBUG( "GNet::DatagramSocket::write: sending " << len << " bytes to " << dst.displayString() ) ;
417 
418  ssize_type nsent = ::sendto( m_socket.fd() , buf, len, 0, dst.address(), dst.length() ) ;
419  if( nsent < 0 )
420  {
421  m_reason = reason() ;
422  G_DEBUG( "GNet::DatagramSocket::write: write error " << m_reason ) ;
423  return -1 ;
424  }
425  return nsent ;
426 }
427 
ssize_type write(const char *buffer, size_type len, const Address &dst)
Sends a datagram to the given address.
Definition: gsocket.cpp:414
static int reason()
DatagramSocket()
Default constructor.
Definition: gsocket.cpp:378
bool listen(int backlog=1)
Starts the socket listening on the bound address for incoming connections or incoming datagrams...
Definition: gsocket.cpp:203
std::pair< bool, Address > getLocalAddress() const
Retrieves local address of the socket.
Definition: gsocket.cpp:236
virtual void addWrite(Descriptor fd, EventHandler &handler)=0
Adds the given event source descriptor and associated handler to the write list.
void dropReadHandler()
Reverses addReadHandler().
Definition: gsocket.cpp:258
void addExceptionHandler(EventHandler &handler)
Adds this socket to the event source list so that the given handler receives exception events...
Definition: gsocket.cpp:270
bool connect(const Address &addr, bool *done=NULL)
Initiates a connection to (or association with) the given address.
Definition: gsocket.cpp:130
virtual ssize_type write(const char *buf, size_type len)
Sends data.
Definition: gsocket.cpp:159
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:263
virtual ~DatagramSocket()
Destructor.
Definition: gsocket.cpp:388
std::pair< bool, Address > getAddress(bool) const
Definition: gsocket.cpp:214
A helper class for calling getsockname() and getpeername() and hiding the definition of sockaddr_stor...
Definition: gaddress.h:225
The Address class encapsulates an IP transport address.
Definition: gaddress.h:48
The Socket class encapsulates a non-blocking Unix socket file descriptor or a Windows 'SOCKET' handle...
Definition: gsocket.h:60
socklen_t * p2()
Returns the length pointer for 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:251
sockaddr * p1()
Returns the sockaddr pointer for getsockname()/getpeername() to write into.
virtual void dropRead(Descriptor fd)=0
Removes the given event source descriptor from the list of read sources.
static Address invalidAddress()
Returns an invalid address.
virtual ~StreamSocket()
Destructor.
Definition: gsocket.cpp:332
int m_reason
Definition: gsocket.h:230
A network file descriptor.
Definition: gdescriptor.h:37
const sockaddr * address() const
Returns the sockaddr address.
StreamSocket()
Default constructor. Check with valid().
Definition: gsocket.cpp:313
bool bind(const Address &address)
Binds the socket with an INADDR_ANY network address and the port number taken from the given address...
Definition: gsocket.cpp:97
int fd(Credentials) const
Returns the socket descriptor as an integer.
Definition: gsocket.cpp:306
void dropExceptionHandler()
Reverses addExceptionHandler().
Definition: gsocket.cpp:282
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:277
std::string reasonString() const
Returns the failure reason as a string.
Definition: gsocket.cpp:294
void disconnect()
Releases the association between two datagram endpoints reversing the effect of the previous Socket::...
Definition: gsocket.cpp:392
std::pair< bool, Address > getPeerAddress() const
Retrieves address of socket's peer.
Definition: gsocket.cpp:241
A derivation of Socket for a stream socket.
Definition: gsocket.h:270
virtual void addRead(Descriptor fd, EventHandler &handler)=0
Adds the given event source descriptor and associated handler to the read list.
#define G_ASSERT(test)
Definition: gassert.h:30
std::string displayString(bool with_port=true, bool with_scope_id=false) const
Returns a string which represents the address for debugging and diagnostics purposes.
size_t size_type
Definition: gsocket.h:63
first_type first
Definition: gsocket.h:252
ssize_type read(void *buffer, size_type len, Address &src)
Reads a datagram and returns the sender's address by reference.
Definition: gsocket.cpp:399
static bool enabled()
Returns true if test features are enabled.
Definition: gtest.cpp:46
socklen_t length() const
Returns the size of the sockaddr address.
virtual ~Socket()
Destructor.
Definition: gsocket.cpp:65
Socket(int domain, int type, int protocol)
Constructor used by derived classes.
Definition: gsocket.cpp:31
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:246
A base class for classes that handle asynchronous socket events.
Definition: geventhandler.h:54
void setReuse()
Definition: gsocket.cpp:196
bool hasPeer() const
Returns true if the socket has a valid peer.
Definition: gsocket.cpp:246
ssize_type read(char *buffer, size_type buffer_length)
Reads data from the socket stream.
Definition: gsocket.cpp:336
#define G_DEBUG(expr)
Definition: glog.h:95
second_type second
Definition: gsocket.h:253
static void init()
An optional early-initialisation function.
void setNoLinger()
Definition: gsocket.cpp:179
bool valid() const
Returns true if the socket handle is valid (open).
Definition: gsocket.cpp:92
void setKeepAlive()
Definition: gsocket.cpp:189
A credentials class that allows SocketProtocol to call Socket::fd().
Definition: gsocket.h:66
void shutdown(bool for_writing=true)
Shuts the socket for writing (or reading).
Definition: gsocket.cpp:301
Descriptor m_socket
Definition: gsocket.h:231
AcceptPair accept()
Accepts an incoming connection, returning a new()ed socket and the peer address.
Definition: gsocket.cpp:354
ssize_t ssize_type
Definition: gsocket.h:64
virtual void dropWrite(Descriptor fd)=0
Removes the given event source descriptor from the list of write sources.
static EventLoop & instance()
Returns a reference to an instance of the class, if any.
Definition: geventloop.cpp:43
void prepare()
Definition: gsocket.cpp:53
std::string asString() const
Returns the socket handle as a string.
Definition: gsocket.cpp:287
#define G_WARNING(expr)
Definition: glog.h:107