1 /*---------------------------------------------------------------
2 * Copyright (c) 1999,2000,2001,2002,2003
3 * The Board of Trustees of the University of Illinois
5 *---------------------------------------------------------------
6 * Permission is hereby granted, free of charge, to any person
7 * obtaining a copy of this software (Iperf) and associated
8 * documentation files (the "Software"), to deal in the Software
9 * without restriction, including without limitation the
10 * rights to use, copy, modify, merge, publish, distribute,
11 * sublicense, and/or sell copies of the Software, and to permit
12 * persons to whom the Software is furnished to do
13 * so, subject to the following conditions:
16 * Redistributions of source code must retain the above
17 * copyright notice, this list of conditions and
18 * the following disclaimers.
21 * Redistributions in binary form must reproduce the above
22 * copyright notice, this list of conditions and the following
23 * disclaimers in the documentation and/or other materials
24 * provided with the distribution.
27 * Neither the names of the University of Illinois, NCSA,
28 * nor the names of its contributors may be used to endorse
29 * or promote products derived from this Software without
30 * specific prior written permission.
32 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
33 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
34 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
35 * NONINFRINGEMENT. IN NO EVENT SHALL THE CONTIBUTORS OR COPYRIGHT
36 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
37 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
38 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE
39 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
40 * ________________________________________________________________
41 * National Laboratory for Applied Network Research
42 * National Center for Supercomputing Applications
43 * University of Illinois at Urbana-Champaign
44 * http://www.ncsa.uiuc.edu
45 * ________________________________________________________________
48 * by Mark Gates <mgates@nlanr.net>
49 * -------------------------------------------------------------------
50 * A client thread initiates a connect to the server and handles
51 * sending and receiving data, then closes the socket.
52 * ------------------------------------------------------------------- */
57 #include "SocketAddr.h"
58 #include "PerfSocket.hpp"
59 #include "Extractor.h"
64 /* -------------------------------------------------------------------
65 * Store server hostname, optionally local hostname, and socket info.
66 * ------------------------------------------------------------------- */
68 Client::Client( thread_Settings *inSettings ) {
69 mSettings = inSettings;
76 mBuf = new char[ mSettings->mBufLen ];
77 pattern( mBuf, mSettings->mBufLen );
78 if ( isFileInput( mSettings ) ) {
79 if ( !isSTDIN( mSettings ) )
80 Extractor_Initialize( mSettings->mFileName, mSettings->mBufLen, mSettings );
82 Extractor_InitializeFile( stdin, mSettings->mBufLen, mSettings );
84 if ( !Extractor_canRead( mSettings ) ) {
85 unsetFileInput( mSettings );
89 if ( isReport( inSettings ) ) {
90 ReportSettings( inSettings );
91 if ( mSettings->multihdr && isMultipleReport( inSettings ) ) {
92 mSettings->multihdr->report->connection.peer = mSettings->peer;
93 mSettings->multihdr->report->connection.local = mSettings->local;
99 /* -------------------------------------------------------------------
100 * Delete memory (hostname strings).
101 * ------------------------------------------------------------------- */
104 if ( mSettings->mSock != INVALID_SOCKET ) {
105 int rc = close( mSettings->mSock );
106 WARN_errno( rc == SOCKET_ERROR, "close" );
107 mSettings->mSock = INVALID_SOCKET;
109 DELETE_ARRAY( mBuf );
112 const double kSecs_to_usecs = 1.0e6;
113 const double kBytes_to_Bits = 8.0;
115 /* -------------------------------------------------------------------
116 * Send data using the connected UDP/TCP socket,
117 * until a termination flag is reached.
118 * Does not close the socket.
119 * ------------------------------------------------------------------- */
121 void Client::Run( void ) {
122 struct dgram_record* mBuf_Dgram = (struct dgram_record*) mBuf;
123 long currLen = 0, packet_gap = 0, delta, loop_time = 0, adjust = 0;
125 bool canRead = true, // Indicates if the stream is readable
126 mMode_Time = isModeTime( mSettings );
127 ReportStruct *reportstruct = NULL;
129 // setup termination variables
132 if (mSettings->mAmount == 0)
133 mEndTime.add(2209032.0); // 7 years - i.e. continuous
135 mEndTime.add(mSettings->mAmount / 100.0);
138 if ( isPacketOriented( mSettings ) ) {
139 // compute delay for bandwidth restriction, constrained to [0,1] seconds
140 packet_gap = (long)((double)(mSettings->mBufLen * kSecs_to_usecs * kBytes_to_Bits) /
141 (double)mSettings->mDgramRate);
143 if (packet_gap < 0 || packet_gap > kSecs_to_usecs) {
144 fprintf( stderr, warn_delay_large, packet_gap / kSecs_to_usecs );
145 packet_gap = (long)kSecs_to_usecs;
147 // Initialise adjustment variable: in this way, the first adjustment will be
148 // the latency for sending the first packet, and all sending times are
149 // synchronised with regard to the first timestamp.
150 adjust = -packet_gap;
152 // Due to the included timestamps etc,
153 // reduce the read size by an amount equal to the header size
154 if ( isFileInput( mSettings ) ) {
155 size_t offset = sizeof(struct dgram_record);
157 if (!isCompat(mSettings))
158 offset += sizeof(struct client_hdr);
160 Extractor_reduceReadSize(offset, mSettings);
165 // InitReport handles Barrier for multiple Streams
166 mSettings->reporthdr = InitReport( mSettings );
167 reportstruct = new ReportStruct;
169 // Connectionless protocols use the first message as "connect" message (which is
170 // counted, but their FIN is not counted. For connection-oriented protocols we
171 // start at message #1 instead of at #0; and the FIN is not counted.
172 reportstruct->packetID = (!isConnectionLess(mSettings) && isPacketOriented(mSettings));
174 // Set a timestamp now corresponding to an imaginary zero-th send time.
175 lastPacketTime.setnow();
178 // Test case: drop 17 packets and send 2 out-of-order:
179 // sequence 51, 52, 70, 53, 54, 71, 72
180 //switch( datagramID ) {
181 // case 53: datagramID = 70; break;
182 // case 71: datagramID = 53; break;
183 // case 55: datagramID = 71; break;
187 // Note that the timestamp does not account for the time-to-wire of the packet.
188 // This plays a role when looking at jitter/delay values.
189 gettimeofday( &(reportstruct->packetTime), NULL );
191 if ( isPacketOriented( mSettings ) ) {
192 // Increment packet ID after sending and before reporting
193 // UDP: sends from 0..n-1, counts from 1..n
194 // DCCP: sends from 1..n, counts from 2..n+1
195 // This difference is of interest for the client only (the server sees
196 // 1..n). The client uses cntDatagrams, so that the count is correct.
197 // (Consider the trick in CloseReport() which resetts packetID.)
198 mBuf_Dgram->id = htonl( reportstruct->packetID++ );
199 mBuf_Dgram->tv_sec = htonl( reportstruct->packetTime.tv_sec );
200 mBuf_Dgram->tv_usec = htonl( reportstruct->packetTime.tv_usec );
201 } else if (!mMode_Time && mSettings->mAmount < mSettings->mBufLen)
202 mSettings->mBufLen = mSettings->mAmount;
204 // Read the next data block from the file if it's file input
205 if ( isFileInput( mSettings ) ) {
206 Extractor_getNextDataBlock( readAt, mSettings );
207 canRead = Extractor_canRead( mSettings ) != 0;
210 // Put the packet onto the wire.
211 // When EAGAIN is returned (DCCP), the internal TX buffer is full: due to
212 // congestion-control issues the packet can not be transported now.
214 currLen = write( mSettings->mSock, mBuf, mSettings->mBufLen );
215 } while (currLen < 0 && errno == EAGAIN);
218 WARN_errno(1, "client write");
222 reportstruct->packetLen = currLen;
223 ReportPacket( mSettings->reporthdr, reportstruct );
226 // time-oriented mode (-t): termination
227 canRead = mEndTime.after(reportstruct->packetTime);
229 // amount-oriented mode (-n): mAmount is unsigned
230 if (currLen >= mSettings->mAmount)
232 mSettings->mAmount -= currLen;
235 if (isPacketOriented(mSettings)) {
236 // Adjust the inter-packet gap using the following variables:
237 // delta is the gap in between calls to send()
238 // adjust acts as a token bucket whenever delta != packet_gap
239 // loop_time equals packet_gap if adjust==0, it is corrected otherwise
241 // TODO this doesn't work well in certain cases, like 2 parallel streams
243 delta = lastPacketTime.delta_usec();
244 adjust += packet_gap - delta;
245 loop_time = packet_gap + adjust;
248 delay_loop(loop_time);
251 } while (canRead && !sInterupted);
254 gettimeofday( &(reportstruct->packetTime), NULL );
255 CloseReport( mSettings->reporthdr, reportstruct );
257 if ( isPacketOriented( mSettings ) ) {
258 // send a final terminating datagram
259 // For connectionless protocols, don't count in the mTotalLen.
260 // The server counts this one, but didn't count our first datagram
261 // (connect message), so we're even now.
263 // store datagram ID into buffer
264 // The negative datagram ID signifies termination to the server.
265 mBuf_Dgram->id = htonl( -(reportstruct->packetID) );
266 mBuf_Dgram->tv_sec = htonl( reportstruct->packetTime.tv_sec );
267 mBuf_Dgram->tv_usec = htonl( reportstruct->packetTime.tv_usec );
269 if ( isMulticast( mSettings ) )
270 write( mSettings->mSock, mBuf, mSettings->mBufLen );
274 DELETE_PTR( reportstruct );
275 EndReport( mSettings->reporthdr );
278 void Client::InitiateServer()
280 client_hdr *temp_hdr = (client_hdr*)mBuf;
282 if (isCompat(mSettings))
284 // connection-less protocols communicate their settings in the first
285 // packet sent to the server; this packet is not counted by the server
286 if (isConnectionLess(mSettings)) {
287 dgram_record *record_hdr = (dgram_record *)mBuf;
288 temp_hdr = (client_hdr*)(record_hdr + 1);
291 Settings_GenerateClientHdr( mSettings, temp_hdr );
293 // connection-oriented protocols use a short "init" message
294 if ( !isConnectionLess( mSettings ) ) {
298 rc = send(mSettings->mSock, mBuf, sizeof(client_hdr), 0);
299 } while (rc < 0 && errno == EAGAIN);
300 WARN_errno(rc < 0, "write failed in InitiateServer()");
304 /* -------------------------------------------------------------------
305 * Setup a socket connected to a server.
306 * If inLocalhost is not null, bind to that address, specifying
307 * which outgoing interface to use.
308 * ------------------------------------------------------------------- */
309 void Client::Connect()
311 socklen_t size_local = sizeof(mSettings->local);
313 MakeSocket(mSettings);
315 /* determine local interface after establishing the connection */
316 getsockname(mSettings->mSock, (struct sockaddr *)&mSettings->local, &size_local);
318 /* The DCCP packet size must not exceed the MPS (RFC 4340, 14.) */
319 if (mSettings->mProtocol == kProto_DCCP) {
320 unsigned mps = getsock_dccp_mps(mSettings->mSock);
322 if (mSettings->mBufLen > mps)
323 die("Buffer length %d exceeds DCCP MPS %d. Use a smaller buffer size "
324 "(-l) on server/client.", mSettings->mBufLen, mps);
329 /* -------------------------------------------------------------------
330 * Send a datagram on the socket. The datagram's contents should signify
331 * a FIN to the application. Keep re-transmitting until an
332 * acknowledgement datagram is received.
333 * ------------------------------------------------------------------- */
334 void Client::write_dgram_FIN( ) {
337 struct timeval timeout;
338 size_t len = mSettings->mBufLen;
340 // we don't need the full buffer size here - it is not counted
341 if (!isConnectionLess(mSettings))
342 len = sizeof(dgram_record);
344 while ( count < 10 ) {
348 write( mSettings->mSock, mBuf, len);
350 // wait until the socket is readable, or our timeout expires
352 FD_SET( mSettings->mSock, &readSet );
354 timeout.tv_usec = 250000; // quarter second, 250 ms
356 rc = select( mSettings->mSock+1, &readSet, NULL, NULL, &timeout );
357 FAIL_errno( rc == SOCKET_ERROR, "select", mSettings );
363 // socket ready to read
364 rc = read( mSettings->mSock, mBuf, mSettings->mBufLen );
365 WARN_errno( rc < 0, "read" );
368 } else if ( rc >= (int) (sizeof(dgram_record) + sizeof(server_hdr)) ) {
369 ReportServerUDP( mSettings, (server_hdr*) ((dgram_record*)mBuf + 1) );
374 fprintf( stderr, warn_no_ack, mSettings->mSock, count );
376 // end write_dgram_FIN