00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "CeylanStreamSocket.h"
00028
00029
00030 #include "CeylanLogPlug.h"
00031 #include "CeylanOperators.h"
00032 #include "CeylanNetwork.h"
00033
00034
00035
00036 #include "CeylanSystemSpecificSocketAddress.h"
00037
00038
00039
00040 #ifdef CEYLAN_USES_CONFIG_H
00041 #include "CeylanConfig.h"
00042 #endif // CEYLAN_USES_CONFIG_H
00043
00044
00045
00046
00047 extern "C"
00048 {
00049
00050
00051 #ifdef CEYLAN_USES_XTI_H
00052 #include <xti.h>
00053 #endif // CEYLAN_USES_XTI_H
00054
00055
00056 #ifdef CEYLAN_USES_NETINET_TCP_H
00057 #include <netinet/tcp.h>
00058 #endif // CEYLAN_USES_NETINET_TCP_H
00059
00060
00061 #ifdef CEYLAN_USES_SYS_SOCKET_H
00062 #include <sys/socket.h>
00063 #endif // CEYLAN_USES_SYS_SOCKET_H
00064
00065
00066 #ifdef CEYLAN_USES_NETINET_IN_H
00067 #include <netinet/in.h>
00068 #endif // CEYLAN_USES_NETINET_IN_H
00069
00070
00071 #ifdef CEYLAN_USES_RESOLV_H
00072 #include <resolv.h>
00073 #endif // CEYLAN_USES_RESOLV_H
00074
00075
00076 #ifdef CEYLAN_USES_STRINGS_H
00077 #include <strings.h>
00078 #endif // CEYLAN_USES_STRINGS_H
00079
00080
00081 #ifdef CEYLAN_USES_SYS_SELECT_H
00082 #include <sys/select.h>
00083 #endif // CEYLAN_USES_SYS_SELECT_H
00084
00085
00086 #ifdef CEYLAN_USES_UNISTD_H
00087 #include <unistd.h>
00088 #endif // CEYLAN_USES_UNISTD_H
00089
00090
00091 #ifdef CEYLAN_USES_FCNTL_H
00092 #include <fcntl.h>
00093 #endif // CEYLAN_USES_STRINGS_H
00094
00095
00096 #ifdef CEYLAN_USES_SYS_IOCTL_H
00097 #include <sys/ioctl.h>
00098 #endif // CEYLAN_USES_SYS_IOCTL_H
00099
00100
00101 #ifdef CEYLAN_USES_WINSOCK2_H
00102 #include <winsock2.h>
00103 #endif // CEYLAN_USES_WINSOCK2_H
00104
00105
00106 }
00107
00108
00109
00110 using namespace Ceylan::System ;
00111 using namespace Ceylan::Network ;
00112 using namespace Ceylan::Log ;
00113
00114
00115 using std::string ;
00116
00117
00118
00119
00120 StreamSocket::StreamSocketException::StreamSocketException(
00121 const std::string & reason ) :
00122 SocketException( reason )
00123 {
00124
00125 }
00126
00127
00128
00129 StreamSocket::StreamSocketException::~StreamSocketException() throw()
00130 {
00131
00132 }
00133
00134
00135
00136
00137 StreamSocket::StreamSocket( bool blocking,
00138 bool sacrificeThroughputToPacketTiming ) :
00139 Socket( blocking ),
00140 _nagleAlgorithmDeactivated( sacrificeThroughputToPacketTiming )
00141 {
00142
00143
00144
00145 #if CEYLAN_USES_NETWORK
00146
00147
00148 #if CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00149 LogPlug::trace( "StreamSocket empty constructor" ) ;
00150 #endif // CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00151
00152
00153 #else // CEYLAN_USES_NETWORK
00154
00155 throw SocketException( "StreamSocket empty constructor failed: "
00156 "network support not available." ) ;
00157
00158 #endif // CEYLAN_USES_NETWORK
00159
00160 }
00161
00162
00163
00164 StreamSocket::StreamSocket( Port port, bool blocking,
00165 bool sacrificeThroughputToPacketTiming ) :
00166 Socket( port, blocking ),
00167 _nagleAlgorithmDeactivated( sacrificeThroughputToPacketTiming )
00168 {
00169
00170
00171
00172 #if CEYLAN_USES_NETWORK
00173
00174
00175 createSocket( _port ) ;
00176
00177 #else // CEYLAN_USES_NETWORK
00178
00179 throw SocketException( "StreamSocket port-based constructor failed: "
00180 "network support not available." ) ;
00181
00182 #endif // CEYLAN_USES_NETWORK
00183
00184 }
00185
00186
00187
00188 StreamSocket::~StreamSocket() throw()
00189 {
00190
00191
00192
00193
00194
00195
00196
00197 }
00198
00199
00200
00201 void StreamSocket::createSocket( Port port )
00202 {
00203
00204 #if CEYLAN_USES_NETWORK
00205
00206 _port = port ;
00207
00208 #if CEYLAN_ARCH_WINDOWS
00209
00210 _originalFD = static_cast<System::FileDescriptor>(
00211 ::socket(
00212 AF_INET,
00213 SOCK_STREAM,
00214 IPPROTO_TCP ) ) ;
00215
00216 if ( _originalFD == INVALID_SOCKET )
00217 throw SocketException( "StreamSocket::createSocket failed: "
00218 + Network::explainSocketError() ) ;
00219
00220 #else // CEYLAN_ARCH_WINDOWS
00221
00222 _originalFD =::socket( PF_INET,
00223 SOCK_STREAM, 0 ) ;
00224
00225 if ( _originalFD == -1 )
00226 throw SocketException( "StreamSocket::createSocket failed: "
00227 + System::explainError() ) ;
00228
00229 #endif // CEYLAN_ARCH_WINDOWS
00230
00231
00232 #if CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00233 LogPlug::debug( "StreamSocket::createSocket: "
00234 "this socket, whose original file descriptor is "
00235 + Ceylan::toString( _originalFD ) + ", will be associated to port "
00236 + Ceylan::toString( _port ) + "." ) ;
00237 #endif // CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00238
00239
00240 if ( ! isBlocking() )
00241 {
00242
00243
00244
00245
00246
00247
00248 try
00249 {
00250 setBlocking( false ) ;
00251 }
00252 catch( Stream::NonBlockingNotSupportedException & e )
00253 {
00254 throw SocketException( "Socket port-less constructor: "
00255 "unable to set this socket in non-blocking mode: "
00256 + e.toString() ) ;
00257 }
00258 }
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269 _address->blank() ;
00270
00271 _address->_socketAddress.sin_family =
00272 AF_INET ;
00273
00274 _address->_socketAddress.sin_port = htons( _port ) ;
00275
00276 #else // CEYLAN_USES_NETWORK
00277
00278 throw Features::FeatureNotAvailableException(
00279 "StreamSocket::createSocket: network support not available." ) ;
00280
00281 #endif // CEYLAN_USES_NETWORK
00282
00283 }
00284
00285
00286
00287 void StreamSocket::setNagleAlgorithmTo( bool activated )
00288 {
00289
00290
00291
00292
00293
00294
00295
00296
00297 #if CEYLAN_USES_NETWORK
00298
00299 LogPlug::debug( "StreamSocket::setNagleAlgorithmTo: "
00300 "setting the algorithm to "
00301 + Ceylan::toString( activated ) ) ;
00302
00303 int optionValue ;
00304
00305 if ( activated )
00306 optionValue = 0 ;
00307 else
00308 optionValue = 1 ;
00309
00310
00311 #if CEYLAN_ARCH_WINDOWS
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322 #pragma warning( push )
00323 #pragma warning( disable: 4312 )
00324
00325 if ( ::setsockopt(
00326 getFileDescriptorForTransport(),
00327 SOL_SOCKET,
00328 TCP_NODELAY,
00329 reinterpret_cast<const char *>( optionValue ),
00330 sizeof( int ) ) == SOCKET_ERROR )
00331 throw StreamSocketException(
00332 "StreamSocket::setNagleAlgorithmTo failed: "
00333 + Network::explainSocketError() ) ;
00334
00335 #pragma warning( pop )
00336
00337 #else // CEYLAN_ARCH_WINDOWS
00338
00339 if ( ::setsockopt(
00340 getFileDescriptorForTransport(),
00341 SOL_SOCKET,
00342 TCP_NODELAY,
00343 reinterpret_cast<const char *>( optionValue ),
00344 sizeof( int ) ) == -1 )
00345 throw StreamSocketException(
00346 "StreamSocket::setNagleAlgorithmTo failed: "
00347 + explainError() ) ;
00348
00349 #endif // CEYLAN_ARCH_WINDOWS
00350
00351 #else // CEYLAN_USES_NETWORK
00352
00353 throw StreamSocketException( "StreamSocket::setNagleAlgorithmTo: "
00354 "network support not available." ) ;
00355
00356 #endif // CEYLAN_USES_NETWORK
00357
00358 }
00359
00360
00361
00362 const std::string StreamSocket::toString( Ceylan::VerbosityLevels level ) const
00363 {
00364
00365 string res = "Stream" + Socket::toString( level ) ;
00366
00367 if ( level == Ceylan::low )
00368 return res ;
00369
00370 if ( _nagleAlgorithmDeactivated )
00371 res += ". The Nagle algorithm is deactivated for this stream socket" ;
00372 else
00373 res += ". The Nagle algorithm is activated for this stream socket" ;
00374
00375 return res ;
00376
00377 }
00378
00379
00380
00381 void StreamSocket::setBlocking( bool newStatus )
00382 {
00383
00384 #if CEYLAN_USES_NETWORK
00385
00386
00387 #if CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00388
00389 if ( getOriginalFileDescriptor() == 0 )
00390 throw NonBlockingNotSupportedException(
00391 "StreamSocket::setBlocking: null descriptor, "
00392 "socket not created yet?" ) ;
00393
00394 #endif // CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00395
00396
00397 #if CEYLAN_USES_FCNTL_FOR_NONBLOCKING_SOCKETS
00398
00399
00400 int currentFDState =::fcntl( getOriginalFileDescriptor(), F_GETFL, 0 ) ;
00401
00402 if ( currentFDState < 0 )
00403 throw NonBlockingNotSupportedException(
00404 "StreamSocket::setBlocking: retrieving attributes failed: "
00405 + System::explainError() ) ;
00406
00407 #endif // CEYLAN_USES_FCNTL_FOR_NONBLOCKING_SOCKETS
00408
00409
00410
00411 if ( newStatus )
00412 {
00413
00414
00415
00416 #if CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00417
00418 LogPlug::trace( "StreamSocket::setBlocking: "
00419 "setting a non-blocking socket to blocking, "
00420 "using file descriptor #"
00421 + Ceylan::toString( getOriginalFileDescriptor() )
00422 + "." ) ;
00423
00424 #endif // CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00425
00426
00427
00428 #if CEYLAN_USES_FCNTL_FOR_NONBLOCKING_SOCKETS
00429
00430 if ( ::fcntl( getOriginalFileDescriptor(), F_SETFL,
00431 currentFDState & (~O_NONBLOCK) ) < 0 )
00432 throw NonBlockingNotSupportedException(
00433 "StreamSocket::setBlocking: "
00434 "setting to blocking with fcntl failed: "
00435 + System::explainError() ) ;
00436
00437 #else // CEYLAN_USES_FCNTL_FOR_NONBLOCKING_SOCKETS
00438
00439 #if CEYLAN_ARCH_WINDOWS
00440
00441 unsigned long nonBlockingMode = 0 ;
00442
00443 if ( ::ioctlsocket( getOriginalFileDescriptor(), FIONBIO,
00444 &nonBlockingMode ) == SOCKET_ERROR )
00445 throw NonBlockingNotSupportedException(
00446 "StreamSocket::setBlocking: "
00447 "setting to blocking with ioctlsocket failed: "
00448 + Network::explainSocketError() ) ;
00449
00450 #else // CEYLAN_ARCH_WINDOWS
00451
00452 int nonBlockingFlag = 0 ;
00453 if ( ::ioctl( getOriginalFileDescriptor(), FIONBIO, &nonBlockingFlag ) )
00454 throw NonBlockingNotSupportedException(
00455 "StreamSocket::setBlocking: "
00456 "setting to blocking with ioctl failed: "
00457 + System::explainError() ) ;
00458
00459 #endif // CEYLAN_ARCH_WINDOWS
00460
00461 #endif // CEYLAN_USES_FCNTL_FOR_NONBLOCKING_SOCKETS
00462
00463 }
00464 else
00465 {
00466
00467
00468 #if CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00469
00470 LogPlug::trace(
00471 "StreamSocket::setBlocking: "
00472 "setting a blocking socket to non-blocking, "
00473 "using file descriptor #"
00474 + Ceylan::toString( getOriginalFileDescriptor() )
00475 + "." ) ;
00476
00477 #endif // CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00478
00479
00480
00481 #if CEYLAN_USES_FCNTL_FOR_NONBLOCKING_SOCKETS
00482
00483 if ( ::fcntl( getOriginalFileDescriptor(), F_SETFL,
00484 currentFDState | O_NONBLOCK ) < 0 )
00485 throw NonBlockingNotSupportedException(
00486 "StreamSocket::setBlocking: "
00487 "setting to non-blocking with fcntl failed: "
00488 + System::explainError() ) ;
00489
00490 #else // CEYLAN_USES_FCNTL_FOR_NONBLOCKING_SOCKETS
00491
00492 #if CEYLAN_ARCH_WINDOWS
00493
00494 unsigned long nonBlockingMode = 1 ;
00495
00496 if ( ::ioctlsocket( getOriginalFileDescriptor(), FIONBIO,
00497 &nonBlockingMode ) == SOCKET_ERROR )
00498 throw NonBlockingNotSupportedException(
00499 "StreamSocket::setBlocking: "
00500 "setting to non-blocking with ioctlsocket failed: "
00501 + Network::explainSocketError() ) ;
00502
00503 #else // CEYLAN_ARCH_WINDOWS
00504
00505 int nonBlockingFlag = 1 ;
00506 if ( ::ioctl( getOriginalFileDescriptor(), FIONBIO, &nonBlockingFlag ) )
00507 throw NonBlockingNotSupportedException(
00508 "StreamSocket::setBlocking: "
00509 "setting to non-blocking with ioctl failed: "
00510 + System::explainError() ) ;
00511
00512 #endif // CEYLAN_ARCH_WINDOWS
00513
00514 #endif // CEYLAN_USES_FCNTL_FOR_NONBLOCKING_SOCKETS
00515
00516 }
00517
00518 _isBlocking = newStatus ;
00519
00520 #else // CEYLAN_USES_NETWORK
00521
00522 throw NonBlockingNotSupportedException(
00523 "StreamSocket::setBlocking failed: network support not available." ) ;
00524
00525 #endif // CEYLAN_USES_NETWORK
00526
00527 }
00528