/*---------------------------------------------------------------
* Copyright ( c ) 1999 , 2000 , 2001 , 2002 , 2003
* The Board of Trustees of the University of Illinois
* All Rights Reserved .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Permission is hereby granted , free of charge , to any person
* obtaining a copy of this software ( Iperf ) and associated
* documentation files ( the " Software " ) , to deal in the Software
* without restriction , including without limitation the
* rights to use , copy , modify , merge , publish , distribute ,
* sublicense , and / or sell copies of the Software , and to permit
* persons to whom the Software is furnished to do
* so , subject to the following conditions :
*
*
* Redistributions of source code must retain the above
* copyright notice , this list of conditions and
* the following disclaimers .
*
*
* Redistributions in binary form must reproduce the above
* copyright notice , this list of conditions and the following
* disclaimers in the documentation and / or other materials
* provided with the distribution .
*
*
* Neither the names of the University of Illinois , NCSA ,
* nor the names of its contributors may be used to endorse
* or promote products derived from this Software without
* specific prior written permission .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND ,
* EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT . IN NO EVENT SHALL THE CONTIBUTORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY ,
* WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE ,
* ARISING FROM , OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE .
* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
* National Laboratory for Applied Network Research
* National Center for Supercomputing Applications
* University of Illinois at Urbana - Champaign
* http : //www.ncsa.uiuc.edu
* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
*
* Client . cpp
* by Mark Gates < mgates @ nlanr . net >
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* A client thread initiates a connect to the server and handles
* sending and receiving data , then closes the socket .
* ------------------------------------------------------------------- */
#include "headers.h"
#include "Client.hpp"
#include "Thread.h"
#include "SocketAddr.h"
#include "PerfSocket.hpp"
#include "Extractor.h"
#include "delay.hpp"
#include "util.h"
#include "Locale.h"
/* -------------------------------------------------------------------
* Store server hostname , optionally local hostname , and socket info .
* ------------------------------------------------------------------- */
Client::Client( thread_Settings *inSettings ) {
mSettings = inSettings;
mBuf = NULL;
// initialize buffer
mBuf = new char [ mSettings->mBufLen ];
pattern( mBuf, mSettings->mBufLen );
if ( isFileInput( mSettings ) ) {
if ( !isSTDIN( mSettings ) )
Extractor_Initialize( mSettings->mFileName, mSettings->mBufLen, mSettings );
else
Extractor_InitializeFile( stdin, mSettings->mBufLen, mSettings );
if ( !Extractor_canRead( mSettings ) ) {
unsetFileInput( mSettings );
}
}
// connect
Connect( );
if ( isReport( inSettings ) ) {
ReportSettings( inSettings );
if ( mSettings->multihdr && isMultipleReport( inSettings ) ) {
mSettings->multihdr->report->connection.peer = mSettings->peer;
mSettings->multihdr->report->connection.size_peer = mSettings->size_peer;
mSettings->multihdr->report->connection.local = mSettings->local;
SockAddr_setPortAny( &mSettings->multihdr->report->connection.local );
mSettings->multihdr->report->connection.size_local = mSettings->size_local;
}
}
} // end Client
/* -------------------------------------------------------------------
* Delete memory ( hostname strings ) .
* ------------------------------------------------------------------- */
Client::~Client() {
if ( mSettings->mSock != INVALID_SOCKET ) {
int rc = close( mSettings->mSock );
WARN_errno( rc == SOCKET_ERROR, "close" );
mSettings->mSock = INVALID_SOCKET;
}
DELETE_ARRAY( mBuf );
} // end ~Client
const double kSecs_to_usecs = 1 e6;
const int kBytes_to_Bits = 8 ;
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 );
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 );
}
/* -------------------------------------------------------------------
* Send data using the connected UDP / TCP socket ,
* until a termination flag is reached .
* Does not close the socket .
* ------------------------------------------------------------------- */
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 );
}
// end Run
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" );
}
}
}
}
/* -------------------------------------------------------------------
* Setup a socket connected to a server .
* If inLocalhost is not null , bind to that address , specifying
* which outgoing interface to use .
* ------------------------------------------------------------------- */
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
rc = bind( mSettings->mSock, (sockaddr*) &mSettings->local,
SockAddr_get_sizeof_sockaddr( &mSettings->local ) );
WARN_errno( rc == SOCKET_ERROR, "bind" );
}
// 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
/* -------------------------------------------------------------------
* Send a datagram on the socket . The datagram ' s contents should signify
* a FIN to the application . Keep re - transmitting until an
* acknowledgement datagram is received .
* ------------------------------------------------------------------- */
void Client::write_UDP_FIN( ) {
int rc;
fd_set readSet;
struct timeval timeout;
int count = 0 ;
while ( count < 10 ) {
count++;
// write data
write( mSettings->mSock, mBuf, mSettings->mBufLen );
// 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 );
}
// end write_UDP_FIN
Messung V0.5 in Prozent C=88 H=92 G=89
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland