void setsock_tcp_mss( int inSock, int inMSS ) { #ifdef TCP_MAXSEG int rc; int newMSS; Socklen_t len; assert( inSock != INVALID_SOCKET ); if ( inMSS > 0 ) { /* set */ newMSS = inMSS; len = sizeof( newMSS ); rc = setsockopt( inSock, IPPROTO_TCP, TCP_MAXSEG, (char*) &newMSS, len ); if ( rc == SOCKET_ERROR ) { fprintf( stderr, warn_mss_fail, newMSS ); return; } /* verify results */ rc = getsockopt( inSock, IPPROTO_TCP, TCP_MAXSEG, (char*) &newMSS, &len ); WARN_errno( rc == SOCKET_ERROR, ( "Unable to get value of TCP_MAXSEG with getsockopt.\n" ) ); if ( newMSS != inMSS ) { fprintf( stderr, warn_mss_notset, inMSS, newMSS ); } } #else IPERF_DEBUGF( SOCKET_DEBUG | IPERF_DBG_TRACE | IPERF_DBG_LEVEL_WARNING, ( "%s", warn_mss_no_attempt ) ); #endif /* TCP_MAXSEG */ } // end setsock_tcp_mss
/* ------------------------------------------------------------------- * Delete memory (buffer). * ------------------------------------------------------------------- */ Listener::~Listener() { if ( mSettings->mSock != INVALID_SOCKET ) { int rc = close( mSettings->mSock ); WARN_errno( rc == SOCKET_ERROR, "close" ); mSettings->mSock = INVALID_SOCKET; } DELETE_ARRAY( mBuf ); } // end ~Listener
Client::~Client() { if ( mSettings->mSock != INVALID_SOCKET ) { int rc = close( mSettings->mSock ); WARN_errno( rc == SOCKET_ERROR, "close" ); mSettings->mSock = INVALID_SOCKET; } if(outFile!=NULL) fclose(outFile); DELETE_ARRAY( mBuf ); } // end ~Client
void Client::Connect( ) { int rc; SockAddr_remoteAddr( mSettings ); assert( mSettings->inHostname != NULL ); // create an internet socket int type = ( isUDP( mSettings ) ? SOCK_DGRAM : SOCK_STREAM); int domain = (SockAddr_isIPv6( &mSettings->peer ) ? #ifdef HAVE_IPV6 AF_INET6 #else AF_INET #endif : AF_INET); mSettings->mSock = socket( domain, type, 0 ); WARN_errno( mSettings->mSock == INVALID_SOCKET, "socket" ); SetSocketOptions( mSettings ); SockAddr_localAddr( mSettings ); if ( mSettings->mLocalhost != NULL ) { // bind socket to local address char temp[100] = "cbind:"; SockAddr_getHostAddress(&mSettings->local, &temp[6], 94); rc = bind( mSettings->mSock, (sockaddr*) &mSettings->local, SockAddr_get_sizeof_sockaddr( &mSettings->local ) ); WARN_errno( rc == SOCKET_ERROR, temp ); } // connect socket rc = connect( mSettings->mSock, (sockaddr*) &mSettings->peer, SockAddr_get_sizeof_sockaddr( &mSettings->peer )); FAIL_errno( rc == SOCKET_ERROR, "connect", mSettings ); getsockname( mSettings->mSock, (sockaddr*) &mSettings->local, &mSettings->size_local ); getpeername( mSettings->mSock, (sockaddr*) &mSettings->peer, &mSettings->size_peer ); } // end Connect
void Client::write_UDP_FIN( ) { int rc; fd_set readSet; struct timeval timeout; int count = 0; while ( count < 10 ) { count++; // write data rc=write( mSettings->mSock, mBuf, mSettings->mBufLen ); WARN_errno( rc < 0, "write"); // wait until the socket is readable, or our timeout expires FD_ZERO( &readSet ); FD_SET( mSettings->mSock, &readSet ); timeout.tv_sec = 0; timeout.tv_usec = 250000; // quarter second, 250 ms rc = select( mSettings->mSock+1, &readSet, NULL, NULL, &timeout ); FAIL_errno( rc == SOCKET_ERROR, "select", mSettings ); if ( rc == 0 ) { // select timed out continue; } else { // socket ready to read rc = read( mSettings->mSock, mBuf, mSettings->mBufLen ); WARN_errno( rc < 0, "read" ); if ( rc < 0 ) { break; } else if ( rc >= (int) (sizeof(UDP_datagram) + sizeof(server_hdr)) ) { ReportServerUDP( mSettings, (server_hdr*) ((UDP_datagram*)mBuf + 1) ); } return; } } fprintf( stderr, warn_no_ack, mSettings->mSock, count ); }
void delay_ms(unsigned long msec) { struct timespec requested, remaining; requested.tv_sec = msec/1000; requested.tv_nsec = (msec%1000) * 1000L; while (nanosleep(&requested, &remaining) == -1) if (errno == EINTR) requested = remaining; else { WARN_errno(1, "nanosleep"); break; } }
void Client::InitiateServer() { if ( !isCompat( mSettings ) ) { int currLen; client_hdr* temp_hdr; if ( isUDP( mSettings ) ) { UDP_datagram *UDPhdr = (UDP_datagram *)mBuf; temp_hdr = (client_hdr*)(UDPhdr + 1); } else { temp_hdr = (client_hdr*)mBuf; } Settings_GenerateClientHdr( mSettings, temp_hdr ); if ( !isUDP( mSettings ) ) { currLen = send( mSettings->mSock, mBuf, sizeof(client_hdr), 0 ); if ( currLen < 0 ) { WARN_errno( currLen < 0, "write1" ); } } } }
static void gettcpistats (ReporterData *stats) { struct tcp_info tcp_internal; socklen_t tcp_info_length = sizeof(struct tcp_info); int retry; if (stats->info.mEnhanced && stats->info.mTCP == kMode_Client) { // Read the TCP retry stats for a client. Do this // on a report interval period. if (getsockopt(stats->info.socket, IPPROTO_TCP, TCP_INFO, &tcp_internal, &tcp_info_length) < 0) { WARN_errno( 1 , "getsockopt"); retry = 0; } else { retry = tcp_internal.tcpi_total_retrans - stats->info.tcp.write.lastTCPretry; } stats->info.tcp.write.TCPretry = retry; stats->info.tcp.write.totTCPretry += retry; stats->info.tcp.write.lastTCPretry = tcp_internal.tcpi_total_retrans; stats->info.tcp.write.cwnd = tcp_internal.tcpi_snd_cwnd * tcp_internal.tcpi_snd_mss / 1024; } }
/* ------------------------------------------------------------------- * returns the TCP maximum segment size * ------------------------------------------------------------------- */ int getsock_tcp_mss( int inSock ) { int theMSS = 0; #ifdef TCP_MAXSEG int rc; Socklen_t len; assert( inSock >= 0 ); /* query for MSS */ len = sizeof( theMSS ); #if defined(WICED) && defined(NETWORK_LwIP) /* We can't use `getsockopt' to query SO_SNDBUF on LwIP */ rc = 0; theMSS = TCP_MSS; #else rc = getsockopt( inSock, IPPROTO_TCP, TCP_MAXSEG, (char*) &theMSS, &len ); WARN_errno( rc == SOCKET_ERROR, ( "Unable to get value of TCP_MAXSEG with getsockopt.\n" ) ); #endif /* defined(WICED) && defined(NETWORK_LwIP) */ #else IPERF_DEBUGF( SOCKET_DEBUG | IPERF_DBG_TRACE | IPERF_DBG_LEVEL_WARNING, ( "Unable get set socket TCP MSS - TCP_MAXSEG is undefined.\n" ) ); #endif /* TCP_MAXSEG */ return theMSS; } // end getsock_tcp_mss
void Client::Run( void ) { struct UDP_datagram* mBuf_UDP = (struct UDP_datagram*) mBuf; unsigned long currLen = 0; int delay_target = 0; int delay = 0; int adjust = 0; char* readAt = mBuf; #if HAVE_THREAD if ( !isUDP( mSettings ) ) { RunTCP(); return; } #endif // Indicates if the stream is readable bool canRead = true, mMode_Time = isModeTime( mSettings ); // setup termination variables if ( mMode_Time ) { mEndTime.setnow(); mEndTime.add( mSettings->mAmount / 100.0 ); } if ( isUDP( mSettings ) ) { // Due to the UDP timestamps etc, included // reduce the read size by an amount // equal to the header size // compute delay for bandwidth restriction, constrained to [0,1] seconds delay_target = (int) ( mSettings->mBufLen * ((kSecs_to_usecs * kBytes_to_Bits) / mSettings->mUDPRate) ); if ( delay_target < 0 || delay_target > (int) 1 * kSecs_to_usecs ) { fprintf( stderr, warn_delay_large, delay_target / kSecs_to_usecs ); delay_target = (int) kSecs_to_usecs * 1; } if ( isFileInput( mSettings ) ) { if ( isCompat( mSettings ) ) { Extractor_reduceReadSize( sizeof(struct UDP_datagram), mSettings ); readAt += sizeof(struct UDP_datagram); } else { Extractor_reduceReadSize( sizeof(struct UDP_datagram) + sizeof(struct client_hdr), mSettings ); readAt += sizeof(struct UDP_datagram) + sizeof(struct client_hdr); } } } ReportStruct *reportstruct = NULL; // InitReport handles Barrier for multiple Streams mSettings->reporthdr = InitReport( mSettings ); reportstruct = new ReportStruct; reportstruct->packetID = 0; lastPacketTime.setnow(); do { // Test case: drop 17 packets and send 2 out-of-order: // sequence 51, 52, 70, 53, 54, 71, 72 //switch( datagramID ) { // case 53: datagramID = 70; break; // case 71: datagramID = 53; break; // case 55: datagramID = 71; break; // default: break; //} gettimeofday( &(reportstruct->packetTime), NULL ); if ( isUDP( mSettings ) ) { // store datagram ID into buffer mBuf_UDP->id = htonl( (reportstruct->packetID)++ ); mBuf_UDP->tv_sec = htonl( reportstruct->packetTime.tv_sec ); mBuf_UDP->tv_usec = htonl( reportstruct->packetTime.tv_usec ); // delay between writes // make an adjustment for how long the last loop iteration took // TODO this doesn't work well in certain cases, like 2 parallel streams adjust = delay_target + lastPacketTime.subUsec( reportstruct->packetTime ); lastPacketTime.set( reportstruct->packetTime.tv_sec, reportstruct->packetTime.tv_usec ); if ( adjust > 0 || delay > 0 ) { delay += adjust; } } // Read the next data block from // the file if it's file input if ( isFileInput( mSettings ) ) { Extractor_getNextDataBlock( readAt, mSettings ); canRead = Extractor_canRead( mSettings ) != 0; } else canRead = true; // perform write currLen = write( mSettings->mSock, mBuf, mSettings->mBufLen ); if ( currLen < 0 && errno != ENOBUFS ) { WARN_errno( currLen < 0, "write2" ); break; } // report packets reportstruct->packetLen = currLen; ReportPacket( mSettings->reporthdr, reportstruct ); if ( delay > 0 ) { delay_loop( delay ); } if ( !mMode_Time ) { /* mAmount may be unsigned, so don't let it underflow! */ if( mSettings->mAmount >= currLen ) { mSettings->mAmount -= currLen; } else { mSettings->mAmount = 0; } } } while ( ! (sInterupted || (mMode_Time && mEndTime.before( reportstruct->packetTime )) || (!mMode_Time && 0 >= mSettings->mAmount)) && canRead ); // stop timing gettimeofday( &(reportstruct->packetTime), NULL ); CloseReport( mSettings->reporthdr, reportstruct ); if ( isUDP( mSettings ) ) { // send a final terminating datagram // Don't count in the mTotalLen. The server counts this one, // but didn't count our first datagram, so we're even now. // The negative datagram ID signifies termination to the server. // store datagram ID into buffer mBuf_UDP->id = htonl( -(reportstruct->packetID) ); mBuf_UDP->tv_sec = htonl( reportstruct->packetTime.tv_sec ); mBuf_UDP->tv_usec = htonl( reportstruct->packetTime.tv_usec ); if ( isMulticast( mSettings ) ) { write( mSettings->mSock, mBuf, mSettings->mBufLen ); } else { write_UDP_FIN( ); } } DELETE_PTR( reportstruct ); EndReport( mSettings->reporthdr ); }
void Client::RunTCP( void ) { unsigned long currLen = 0; struct itimerval it; max_size_t totLen = 0; int err; char* readAt = mBuf; // Indicates if the stream is readable bool canRead = true, mMode_Time = isModeTime( mSettings ); ReportStruct *reportstruct = NULL; // InitReport handles Barrier for multiple Streams mSettings->reporthdr = InitReport( mSettings ); reportstruct = new ReportStruct; reportstruct->packetID = 0; lastPacketTime.setnow(); if ( mMode_Time ) { memset (&it, 0, sizeof (it)); it.it_value.tv_sec = (int) (mSettings->mAmount / 100.0); it.it_value.tv_usec = (int) 10000 * (mSettings->mAmount - it.it_value.tv_sec * 100.0); err = setitimer( ITIMER_REAL, &it, NULL ); if ( err != 0 ) { perror("setitimer"); exit(1); } } do { // Read the next data block from // the file if it's file input if ( isFileInput( mSettings ) ) { Extractor_getNextDataBlock( readAt, mSettings ); canRead = Extractor_canRead( mSettings ) != 0; } else canRead = true; // perform write currLen = write( mSettings->mSock, mBuf, mSettings->mBufLen ); #ifdef __linux OutTcpInfo(outFile, mSettings->mSock); #endif if ( currLen < 0 ) { WARN_errno( currLen < 0, "write2" ); break; } totLen += currLen; if(mSettings->mInterval > 0) { gettimeofday( &(reportstruct->packetTime), NULL ); reportstruct->packetLen = currLen; ReportPacket( mSettings->reporthdr, reportstruct ); } if ( !mMode_Time ) { /* mAmount may be unsigned, so don't let it underflow! */ if( mSettings->mAmount >= currLen ) { mSettings->mAmount -= currLen; } else { mSettings->mAmount = 0; } } } while ( ! (sInterupted || (!mMode_Time && 0 >= mSettings->mAmount)) && canRead ); // stop timing gettimeofday( &(reportstruct->packetTime), NULL ); // if we're not doing interval reporting, report the entire transfer as one big packet if(0.0 == mSettings->mInterval) { reportstruct->packetLen = totLen; ReportPacket( mSettings->reporthdr, reportstruct ); } CloseReport( mSettings->reporthdr, reportstruct ); DELETE_PTR( reportstruct ); EndReport( mSettings->reporthdr ); }
/* ------------------------------------------------------------------- * main() * Entry point into Iperf * * sets up signal handlers * initialize global locks and conditions * parses settings from environment and command line * starts up server or client thread * waits for all threads to complete * ------------------------------------------------------------------- */ int main( int argc, char **argv ) { #ifdef WIN32 // Start winsock WSADATA wsaData; int rc; #endif // Set SIGTERM and SIGINT to call our user interrupt function my_signal( SIGTERM, Sig_Interupt ); my_signal( SIGINT, Sig_Interupt ); #ifndef WIN32 // SIGALRM=14, _NSIG=3... my_signal( SIGALRM, Sig_Interupt ); #endif #ifndef WIN32 // Ignore broken pipes signal(SIGPIPE,SIG_IGN); #endif #ifdef WIN32 // Start winsock rc = WSAStartup( 0x202, &wsaData ); WARN_errno( rc == SOCKET_ERROR, "WSAStartup" ); if (rc == SOCKET_ERROR) return 0; // Tell windows we want to handle our own signals SetConsoleCtrlHandler( sig_dispatcher, true ); #endif // Initialize global mutexes and conditions Condition_Initialize ( &ReportCond ); Condition_Initialize ( &ReportDoneCond ); Mutex_Initialize( &groupCond ); Mutex_Initialize( &clients_mutex ); // Initialize the thread subsystem thread_init( ); // Initialize the interrupt handling thread to 0 sThread = thread_zeroid(); // perform any cleanup when quitting Iperf atexit( cleanup ); // Allocate the "global" settings thread_Settings* ext_gSettings = new thread_Settings; // Initialize settings to defaults Settings_Initialize( ext_gSettings ); // read settings from environment variables Settings_ParseEnvironment( ext_gSettings ); // read settings from command-line parameters Settings_ParseCommandLine( argc, argv, ext_gSettings ); // Check for either having specified client or server if ( ext_gSettings->mThreadMode == kMode_Client || ext_gSettings->mThreadMode == kMode_Listener ) { #ifdef WIN32 // Start the server as a daemon // Daemon mode for non-windows in handled // in the listener_spawn function if ( isDaemon( ext_gSettings ) ) { CmdInstallService(argc, argv); return 0; } // Remove the Windows service if requested if ( isRemoveService( ext_gSettings ) ) { // remove the service if ( CmdRemoveService() ) { fprintf(stderr, "IPerf Service is removed.\n"); return 0; } } #endif // initialize client(s) if ( ext_gSettings->mThreadMode == kMode_Client ) { client_init( ext_gSettings ); } #ifdef HAVE_THREAD // start up the reporter and client(s) or listener { thread_Settings *into = NULL; // Create the settings structure for the reporter thread Settings_Copy( ext_gSettings, &into ); into->mThreadMode = kMode_Reporter; // Have the reporter launch the client or listener into->runNow = ext_gSettings; // Start all the threads that are ready to go thread_start( into ); } #else // No need to make a reporter thread because we don't have threads thread_start( ext_gSettings ); #endif } else { // neither server nor client mode was specified // print usage and exit #ifdef WIN32 // In Win32 we also attempt to start a previously defined service // Starting in 2.0 to restart a previously defined service // you must call iperf with "iperf -D" or using the environment variable SERVICE_TABLE_ENTRY dispatchTable[] = { { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main}, { NULL, NULL} }; // Only attempt to start the service if "-D" was specified if ( !isDaemon(ext_gSettings) || // starting the service by SCM, there is no arguments will be passed in. // the arguments will pass into Service_Main entry. !StartServiceCtrlDispatcher(dispatchTable) ) // If the service failed to start then print usage #endif fprintf( stderr, usage_short, argv[0], argv[0] ); return 0; } // wait for other (client, server) threads to complete thread_joinall(); // all done! return 0; } // end main
/* ------------------------------------------------------------------- * Set socket options before the listen() or connect() calls. * These are optional performance tuning factors. * ------------------------------------------------------------------- */ void SetSocketOptions( thread_Settings *inSettings ) { // set the TCP window size (socket buffer sizes) // also the UDP buffer size // must occur before call to accept() for large window sizes setsock_tcp_windowsize( inSettings->mSock, inSettings->mTCPWin, (inSettings->mThreadMode == kMode_Client ? 1 : 0) ); if ( isCongestionControl( inSettings ) ) { #ifdef TCP_CONGESTION Socklen_t len = strlen( inSettings->mCongestion ) + 1; int rc = setsockopt( inSettings->mSock, IPPROTO_TCP, TCP_CONGESTION, inSettings->mCongestion, len); FAIL( rc == SOCKET_ERROR, ( "Attempt to set '%s' congestion control failed: %s\r\n", inSettings->mCongestion, strerror(errno) ), NULL ); #else fprintf( stderr, "The -Z option is not available on this operating system\r\n"); #endif /* TCP_CONGESTION */ } // check if we're sending multicast, and set TTL if ( isMulticast( inSettings ) && ( inSettings->mTTL > 0 ) ) { #ifdef HAVE_MULTICAST int val = inSettings->mTTL; if ( !SockAddr_isIPv6( &inSettings->local ) ) { int rc = setsockopt( inSettings->mSock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*) &val, (Socklen_t) sizeof(val)); WARN_errno( rc == SOCKET_ERROR, ( "Failed to set multicast TTL.\r\n" ) ); } #ifdef HAVE_IPV6_MULTICAST else { int rc = setsockopt( inSettings->mSock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char*) &val, (Socklen_t) sizeof(val)); WARN_errno( rc == SOCKET_ERROR, ( "Failed to set multicast TTL.\r\n" ) ); } #endif /* HAVE_IPV6_MULTICAST */ #endif /* HAVE_MULTICAST */ } #ifdef IP_TOS // set IP TOS (type-of-service) field // if ( inSettings->mTOS > 0 ) { int tos, rc; tos = inSettings->mTOS; Socklen_t len = sizeof(tos); rc = setsockopt( inSettings->mSock, IPPROTO_IP, IP_TOS,(char*) &tos, len ); WARN_errno( rc == SOCKET_ERROR, ( "Failed to set IP_TOS.\r\n" ) ); // } #endif /* IP_TOS */ if ( !isUDP( inSettings ) ) { // set the TCP maximum segment size setsock_tcp_mss( inSettings->mSock, inSettings->mMSS ); #ifdef TCP_NODELAY // set TCP nodelay option if ( isNoDelay( inSettings ) ) { int nodelay = 1; Socklen_t len = sizeof(nodelay); int rc = setsockopt( inSettings->mSock, IPPROTO_TCP, TCP_NODELAY, (char*) &nodelay, len ); WARN_errno( rc == SOCKET_ERROR, ( "Failed to set TCP_NODELAY.\r\n" ) ); } #endif /* TCP_NODELAY */ } } // end SetSocketOptions
/* ------------------------------------------------------------------- * main() * Entry point into Iperf * * sets up signal handlers * initialize global locks and conditions * parses settings from environment and command line * starts up server or client thread * waits for all threads to complete * ------------------------------------------------------------------- */ #ifdef __cplusplus extern "C" #endif /* __cplusplus */ int IPERF_MAIN( int argc, char **argv ) { #ifdef NO_EXIT should_exit = 0; #endif /* NO_EXIT */ #ifdef IPERF_DEBUG debug_init(); #endif /* IPERF_DEBUG */ #ifndef NO_INTERRUPTS #ifdef WIN32 setsigalrmfunc(call_sigalrm); #endif /* WIN32 */ // Set SIGTERM and SIGINT to call our user interrupt function my_signal( SIGTERM, Sig_Interupt ); my_signal( SIGINT, Sig_Interupt ); my_signal( SIGALRM, Sig_Interupt ); #ifndef WIN32 // Ignore broken pipes signal(SIGPIPE,SIG_IGN); #else // Start winsock WORD wVersionRequested; WSADATA wsaData; // Using MAKEWORD macro, Winsock version request 2.2 wVersionRequested = MAKEWORD(2, 2); int rc = WSAStartup( wVersionRequested, &wsaData ); WARN_errno( rc == SOCKET_ERROR, ( "WSAStartup failed.\n" ) ); if (rc != 0) { fprintf(stderr, "The Winsock DLL was not found!\n"); return 1; } /* * Confirm that the WinSock DLL supports 2.2. Note that if the DLL supports * versions greater than 2.2 in addition to 2.2, it will still return 2.2 in * wVersion since that is the version we requested. */ if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2 ) { /* Tell the user that we could not find a usable WinSock DLL. */ fprintf(stderr, "The DLL does not support the Winsock version %u.%u!\n", LOBYTE(wsaData.wVersion),HIBYTE(wsaData.wVersion)); WSACleanup(); return 1; } // Tell windows we want to handle our own signals SetConsoleCtrlHandler( sig_dispatcher, true ); #endif /* WIN32 */ #endif /* NO_INTERRUPTS */ // Initialize global mutexes and conditions IPERF_DEBUGF( CONDITION_DEBUG | IPERF_DBG_TRACE, ( "Initializing report condition.\n" ) ); Condition_Initialize ( &ReportCond ); IPERF_DEBUGF( CONDITION_DEBUG | IPERF_DBG_TRACE, ( "Initializing report done condition.\n" ) ); Condition_Initialize ( &ReportDoneCond ); IPERF_DEBUGF( MUTEX_DEBUG | IPERF_DBG_TRACE, ( "Initializing group condition mutex.\n" ) ); Mutex_Initialize( &groupCond ); IPERF_DEBUGF( MUTEX_DEBUG | IPERF_DBG_TRACE, ( "Initializing clients mutex.\n" ) ); Mutex_Initialize( &clients_mutex ); // Initialize the thread subsystem IPERF_DEBUGF( THREAD_DEBUG | IPERF_DBG_TRACE, ( "Initializing the thread subsystem.\n" ) ); thread_init( ); // Initialize the interrupt handling thread to 0 sThread = thread_zeroid(); #ifndef NO_EXIT // perform any cleanup when quitting Iperf atexit( cleanup ); #endif /* NO_EXIT */ // Allocate the "global" settings thread_Settings *ext_gSettings = (thread_Settings*) malloc( sizeof( thread_Settings ) ); FAIL( ext_gSettings == NULL, ( "Unable to allocate memory for thread_Settings ext_gSettings.\n" ), NULL ); IPERF_DEBUGF( MEMALLOC_DEBUG, IPERF_MEMALLOC_MSG( ext_gSettings, sizeof( thread_Settings ) ) ); // Initialize settings to defaults Settings_Initialize( ext_gSettings ); #ifndef NO_ENVIRONMENT // read settings from environment variables Settings_ParseEnvironment( ext_gSettings ); #endif /* NO_ENVIORNMENT */ // read settings from command-line parameters Settings_ParseCommandLine( argc, argv, ext_gSettings ); #ifdef NO_EXIT if (should_exit) { IPERF_DEBUGF( MEMFREE_DEBUG | IPERF_DBG_TRACE, IPERF_MEMFREE_MSG( ext_gSettings ) ); FREE_PTR( ext_gSettings ); IPERF_DEBUGF( CONDITION_DEBUG | IPERF_DBG_TRACE, ( "Destroying report condition.\n" ) ); Condition_Destroy( &ReportCond ); IPERF_DEBUGF( CONDITION_DEBUG | IPERF_DBG_TRACE, ( "Destroying report done condition.\n" ) ); Condition_Destroy( &ReportDoneCond ); IPERF_DEBUGF( MUTEX_DEBUG | IPERF_DBG_TRACE, ( "Destroying group condition mutex.\n" ) ); Mutex_Destroy( &groupCond ); IPERF_DEBUGF( MUTEX_DEBUG | IPERF_DBG_TRACE, ( "Destroying clients mutex.\n" ) ); Mutex_Destroy( &clients_mutex ); return 0; } #endif /* NO_EXIT */ // Check for either having specified client or server if ( ext_gSettings->mThreadMode == kMode_Client || ext_gSettings->mThreadMode == kMode_Listener ) { #ifdef WIN32 #ifndef NO_DAEMON // Start the server as a daemon // Daemon mode for non-windows in handled // in the listener_spawn function if ( isDaemon( ext_gSettings ) ) { CmdInstallService(argc, argv); return 0; } #endif /* NO_DAEMON */ #ifndef NO_SERVICE // Remove the Windows service if requested if ( isRemoveService( ext_gSettings ) ) { // remove the service if ( CmdRemoveService() ) { fprintf(stderr, "IPerf Service is removed.\n"); return 0; } } #endif /* NO_SERVICE */ #endif /* WIN32 */ // initialize client(s) if ( ext_gSettings->mThreadMode == kMode_Client ) { IPERF_DEBUGF( CLIENT_DEBUG | LISTENER_DEBUG | IPERF_DBG_TRACE, ( "Initializing client(s)...\n" ) ); client_init( ext_gSettings ); } #ifdef HAVE_THREAD // start up the reporter and client(s) or listener thread_Settings *into = NULL; // Create the settings structure for the reporter thread IPERF_DEBUGF( CLIENT_DEBUG | LISTENER_DEBUG | REPORTER_DEBUG | IPERF_DBG_TRACE, ( "Creating the settings structure for the reporter thread.\n" ) ); Settings_Copy( ext_gSettings, &into ); into->mThreadMode = kMode_Reporter; // Have the reporter launch the client or listener IPERF_DEBUGF( CLIENT_DEBUG | LISTENER_DEBUG | IPERF_DBG_TRACE, ( "Setting the reporter to launch the client or listener before launching itself.\n" ) ); into->runNow = ext_gSettings; // Start all the threads that are ready to go IPERF_DEBUGF( THREAD_DEBUG | IPERF_DBG_TRACE, ( "Starting all the threads...\n" ) ); thread_start( into ); #else // No need to make a reporter thread because we don't have threads IPERF_DEBUGF( THREAD_DEBUG | IPERF_DBG_TRACE, ( "Starting iperf in a single thread...\n" ) ); thread_start( ext_gSettings ); #endif /* HAVE_THREAD */ } else { // neither server nor client mode was specified // print usage and exit #ifdef WIN32 // In Win32 we also attempt to start a previously defined service // Starting in 2.0 to restart a previously defined service // you must call iperf with "iperf -D" or using the environment variable SERVICE_TABLE_ENTRY dispatchTable[] = { { TEXT((char *) SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main}, { NULL, NULL} }; #ifndef NO_DAEMON // Only attempt to start the service if "-D" was specified if ( !isDaemon(ext_gSettings) || // starting the service by SCM, there is no arguments will be passed in. // the arguments will pass into Service_Main entry. !StartServiceCtrlDispatcher(dispatchTable) ) // If the service failed to start then print usage #endif /* NO_DAEMON */ #endif /* WIN32 */ fprintf( stderr, usage_short, argv[0], argv[0] ); return 0; } // wait for other (client, server) threads to complete // IPERF_DEBUGF( THREAD_DEBUG | IPERF_DBG_TRACE, ( "Waiting for other (client, server) threads to complete...\n" ) ); // thread_joinall(); #ifdef NO_EXIT /* We can't run the atexit function */ #ifdef WIN32 // Shutdown Winsock WSACleanup(); #endif /* WIN32 */ // clean up the list of clients Iperf_destroy ( &clients ); // shutdown the thread subsystem IPERF_DEBUGF( THREAD_DEBUG | IPERF_DBG_TRACE, ( "Deinitializing the thread subsystem.\n" ) ); IPERF_DEBUGF( CONDITION_DEBUG | IPERF_DBG_TRACE, ( "Destroying report condition.\n" ) ); Condition_Destroy( &ReportCond ); IPERF_DEBUGF( CONDITION_DEBUG | IPERF_DBG_TRACE, ( "Destroying report done condition.\n" ) ); Condition_Destroy( &ReportDoneCond ); IPERF_DEBUGF( MUTEX_DEBUG | IPERF_DBG_TRACE, ( "Destroying group condition mutex.\n" ) ); Mutex_Destroy( &groupCond ); IPERF_DEBUGF( MUTEX_DEBUG | IPERF_DBG_TRACE, ( "Destroying clients mutex.\n" ) ); Mutex_Destroy( &clients_mutex ); #ifdef IPERF_DEBUG debug_init(); #endif /* IPERF_DEBUG */ #endif /* NO_EXIT */ // all done! IPERF_DEBUGF( IPERF_DBG_TRACE | IPERF_DBG_STATE, ( "Done!\n" ) ); return 0; } // end main
void PerfSocket::Client_Recv_TCP(void) { // terminate loop nicely on user interupts sInterupted = false; //my_signal( SIGINT, Sig_Interupt ); //my_signal( SIGPIPE, Sig_Interupt ); #ifndef WIN32 signal (SIGPIPE, SIG_IGN); #endif int currLen; InitTransfer(); double fract = 0.0; mStartTime.setnow(); long endSize = get_tcp_windowsize(mSock), startSize=endSize, loopLen =0, prevLen =0; Timestamp prevTime; prevTime.setnow(); /* Periodic reporting is done here in the loop itself, if Suggest Window Size option is set*/ mPReporting = false; /* Send the first packet indicating that the server has to send data */ mBuf[0] = 'a'; currLen = write( mSock, mBuf, mSettings->mBufLen ); if ( currLen < 0 ) { WARN_errno( currLen < 0, "write" ); return; } do { // perform read currLen = read( mSock, mBuf, mSettings->mBufLen ); mPacketTime.setnow(); if ( currLen < 0 ) { WARN_errno( currLen < 0, "read" ); break; } mTotalLen += currLen; loopLen +=currLen; // periodically report bandwidths ReportPeriodicBW(); double nFract = mStartTime.fraction(mPacketTime,mEndTime); if ( nFract > (fract + 0.1) ) { printf(seperator_line); ReportWindowSize(); sReporting.Lock(); ReportBW( loopLen, prevTime.subSec(mStartTime), mPacketTime.subSec( mStartTime)); sReporting.Unlock(); fract +=0.1; if ( startSize != endSize ) { /* Change the window size only if the data transfer has changed at least by 5% */ if ( loopLen < prevLen ) { if ( ( ((double)(prevLen - loopLen)) / ((double)prevLen)) > 0.05 ) { endSize = startSize + (endSize - startSize)/2; } } else { if ( ( ((double)(loopLen - prevLen)) / ((double)prevLen) ) > 0.05 ) { startSize = endSize; endSize = endSize*2; prevLen = loopLen; } } } else { endSize = endSize*2; prevLen = loopLen; } /** Reset the variables after setting new window size */ prevTime.setnow(); loopLen = 0 ; //shutdown(mSock,SHUT_RDWR); close(mSock); mSock = -1; Connect( mSettings->mHost, mSettings->mLocalhost ); mBuf[0] = 'a'; if ( set_tcp_windowsize(mSock,endSize) == -1 ) { printf(unable_to_change_win); } if ( get_tcp_windowsize(mSock) != endSize ) { printf(unable_to_change_win); } write( mSock, mBuf, mSettings->mBufLen ); } } while ( ! (sInterupted || (mMode_time && mPacketTime.after( mEndTime )) || (!mMode_time && mTotalLen >= mAmount)) ); printf( seperator_line ); ReportWindowSize(); sReporting.Lock(); ReportBW( loopLen, prevTime.subSec(mStartTime), mPacketTime.subSec( mStartTime)); sReporting.Unlock(); printf( seperator_line); printf( opt_estimate); if ( loopLen > prevLen ) set_tcp_windowsize(mSock,endSize); else set_tcp_windowsize(mSock,startSize); ReportWindowSize(); printf( seperator_line ); // stop timing mEndTime.setnow(); sReporting.Lock(); ReportBW( mTotalLen, 0.0, mEndTime.subSec( mStartTime )); sReporting.Unlock(); if ( mSettings->mPrintMSS ) { // read the socket option for MSS (maximum segment size) ReportMSS( getsock_tcp_mss( mSock )); } //close(mSock); //mSock = -1; }
void PerfSocket::Send_TCP( void ) { if ( false ) { Client_Recv_TCP(); return; } // terminate loop nicely on user interupts sInterupted = false; SigfuncPtr oldINT = my_signal( SIGINT, Sig_Interupt ); SigfuncPtr oldPIPE = my_signal( SIGPIPE, Sig_Interupt ); int currLen; bool canRead; InitTransfer(); do { // If the input is from a // file, fill the buffer with // data from the file if ( mSettings->mFileInput ) { extractor->getNextDataBlock(mBuf); // If the first character is 'a' // change if to '0' so that the // server does not mistake it for // a Window Suggest option if ( mBuf[0] == 'a' ) mBuf[0] = '0'; canRead = extractor->canRead(); } else canRead = true; // perform write currLen = write( mSock, mBuf, mSettings->mBufLen ); mPacketTime.setnow(); if ( currLen < 0 ) { WARN_errno( currLen < 0, "write" ); break; } // periodically report bandwidths ReportPeriodicBW(); mTotalLen += currLen; } while ( ! (sInterupted || (mMode_time && mPacketTime.after( mEndTime )) || (!mMode_time && mTotalLen >= mAmount)) && canRead ); if ( oldINT != Sig_Interupt ) { // Return signal handlers to previous handlers my_signal( SIGINT, oldINT ); my_signal( SIGPIPE, oldPIPE ); } // shutdown sending connection and wait (read) // for the other side to shutdown shutdown( mSock, SHUT_WR ); currLen = read( mSock, mBuf, mSettings->mBufLen ); WARN_errno( currLen == SOCKET_ERROR, "read on server close" ); WARN( currLen > 0, "server sent unexpected data" ); // stop timing mEndTime.setnow(); sReporting.Lock(); ReportBW( mTotalLen, 0.0, mEndTime.subSec( mStartTime )); sReporting.Unlock(); if ( mSettings->mPrintMSS ) { // read the socket option for MSS (maximum segment size) ReportMSS( getsock_tcp_mss( mSock )); } }