#endif
} /* end warn_errno */
+/* fatal error */
+void die(const char *fmt, ...)
+{
+ va_list ap;
+
+ fflush(stdout); /* in case stdout and stderr are the same */
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ if (errno)
+ fprintf(stderr, " (%s)", strerror(errno));
+ fputc('\n', stderr);
+ fflush(stderr);
+ exit(1);
+}
+
#ifdef __cplusplus
} /* end extern "C" */
#endif
// UDP / TCP
void Send( void );
- void write_UDP_FIN( );
+ void write_dgram_FIN( );
// client connect
void Connect( );
extern const char tcp_window_size[];
-extern const char udp_buffer_size[];
+extern const char dgram_buffer_size[];
extern const char window_default[];
extern const char warn_invalid_compatibility_option[];
-extern const char warn_implied_udp[];
-
extern const char warn_implied_compatibility[];
extern const char warn_buffer_too_small[];
#include "Mutex.h"
#include "Settings.hpp"
+ void MakeSocket( thread_Settings *inSettings );
void SetSocketOptions( thread_Settings *inSettings );
// handle interupts
int PacketID;
int mBufLen; // -l
int mMSS; // -M
- int mTCPWin; // -w
+ int mWinSize; // -w
/* flags is a BitMask of old bools
bool mBufLenSet; // -l
bool mCompat; // -C
bool mSuggestWin; // -W
bool mUDP;
bool mMode_time;*/
- int flags;
+ int flags;
+ Protocol mProtocol;
// enums (which should be special int's)
ThreadMode mThreadMode; // -s or -c
ReportMode mode;
// accepts connection and receives data
void Run( void );
- void write_UDP_AckFIN( );
+ void write_dgram_AckFin( );
static void Sig_Int( int inSigno );
extern "C" {
#endif
+// Convention: odd=connection-oriented, even=connection-less
+typedef enum Protocol {
+ kProto_TCP = IPPROTO_TCP,
+ kProto_DCCP = IPPROTO_DCCP,
+ kProto_UDP = IPPROTO_UDP,
+} Protocol;
+
+static inline const char *protoName(const unsigned proto)
+{
+ switch(proto) {
+ case kProto_TCP: return "TCP";
+ case kProto_DCCP: return "DCCP";
+ case kProto_UDP: return "UDP";
+ default: return "(unknown)";
+ }
+}
+
+static inline unsigned sockType(const Protocol p)
+{
+ switch (p) {
+ case kProto_TCP: return SOCK_STREAM;
+ case kProto_UDP: return SOCK_DGRAM;
+ case kProto_DCCP: return SOCK_DCCP;
+ }
+}
+
+static inline bool is_connectionless(const Protocol p)
+{
+ return p == kProto_UDP;
+}
+
// server/client mode
typedef enum ThreadMode {
kMode_Unknown = 0,
int Extractor_size;
int mBufLen; // -l
int mMSS; // -M
- int mTCPWin; // -w
+ int mWinSize; // -w
/* flags is a BitMask of old bools
bool mBufLenSet; // -l
bool mCompat; // -C
bool mNoServerReport; // -x
bool mNoMultReport; // -x m
bool mSinlgeClient; // -1 */
- int flags;
+ int flags;
+ Protocol mProtocol;
+
// enums (which should be special int's)
ThreadMode mThreadMode; // -s or -c
ReportMode mReportMode;
- TestMode mMode; // -r or -d
+ TestMode mMode; // -r or -d
// Hopefully int64_t's
- max_size_t mUDPRate; // -b or -u
+ max_size_t mDgramRate; // -b
max_size_t mAmount; // -n or -t
// doubles
double mInterval; // -i
#define FLAG_STDIN 0x00000100
#define FLAG_STDOUT 0x00000200
#define FLAG_SUGGESTWIN 0x00000400
-#define FLAG_UDP 0x00000800
#define FLAG_MODETIME 0x00001000
#define FLAG_REPORTSETTINGS 0x00002000
#define FLAG_MULTICAST 0x00004000
#define FLAG_NOMULTREPORT 0x00080000
#define FLAG_SINGLECLIENT 0x00100000
#define FLAG_SINGLEUDP 0x00200000
+#define FLAG_PACKETORIENTED 0x01000000
+
#define isBuflenSet(settings) ((settings->flags & FLAG_BUFLENSET) != 0)
#define isCompat(settings) ((settings->flags & FLAG_COMPAT) != 0)
#define isSTDIN(settings) ((settings->flags & FLAG_STDIN) != 0)
#define isSTDOUT(settings) ((settings->flags & FLAG_STDOUT) != 0)
#define isSuggestWin(settings) ((settings->flags & FLAG_SUGGESTWIN) != 0)
-#define isUDP(settings) ((settings->flags & FLAG_UDP) != 0)
#define isModeTime(settings) ((settings->flags & FLAG_MODETIME) != 0)
#define isReport(settings) ((settings->flags & FLAG_REPORTSETTINGS) != 0)
#define isMulticast(settings) ((settings->flags & FLAG_MULTICAST) != 0)
// end Active Low
#define isSingleClient(settings) ((settings->flags & FLAG_SINGLECLIENT) != 0)
#define isSingleUDP(settings) ((settings->flags & FLAG_SINGLEUDP) != 0)
+#define isConnectionLess(settings) is_connectionless((settings)->mProtocol)
+#define isPacketOriented(settings) (((settings)->flags & FLAG_PACKETORIENTED) != 0)
#define setBuflenSet(settings) settings->flags |= FLAG_BUFLENSET
#define setCompat(settings) settings->flags |= FLAG_COMPAT
#define setSTDIN(settings) settings->flags |= FLAG_STDIN
#define setSTDOUT(settings) settings->flags |= FLAG_STDOUT
#define setSuggestWin(settings) settings->flags |= FLAG_SUGGESTWIN
-#define setUDP(settings) settings->flags |= FLAG_UDP
#define setModeTime(settings) settings->flags |= FLAG_MODETIME
#define setReport(settings) settings->flags |= FLAG_REPORTSETTINGS
#define setMulticast(settings) settings->flags |= FLAG_MULTICAST
#define setNoMultReport(settings) settings->flags |= FLAG_NOMULTREPORT
#define setSingleClient(settings) settings->flags |= FLAG_SINGLECLIENT
#define setSingleUDP(settings) settings->flags |= FLAG_SINGLEUDP
+#define setPacketOriented(settings) settings->flags |= FLAG_PACKETORIENTED
#define unsetBuflenSet(settings) settings->flags &= ~FLAG_BUFLENSET
#define unsetCompat(settings) settings->flags &= ~FLAG_COMPAT
#define unsetSTDIN(settings) settings->flags &= ~FLAG_STDIN
#define unsetSTDOUT(settings) settings->flags &= ~FLAG_STDOUT
#define unsetSuggestWin(settings) settings->flags &= ~FLAG_SUGGESTWIN
-#define unsetUDP(settings) settings->flags &= ~FLAG_UDP
#define unsetModeTime(settings) settings->flags &= ~FLAG_MODETIME
#define unsetReport(settings) settings->flags &= ~FLAG_REPORTSETTINGS
#define unsetMulticast(settings) settings->flags &= ~FLAG_MULTICAST
#define unsetNoMultReport(settings) settings->flags &= ~FLAG_NOMULTREPORT
#define unsetSingleClient(settings) settings->flags &= ~FLAG_SINGLECLIENT
#define unsetSingleUDP(settings) settings->flags &= ~FLAG_SINGLEUDP
+#define unsetPacketOriented(settings) settings->flags &= ~FLAG_PACKETORIENTED
#define HEADER_VERSION1 0x80000000
#define RUN_NOW 0x00000001
-// used to reference the 4 byte ID number we place in UDP datagrams
-// use int32_t if possible, otherwise a 32 bit bitfield (e.g. on J90)
-typedef struct UDP_datagram {
+/*
+ * Datagram record for record-oriented applications
+ * used to reference the 4 byte ID number we place in UDP datagrams
+ * use int32_t if possible, otherwise a 32 bit bitfield (e.g. on J90)
+ */
+typedef struct dgram_record {
#ifdef HAVE_INT32_T
int32_t id;
u_int32_t tv_sec;
unsigned int tv_sec : 32;
unsigned int tv_usec : 32;
#endif
-} UDP_datagram;
+} dgram_record;
/*
* The client_hdr structure is sent from clients
#define SHUT_RDWR 2
#endif // SHUT_RD
+/* DCCP-specific definitions */
+#include <linux/dccp.h>
+#ifndef SOCK_DCCP
+#define SOCK_DCCP 6 /* include/linux/net.h */
+#endif
+#ifndef IPPROTO_DCCP
+#define IPPROTO_DCCP 33 /* include/linux/in.h */
+#endif
+#ifndef SOL_DCCP
+#define SOL_DCCP 269 /* include/linux/socket.h */
+#endif
#endif /* HEADERS_H */
-
-
-
-
-
-
#ifndef UTIL_H
#define UTIL_H
-
+#include <stdarg.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
* set/getsockopt wrappers for SO_RCVBUF and SO_SNDBUF; TCP_MAXSEG
* socket.c
* ------------------------------------------------------------------- */
-int setsock_tcp_windowsize( int inSock, int inTCPWin, int inSend );
-int getsock_tcp_windowsize( int inSock, int inSend );
+int setsock_tcp_windowsize(int inSock, int inTCPWin, int inSend);
+int set_buffer_sock_size(int inSock, int inWinSize, bool inSend);
+int get_buffer_sock_size(int inSock, int inSend);
void setsock_tcp_mss( int inSock, int inTCPWin );
int getsock_tcp_mss( int inSock );
+int getsock_dccp_mps( int inSock );
+
/* -------------------------------------------------------------------
* signal handlers
* signal.c
* error handlers
* error.c
* ------------------------------------------------------------------- */
+void die(const char *fmt, ...);
void warn ( const char *inMessage, const char *inFile, int inLine );
void warn_errno( const char *inMessage, const char *inFile, int inLine );
#ifdef __cplusplus
} /* end extern "C" */
#endif
-
#endif /* UTIL_H */
-#define IPERF_VERSION "2.0.2"
-#define IPERF_VERSION_DATE "03 May 2005"
+#define IPERF_VERSION "2.0.2 with support for DCCP"
+#define IPERF_VERSION_DATE "20th Jan 2009"
mSettings = inSettings;
mBuf = NULL;
+ // connect
+ Connect( );
+
// initialize buffer
mBuf = new char[ mSettings->mBufLen ];
pattern( mBuf, mSettings->mBufLen );
}
}
- // connect
- Connect( );
-
if ( isReport( inSettings ) ) {
ReportSettings( inSettings );
if ( mSettings->multihdr && isMultipleReport( inSettings ) ) {
DELETE_ARRAY( mBuf );
} // end ~Client
-const double kSecs_to_usecs = 1e6;
-const int kBytes_to_Bits = 8;
+const double kSecs_to_usecs = 1.0e6;
+const double kBytes_to_Bits = 8.0;
/* -------------------------------------------------------------------
* Send data using the connected UDP/TCP socket,
* ------------------------------------------------------------------- */
void Client::Run( void ) {
- struct UDP_datagram* mBuf_UDP = (struct UDP_datagram*) mBuf;
- long currLen = 0;
-
- int delay_target = 0;
- int delay = 0;
- int adjust = 0;
-
+ struct dgram_record* mBuf_Dgram = (struct dgram_record*) mBuf;
+ long currLen = 0, packet_gap = 0, delta, loop_time = 0, adjust = 0;
char* readAt = mBuf;
-
- // Indicates if the stream is readable
- bool canRead = true, mMode_Time = isModeTime( mSettings );
+ bool canRead = true, // Indicates if the stream is readable
+ mMode_Time = isModeTime( mSettings );
+ ReportStruct *reportstruct = NULL;
// setup termination variables
if ( mMode_Time ) {
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
-
+ if ( isPacketOriented( mSettings ) ) {
// 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;
+ packet_gap = (long)((double)(mSettings->mBufLen * kSecs_to_usecs * kBytes_to_Bits) /
+ (double)mSettings->mDgramRate);
+
+ if (packet_gap < 0 || packet_gap > kSecs_to_usecs) {
+ fprintf( stderr, warn_delay_large, packet_gap / kSecs_to_usecs );
+ packet_gap = (long)kSecs_to_usecs;
}
+ // Initialise adjustment variable: in this way, the first adjustment will be
+ // the latency for sending the first packet, and all sending times are
+ // synchronised with regard to the first timestamp.
+ adjust = -packet_gap;
+
+ // Due to the included timestamps etc,
+ // reduce the read size by an amount equal to the header size
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);
- }
+ size_t offset = sizeof(struct dgram_record);
+
+ if (!isCompat(mSettings))
+ offset += sizeof(struct client_hdr);
+
+ Extractor_reduceReadSize(offset, mSettings);
+ readAt += offset;
}
}
- ReportStruct *reportstruct = NULL;
-
// InitReport handles Barrier for multiple Streams
mSettings->reporthdr = InitReport( mSettings );
reportstruct = new ReportStruct;
- reportstruct->packetID = 0;
+ // Connectionless protocols use the first message as "connect" message (which is
+ // counted, but their FIN is not counted. For connection-oriented protocols we
+ // start at message #1 instead of at #0; and the FIN is not counted.
+ reportstruct->packetID = (!isConnectionLess(mSettings) && isPacketOriented(mSettings));
+
+ // Set a timestamp now corresponding to an imaginary zero-th send time.
lastPacketTime.setnow();
-
- do {
+ do {
// Test case: drop 17 packets and send 2 out-of-order:
// sequence 51, 52, 70, 53, 54, 71, 72
//switch( datagramID ) {
// 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;
- }
- }
+ // Note that the timestamp does not account for the time-to-wire of the packet.
+ // This plays a role when looking at jitter/delay values.
+ gettimeofday( &(reportstruct->packetTime), NULL );
- // Read the next data block from
- // the file if it's file input
+ if ( isPacketOriented( mSettings ) ) {
+ // Increment packet ID after sending and before reporting
+ // UDP: sends from 0..n-1, counts from 1..n
+ // DCCP: sends from 1..n, counts from 2..n+1
+ // This difference is of interest for the client only (the server sees
+ // 1..n). The client uses cntDatagrams, so that the count is correct.
+ // (Consider the trick in CloseReport() which resetts packetID.)
+ mBuf_Dgram->id = htonl( reportstruct->packetID++ );
+ mBuf_Dgram->tv_sec = htonl( reportstruct->packetTime.tv_sec );
+ mBuf_Dgram->tv_usec = htonl( reportstruct->packetTime.tv_usec );
+ } else if (!mMode_Time && mSettings->mAmount < mSettings->mBufLen)
+ mSettings->mBufLen = mSettings->mAmount;
+
+ // 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;
}
- // report packets
+ // Put the packet onto the wire.
+ // When EAGAIN is returned (DCCP), the internal TX buffer is full: due to
+ // congestion-control issues the packet can not be transported now.
+ do {
+ currLen = write( mSettings->mSock, mBuf, mSettings->mBufLen );
+ } while (currLen < 0 && errno == EAGAIN);
+
+ if (currLen < 0) {
+ WARN_errno(1, "client write");
+ break;
+ }
+ // update statistics
reportstruct->packetLen = currLen;
ReportPacket( mSettings->reporthdr, reportstruct );
-
- if ( delay > 0 ) {
- delay_loop( delay );
- }
- if ( !mMode_Time ) {
+
+ if (mMode_Time) {
+ // time-oriented mode (-t): termination
+ canRead = mEndTime.after(reportstruct->packetTime);
+ } else {
+ // amount-oriented mode (-n): mAmount is unsigned
+ if (currLen >= mSettings->mAmount)
+ break;
mSettings->mAmount -= currLen;
}
- } while ( ! (sInterupted ||
- (mMode_Time && mEndTime.before( reportstruct->packetTime )) ||
- (!mMode_Time && 0 >= mSettings->mAmount)) && canRead );
+ if (isPacketOriented(mSettings)) {
+ // Adjust the inter-packet gap using the following variables:
+ // delta is the gap in between calls to send()
+ // adjust acts as a token bucket whenever delta != packet_gap
+ // loop_time equals packet_gap if adjust==0, it is corrected otherwise
+ //
+ // TODO this doesn't work well in certain cases, like 2 parallel streams
+ //
+ delta = lastPacketTime.delta_usec();
+ adjust += packet_gap - delta;
+ loop_time = packet_gap + adjust;
+
+ if (loop_time > 0)
+ delay_loop(loop_time);
+ }
+
+ } while (canRead && !sInterupted);
// stop timing
gettimeofday( &(reportstruct->packetTime), NULL );
CloseReport( mSettings->reporthdr, reportstruct );
- if ( isUDP( mSettings ) ) {
+ if ( isPacketOriented( 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.
-
+ // For connectionless protocols, don't count in the mTotalLen.
+ // The server counts this one, but didn't count our first datagram
+ // (connect message), so we're even now.
+
// 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 );
+ // The negative datagram ID signifies termination to the server.
+ mBuf_Dgram->id = htonl( -(reportstruct->packetID) );
+ mBuf_Dgram->tv_sec = htonl( reportstruct->packetTime.tv_sec );
+ mBuf_Dgram->tv_usec = htonl( reportstruct->packetTime.tv_usec );
- if ( isMulticast( mSettings ) ) {
+ if ( isMulticast( mSettings ) )
write( mSettings->mSock, mBuf, mSettings->mBufLen );
- } else {
- write_UDP_FIN( );
- }
+ else
+ write_dgram_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" );
- }
- }
- }
+
+void Client::InitiateServer()
+{
+ client_hdr *temp_hdr = (client_hdr*)mBuf;
+
+ if (isCompat(mSettings))
+ return;
+ // connection-less protocols communicate their settings in the first
+ // packet sent to the server; this packet is not counted by the server
+ if (isConnectionLess(mSettings)) {
+ dgram_record *record_hdr = (dgram_record *)mBuf;
+ temp_hdr = (client_hdr*)(record_hdr + 1);
+ }
+
+ Settings_GenerateClientHdr( mSettings, temp_hdr );
+
+ // connection-oriented protocols use a short "init" message
+ if ( !isConnectionLess( mSettings ) ) {
+ int rc;
+
+ do {
+ rc = send(mSettings->mSock, mBuf, sizeof(client_hdr), 0);
+ } while (rc < 0 && errno == EAGAIN);
+ WARN_errno(rc < 0, "write failed in InitiateServer()");
+ }
}
/* -------------------------------------------------------------------
* If inLocalhost is not null, bind to that address, specifying
* which outgoing interface to use.
* ------------------------------------------------------------------- */
-
-void Client::Connect( ) {
+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" );
-
+ // The local socket needs to be filled in first, since the
+ // IPv6 address testing for the peer depends on the type of
+ // the local socket
+ SockAddr_localAddr( mSettings );
+ SockAddr_remoteAddr( mSettings );
+ MakeSocket( mSettings);
SetSocketOptions( mSettings );
-
- SockAddr_localAddr( mSettings );
if ( mSettings->mLocalhost != NULL ) {
// bind socket to local address
rc = bind( mSettings->mSock, (sockaddr*) &mSettings->local,
&mSettings->size_local );
getpeername( mSettings->mSock, (sockaddr*) &mSettings->peer,
&mSettings->size_peer );
-} // end Connect
+
+ /* The DCCP packet size must not exceed the MPS (RFC 4340, 14.) */
+ if (mSettings->mProtocol == kProto_DCCP) {
+ unsigned mps = getsock_dccp_mps(mSettings->mSock);
+
+ if (mSettings->mBufLen > mps)
+ die("Buffer length %d exceeds DCCP MPS %d. Use a smaller buffer size "
+ "(-l) on server/client.", mSettings->mBufLen, mps);
+ }
+}
+
/* -------------------------------------------------------------------
* 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;
+void Client::write_dgram_FIN( ) {
+ int rc, count = 0;
fd_set readSet;
struct timeval timeout;
+ size_t len = mSettings->mBufLen;
+
+ // we don't need the full buffer size here - it is not counted
+ if (!isConnectionLess(mSettings))
+ len = sizeof(dgram_record);
- int count = 0;
while ( count < 10 ) {
- count++;
+ count++;
// write data
- write( mSettings->mSock, mBuf, mSettings->mBufLen );
+ write( mSettings->mSock, mBuf, len);
// wait until the socket is readable, or our timeout expires
FD_ZERO( &readSet );
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) );
+ } else if ( rc >= (int) (sizeof(dgram_record) + sizeof(server_hdr)) ) {
+ ReportServerUDP( mSettings, (server_hdr*) ((dgram_record*)mBuf + 1) );
}
-
return;
}
}
-
fprintf( stderr, warn_no_ack, mSettings->mSock, count );
}
-// end write_UDP_FIN
+// end write_dgram_FIN
/**
* This is used to reduce the read size
- * Used in UDP transfer to accomodate the
+ * Used in datagram transfer to accommodate the
* the header (timestamp)
* @arg delta Size to reduce
*/
/* -------------------------------------------------------------------
* Listens for connections and starts Servers to handle data.
* For TCP, each accepted connection spawns a Server thread.
- * For UDP, handle all data in this thread for Win32 Only, otherwise
- * spawn a new Server thread.
+ * For datagram-oriented protocols, spawn a new Server thread.
* ------------------------------------------------------------------- */
-void Listener::Run( void ) {
+void Listener::Run(void)
+{
#ifdef sun
- if ( ( isUDP( mSettings ) &&
- isMulticast( mSettings ) &&
- !isSingleUDP( mSettings ) ) ||
- isSingleUDP( mSettings ) ) {
+ if ( ( isPacketOriented( mSettings ) &&
+ isMulticast( mSettings ) &&
+ !isSingleUDP( mSettings ) ) || isSingleUDP( mSettings ) ) {
UDPSingleServer();
- } else
#else
if ( isSingleUDP( mSettings ) ) {
UDPSingleServer();
- } else
#endif
- {
- bool client = false, UDP = isUDP( mSettings ), mCount = (mSettings->mThreads != 0);
+ } else {
+ bool client = false,
+ mCount = (mSettings->mThreads != 0);
thread_Settings *tempSettings = NULL;
Iperf_ListEntry *exist, *listtemp;
- client_hdr* hdr = ( UDP ? (client_hdr*) (((UDP_datagram*)mBuf) + 1) :
- (client_hdr*) mBuf);
+ client_hdr* hdr;
+
if ( mSettings->mHost != NULL ) {
client = true;
(sockaddr*) &server->peer ) ) {
// Not allowed try again
close( server->mSock );
- if ( isUDP( mSettings ) ) {
+ if ( isConnectionLess( mSettings ) ) {
mSettings->mSock = -1;
Listen();
}
tempSettings = NULL;
if ( !isCompat( mSettings ) && !isMulticast( mSettings ) ) {
- if ( !UDP ) {
- // TCP does not have the info yet
+ if ( !isConnectionLess(mSettings)) {
+ hdr = (client_hdr*) mBuf;
+
+ // TCP/DCCP does not have the info yet
if ( recv( server->mSock, (char*)hdr, sizeof(client_hdr), 0) > 0 ) {
- Settings_GenerateClientSettings( server, &tempSettings,
- hdr );
+ Settings_GenerateClientSettings( server, &tempSettings, hdr );
}
} else {
- Settings_GenerateClientSettings( server, &tempSettings,
- hdr );
+ hdr = (client_hdr *) ( ((dgram_record*)mBuf) + 1 );
+ Settings_GenerateClientSettings( server, &tempSettings, hdr );
}
}
-
if ( tempSettings != NULL ) {
client_init( tempSettings );
if ( tempSettings->mMode == kTest_DualTest ) {
thread_start( server );
// create a new socket
- if ( UDP ) {
+ if ( isConnectionLess( mSettings ) ) {
mSettings->mSock = -1;
Listen( );
}
/* -------------------------------------------------------------------
* Setup a socket listening on a port.
- * For TCP, this calls bind() and listen().
- * For UDP, this just calls bind().
+ * For connection-oriented protocols, this calls bind() and listen().
+ * For connection-less protocols, this just calls bind().
* If inLocalhost is not null, bind to that address rather than the
* wildcard server address, specifying what incoming interface to
* accept connections on.
* ------------------------------------------------------------------- */
-void Listener::Listen( ) {
+void Listener::Listen()
+{
int rc;
+ int boolean = 1;
+ Socklen_t len = sizeof(boolean);
SockAddr_localAddr( mSettings );
-
- // create an internet TCP socket
- int type = (isUDP( mSettings ) ? SOCK_DGRAM : SOCK_STREAM);
- int domain = (SockAddr_isIPv6( &mSettings->local ) ?
-#ifdef HAVE_IPV6
- AF_INET6
-#else
- AF_INET
-#endif
- : AF_INET);
-
- mSettings->mSock = socket( domain, type, 0 );
- WARN_errno( mSettings->mSock == INVALID_SOCKET, "socket" );
+ MakeSocket( mSettings );
SetSocketOptions( mSettings );
// reuse the address, so we can run if a former server was killed off
- int boolean = 1;
- Socklen_t len = sizeof(boolean);
setsockopt( mSettings->mSock, SOL_SOCKET, SO_REUSEADDR, (char*) &boolean, len );
- // bind socket to server address
+ // listen for connections (TCP/DCCP only).
rc = bind( mSettings->mSock, (sockaddr*) &mSettings->local, mSettings->size_local );
WARN_errno( rc == SOCKET_ERROR, "bind" );
- // listen for connections (TCP only).
// default backlog traditionally 5
- if ( !isUDP( mSettings ) ) {
+ if ( !isConnectionLess( mSettings ) ) {
rc = listen( mSettings->mSock, 5 );
WARN_errno( rc == SOCKET_ERROR, "listen" );
}
* until a new connection arrives.
* ------------------------------------------------------------------- */
-void Listener::Accept( thread_Settings *server ) {
-
+void Listener::Accept( thread_Settings *server )
+{
server->size_peer = sizeof(iperf_sockaddr);
- if ( isUDP( server ) ) {
+
+ if ( isConnectionLess( server ) ) {
/* -------------------------------------------------------------------
- * Do the equivalent of an accept() call for UDP sockets. This waits
- * on a listening UDP socket until we get a datagram.
+ * Do the equivalent of an accept() call for connection-less sockets.
+ * This waits on a listening datagram socket until we get a datagram.
* ------------------------------------------------------------------- */
int rc;
Iperf_ListEntry *exist;
int32_t datagramID;
+
server->mSock = INVALID_SOCKET;
while ( server->mSock == INVALID_SOCKET ) {
rc = recvfrom( mSettings->mSock, mBuf, mSettings->mBufLen, 0,
Mutex_Lock( &clients_mutex );
- // Handle connection for UDP sockets.
+ // Handle connection for datagram-based sockets.
exist = Iperf_present( &server->peer, clients);
- datagramID = ntohl( ((UDP_datagram*) mBuf)->id );
+ datagramID = ntohl( ((dgram_record*) mBuf)->id );
if ( exist == NULL && datagramID >= 0 ) {
server->mSock = mSettings->mSock;
int rc = connect( server->mSock, (struct sockaddr*) &server->peer,
server->size_peer );
- FAIL_errno( rc == SOCKET_ERROR, "connect UDP", mSettings );
+ FAIL_errno( rc == SOCKET_ERROR, "datagram-based connect", mSettings );
} else {
server->mSock = INVALID_SOCKET;
}
void Listener::UDPSingleServer( ) {
- bool client = false, UDP = isUDP( mSettings ), mCount = (mSettings->mThreads != 0);
+ bool client = false, mCount = (mSettings->mThreads != 0);
thread_Settings *tempSettings = NULL;
Iperf_ListEntry *exist, *listtemp;
int rc;
int32_t datagramID;
- client_hdr* hdr = ( UDP ? (client_hdr*) (((UDP_datagram*)mBuf) + 1) :
- (client_hdr*) mBuf);
ReportStruct *reportstruct = new ReportStruct;
+ dgram_record *dgram_hdr = (dgram_record *)mBuf;
+
+ assert( isPacketOriented( mSettings ) );
+
if ( mSettings->mHost != NULL ) {
client = true;
SockAddr_remoteAddr( mSettings );
}
- // Handle connection for UDP sockets.
+ // Handle connection for datagram-based sockets.
exist = Iperf_present( &server->peer, clients);
- datagramID = ntohl( ((UDP_datagram*) mBuf)->id );
+ datagramID = ntohl( dgram_hdr->id );
if ( datagramID >= 0 ) {
if ( exist != NULL ) {
// read the datagram ID and sentTime out of the buffer
reportstruct->packetID = datagramID;
- reportstruct->sentTime.tv_sec = ntohl( ((UDP_datagram*) mBuf)->tv_sec );
- reportstruct->sentTime.tv_usec = ntohl( ((UDP_datagram*) mBuf)->tv_usec );
+ reportstruct->sentTime.tv_sec = ntohl( dgram_hdr->tv_sec );
+ reportstruct->sentTime.tv_usec = ntohl( dgram_hdr->tv_usec );
reportstruct->packetLen = rc;
gettimeofday( &(reportstruct->packetTime), NULL );
if ( exist != NULL ) {
// read the datagram ID and sentTime out of the buffer
reportstruct->packetID = -datagramID;
- reportstruct->sentTime.tv_sec = ntohl( ((UDP_datagram*) mBuf)->tv_sec );
- reportstruct->sentTime.tv_usec = ntohl( ((UDP_datagram*) mBuf)->tv_usec );
+ reportstruct->sentTime.tv_sec = ntohl( dgram_hdr->tv_sec );
+ reportstruct->sentTime.tv_usec = ntohl( dgram_hdr->tv_usec );
reportstruct->packetLen = rc;
gettimeofday( &(reportstruct->packetTime), NULL );
gettimeofday( &(reportstruct->packetTime), NULL );
CloseReport( exist->server->reporthdr, reportstruct );
- if ( rc > (int) ( sizeof( UDP_datagram )
- + sizeof( server_hdr ) ) ) {
- UDP_datagram *UDP_Hdr;
- server_hdr *hdr;
-
- UDP_Hdr = (UDP_datagram*) mBuf;
+ if ( rc > (int)(sizeof(dgram_record) + sizeof(server_hdr)) ) {
+ server_hdr *hdr = (server_hdr *)(dgram_hdr + 1);
Transfer_Info *stats = GetReport( exist->server->reporthdr );
- hdr = (server_hdr*) (UDP_Hdr+1);
hdr->flags = htonl( HEADER_VERSION1 );
hdr->total_len1 = htonl( (long) (stats->TotalLen >> 32) );
EndReport( exist->server->reporthdr );
exist->server->reporthdr = NULL;
Iperf_delete( &(exist->server->peer), &clients );
- } else if ( rc > (int) ( sizeof( UDP_datagram )
- + sizeof( server_hdr ) ) ) {
- UDP_datagram *UDP_Hdr;
- server_hdr *hdr;
-
- UDP_Hdr = (UDP_datagram*) mBuf;
- hdr = (server_hdr*) (UDP_Hdr+1);
+
+ } else if ( rc > (int)(sizeof(dgram_record) + sizeof(server_hdr)) ) {
+ server_hdr *hdr = (server_hdr *) (dgram_hdr + 1);
hdr->flags = htonl( 0 );
}
sendto( mSettings->mSock, mBuf, mSettings->mBufLen, 0,
tempSettings = NULL;
if ( !isCompat( mSettings ) && !isMulticast( mSettings ) ) {
- Settings_GenerateClientSettings( server, &tempSettings,
- hdr );
+ client_hdr* hdr = (client_hdr *)(dgram_hdr + 1);
+
+ Settings_GenerateClientSettings(server, &tempSettings, hdr);
}
-l, --len #[KM] length of buffer to read or write (default 8 KB)\n\
-m, --print_mss print TCP maximum segment size (MTU - TCP/IP header)\n\
-p, --port # server port to listen on/connect to\n\
- -u, --udp use UDP rather than TCP\n\
+ -u, --udp use UDP as transport protocol\n\
+ -d, --dccp use DCCP as transport protocol\n\
-w, --window #[KM] TCP window size (socket buffer size)\n\
-B, --bind <host> bind to <host>, an interface or multicast address\n\
-C, --compatibility for use with older versions does not sent extra msgs\n\
-D, --daemon run the server as a daemon\n\
\n\
Client specific:\n\
- -b, --bandwidth #[KM] for UDP, bandwidth to send at in bits/sec\n\
+ -b, --bandwidth #[KM] for UDP/DCCP, bandwidth to send at in bits/sec\n\
(default 1 Mbit/sec, implies -u)\n\
-c, --client <host> run in client mode, connecting to <host>\n\
- -d, --dualtest Do a bidirectional test simultaneously\n\
- -n, --num #[KM] number of bytes to transmit (instead of -t)\n\
+ -2, --dualtest Do a bidirectional test simultaneously\n\
-r, --tradeoff Do a bidirectional test individually\n\
-t, --time # time in seconds to transmit for (default 10 secs)\n\
+ -n, --num #[KM] number of bytes to transmit (instead of -t)\n\
-F, --fileinput <name> input the data to be transmitted from a file\n\
-I, --stdin input the data to be transmitted from stdin\n\
- -L, --listenport # port to recieve bidirectional tests back on\n\
+ -L, --listenport # port to receive bidirectional tests back on\n\
-P, --parallel # number of parallel client threads to run\n\
-T, --ttl # time-to-live, for multicast (default 1)\n\
\n\
TCP_WINDOW_SIZE. Most other options can be set by an environment variable\n\
IPERF_<long option name>, such as IPERF_BANDWIDTH.\n\
\n\
-Report bugs to <dast@nlanr.net>\n";
+Report bugs to <gerrit@erg.abdn.ac.uk>";
// include a description of the threading in the version
#if defined( HAVE_POSIX_THREAD )
#endif
const char version[] =
-"iperf version " IPERF_VERSION " (" IPERF_VERSION_DATE ") " IPERF_THREADS "\n";
+"iperf version " IPERF_VERSION " (" IPERF_VERSION_DATE ") " IPERF_THREADS;
/* -------------------------------------------------------------------
* settings
const char tcp_window_size[] =
"TCP window size";
-const char udp_buffer_size[] =
-"UDP buffer size";
+const char dgram_buffer_size[] =
+"datagram buffer size";
const char window_default[] =
"(default)";
Datagrams\n";
const char report_bw_jitter_loss_format[] =
-"[%3d] %4.1f-%4.1f sec %ss %ss/sec %5.3f ms %4d/%5d (%.2g%%)\n";
+"[%3d] %4.1f-%4.1f sec %ss %ss/sec %6.3f ms %4d/%5d (%.2g%%)\n";
const char report_sum_bw_jitter_loss_format[] =
-"[SUM] %4.1f-%4.1f sec %ss %ss/sec %5.3f ms %4d/%5d (%.2g%%)\n";
+"[SUM] %4.1f-%4.1f sec %ss %ss/sec %6.3f ms %4d/%5d (%.2g%%)\n";
const char report_outoforder[] =
"[%3d] %4.1f-%4.1f sec %d datagrams received out-of-order\n";
"[%3d] local %s port %u connected with %s port %u\n";
const char report_mss_unsupported[] =
-"[%3d] MSS and MTU size unknown (TCP_MAXSEG not supported by OS?)\n";
+"[%3d] MSS and MTU size unknown (socket option not supported by OS?)\n";
const char report_mss[] =
"[%3d] MSS size %d bytes (MTU %d bytes, %s)\n";
const char warn_window_requested[] =
" (WARNING: requested %s)";
-const char warn_window_small[] = "\
-WARNING: TCP window size set to %d bytes. A small window size\n\
-will give poor performance. See the Iperf documentation.\n";
-
const char warn_delay_large[] =
"WARNING: delay too large, reducing from %.1f to 1.0 seconds.\n";
const char warn_invalid_compatibility_option[] =
"WARNING: option -%c is not valid in compatibility mode\n";
-const char warn_implied_udp[] =
-"WARNING: option -%c implies udp testing\n";
-
const char warn_implied_compatibility[] =
"WARNING: option -%c has implied compatibility mode\n";
const char warn_buffer_too_small[] =
-"WARNING: the UDP buffer was increased to %d for proper operation\n";
+"WARNING: the buffer was increased to %d for proper operation\n";
const char warn_invalid_single_threaded[] =
"WARNING: option -%c is not valid in single threaded versions\n";
* <netinet/in.h>
* <sys/socket.h>
* ------------------------------------------------------------------- */
-
-
#define HEADERS()
#include "headers.h"
#include "SocketAddr.h"
#include "util.h"
+// create an internet socket
+void MakeSocket(thread_Settings *inSettings)
+{
+ int type = 0, proto = 0, domain =
+ SockAddr_isIPv6(&inSettings->local) ? AF_INET6 :
+ AF_INET;
+
+ switch (inSettings->mProtocol) {
+ case kProto_TCP: type = SOCK_STREAM; break;
+ case kProto_UDP: type = SOCK_DGRAM; break;
+ case kProto_DCCP: type = SOCK_DCCP; break;
+ }
+ inSettings->mSock = socket( domain, type, proto );
+
+ WARN_errno( inSettings->mSock == INVALID_SOCKET, "socket" );
+}
+
/* -------------------------------------------------------------------
* 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) );
+void SetSocketOptions( thread_Settings *inSettings )
+{
+ int rc, val;
+ Socklen_t len = sizeof(int);
// check if we're sending multicast, and set TTL
if ( isMulticast( inSettings ) && ( inSettings->mTTL > 0 ) ) {
- int val = inSettings->mTTL;
+ val = inSettings->mTTL;
#ifdef HAVE_MULTICAST
if ( !SockAddr_isIPv6( &inSettings->local ) ) {
- int rc = setsockopt( inSettings->mSock, IPPROTO_IP, IP_MULTICAST_TTL,
- (const void*) &val, (Socklen_t) sizeof(val));
+ rc = setsockopt( inSettings->mSock, IPPROTO_IP, IP_MULTICAST_TTL,
+ &val, len);
WARN_errno( rc == SOCKET_ERROR, "multicast ttl" );
}
#ifdef HAVE_IPV6_MULTICAST
else {
- int rc = setsockopt( inSettings->mSock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
- (const void*) &val, (Socklen_t) sizeof(val));
+ rc = setsockopt( inSettings->mSock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ &val, len);
WARN_errno( rc == SOCKET_ERROR, "multicast ttl" );
}
-#endif
-#endif
+#endif /* IPV6_MULTICAST */
+#endif /* MULTICAST */
}
-#ifdef IP_TOS
-
// set IP TOS (type-of-service) field
+#ifdef IP_TOS
if ( inSettings->mTOS > 0 ) {
- int tos = inSettings->mTOS;
- Socklen_t len = sizeof(tos);
- int rc = setsockopt( inSettings->mSock, IPPROTO_IP, IP_TOS,
- (char*) &tos, len );
+ val = inSettings->mTOS;
+ rc = setsockopt( inSettings->mSock, IPPROTO_IP, IP_TOS, &val, len );
WARN_errno( rc == SOCKET_ERROR, "setsockopt IP_TOS" );
}
#endif
- if ( !isUDP( inSettings ) ) {
- // set the TCP maximum segment size
+
+ // TCP-specific options
+ if ( inSettings->mProtocol == kProto_TCP ) {
+
+ // set the TCP window size (socket buffer sizes)
+ // must occur before call to accept() for large window sizes
+ setsock_tcp_windowsize(inSettings->mSock, inSettings->mWinSize,
+ inSettings->mThreadMode == kMode_Client);
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 );
+ val = 1;
+ rc = setsockopt( inSettings->mSock, IPPROTO_TCP, TCP_NODELAY,
+ &val, len );
WARN_errno( rc == SOCKET_ERROR, "setsockopt TCP_NODELAY" );
}
#endif
+ } else {
+ rc = set_buffer_sock_size(inSettings->mSock, inSettings->mWinSize,
+ inSettings->mThreadMode == kMode_Client);
+ WARN_errno( rc < 0 , "setsockopt for buffer size" );
+ }
+ // DCCP-specific options
+ if ( inSettings->mProtocol == kProto_DCCP ) {
+ /*
+ * We use the service code SC:PERF (0x50455246) from
+ * draft-fairhurst-dccp-serv-codes to identify this service.
+ */
+ val = htonl(0x50455246); /* ALWAYS use htonl */
+ rc = setsockopt( inSettings->mSock, SOL_DCCP, DCCP_SOCKOPT_SERVICE,
+ &val, len );
+ WARN_errno( rc == SOCKET_ERROR, "setsockopt DCCP_SOCKOPT_SERVICE" );
}
}
-// end SetSocketOptions
stats->mFormat);
if ( stats->mUDP != (char)kMode_Server ) {
- // TCP Reporting
+ // TCP-like Reporting
printf( report_bw_format, stats->transferID,
stats->startTime, stats->endTime,
buffer, &buffer[sizeof(buffer)/2] );
} else {
- // UDP Reporting
+ // UDP-like Reporting
printf( report_bw_jitter_loss_format, stats->transferID,
stats->startTime, stats->endTime,
buffer, &buffer[sizeof(buffer)/2],
stats->mFormat);
if ( stats->mUDP != (char)kMode_Server ) {
- // TCP Reporting
+ // TCP-like Reporting
printf( report_sum_bw_format,
stats->startTime, stats->endTime,
buffer, &buffer[sizeof(buffer)/2] );
} else {
- // UDP Reporting
+ // UDP-like Reporting
printf( report_sum_bw_jitter_loss_format,
stats->startTime, stats->endTime,
buffer, &buffer[sizeof(buffer)/2],
void reporter_reportsettings( ReporterData *data ) {
int win, win_requested;
- win = getsock_tcp_windowsize( data->info.transferID,
- (data->mThreadMode == kMode_Listener ? 0 : 1) );
- win_requested = data->mTCPWin;
+ win = get_buffer_sock_size(data->info.transferID,
+ data->mThreadMode != kMode_Listener);
+ win_requested = data->mWinSize;
printf( seperator_line );
if ( data->mThreadMode == kMode_Listener ) {
printf( server_port,
- (isUDP( data ) ? "UDP" : "TCP"),
+ protoName(data->mProtocol),
data->mPort );
} else {
printf( client_port,
data->mHost,
- (isUDP( data ) ? "UDP" : "TCP"),
+ protoName(data->mProtocol),
data->mPort );
}
if ( data->mLocalhost != NULL ) {
}
}
- if ( isUDP( data ) ) {
+ if ( isPacketOriented( data ) ) {
printf( (data->mThreadMode == kMode_Listener ?
- server_datagram_size : client_datagram_size),
+ server_datagram_size : client_datagram_size),
data->mBufLen );
if ( SockAddr_isMulticast( &data->connection.peer ) ) {
printf( multicast_ttl, data->info.mTTL);
}
- }
+ } else if (data->mProtocol == kProto_DCCP)
+ printf("NOTE: running in bytestream-mode (maximum speed)\n");
+
byte_snprintf( buffer, sizeof(buffer), win,
toupper( data->info.mFormat));
- printf( "%s: %s", (isUDP( data ) ?
- udp_buffer_size : tcp_window_size), buffer );
+
+ if (data->mProtocol == kProto_TCP)
+ printf( "%s: %s", tcp_window_size, buffer);
+ else
+ printf( "%s %s: %s", protoName(data->mProtocol), dgram_buffer_size, buffer);
if ( win_requested == 0 ) {
printf( " %s", window_default );
data->mLocalhost = agent->mLocalhost;
data->mBufLen = agent->mBufLen;
data->mMSS = agent->mMSS;
- data->mTCPWin = agent->mTCPWin;
+ data->mWinSize = agent->mWinSize;
data->flags = agent->flags;
+ data->mProtocol = agent->mProtocol;
data->mThreadMode = agent->mThreadMode;
data->mode = agent->mReportMode;
data->info.mFormat = agent->mFormat;
data->info.mTTL = agent->mTTL;
- if ( isUDP( agent ) ) {
+ if ( isPacketOriented( agent ) ) {
multihdr->report->info.mUDP = (char)agent->mThreadMode;
}
if ( isConnectionReport( agent ) ) {
data->mLocalhost = agent->mLocalhost;
data->mBufLen = agent->mBufLen;
data->mMSS = agent->mMSS;
- data->mTCPWin = agent->mTCPWin;
+ data->mWinSize = agent->mWinSize;
data->flags = agent->flags;
+ data->mProtocol = agent->mProtocol;
data->mThreadMode = agent->mThreadMode;
data->mode = agent->mReportMode;
data->info.mFormat = agent->mFormat;
data->info.mTTL = agent->mTTL;
- if ( isUDP( agent ) ) {
+ if ( isPacketOriented( agent ) ) {
reporthdr->report.info.mUDP = (char)agent->mThreadMode;
}
} else {
*/
void CloseReport( ReportHeader *agent, ReportStruct *packet ) {
if ( agent != NULL) {
-
/*
* Using PacketID of -1 ends reporting
*/
data->type = SETTINGS_REPORT;
data->mBufLen = agent->mBufLen;
data->mMSS = agent->mMSS;
- data->mTCPWin = agent->mTCPWin;
+ data->mWinSize = agent->mWinSize;
data->flags = agent->flags;
+ data->mProtocol = agent->mProtocol;
data->mThreadMode = agent->mThreadMode;
data->mPort = agent->mPort;
data->info.mFormat = agent->mFormat;
Transfer_Info *stats = &reporthdr->report.info;
int finished = 0;
+ // update received amount and time
+ data->TotalLen += packet->packetLen;
+ data->packetTime = packet->packetTime;
data->cntDatagrams++;
- // If this is the last packet set the endTime
- if ( packet->packetID < 0 ) {
- data->packetTime = packet->packetTime;
+
+ if (packet->packetID < 0) {
+ // This is the last packet (FIN)
finished = 1;
- if ( reporthdr->report.mThreadMode != kMode_Client ) {
- data->TotalLen += packet->packetLen;
+ if (isConnectionLess(&reporthdr->report)) {
+ // connectionless protocols don't count the last payload
+ if (reporthdr->report.mThreadMode == kMode_Client)
+ data->TotalLen -= packet->packetLen;
+ } else {
+ // connection-oriented protocols dont't count the FIN
+ data->cntDatagrams--;
}
- } else {
- // update recieved amount and time
- data->packetTime = packet->packetTime;
- reporter_condprintstats( &reporthdr->report, reporthdr->multireport, finished );
- data->TotalLen += packet->packetLen;
- if ( packet->packetID != 0 ) {
- // UDP packet
- double transit;
- double deltaTransit;
-
+ } else if ( packet->packetID != 0 ) {
+ // UDP or DCCP packet
+ double transit, deltaTransit;
+
// from RFC 1889, Real Time Protocol (RTP)
// J = J + ( | D(i-1,i) | - J ) / 16
transit = TimeDifference( packet->packetTime, packet->sentTime );
stats->jitter += (deltaTransit - stats->jitter) / (16.0);
}
data->lastTransit = transit;
-
+
// packet loss occured if the datagram numbers aren't sequential
if ( packet->packetID != data->PacketID + 1 ) {
if ( packet->packetID < data->PacketID + 1 ) {
if ( packet->packetID > data->PacketID ) {
data->PacketID = packet->packetID;
}
- }
}
-
// Print a report if appropriate
return reporter_condprintstats( &reporthdr->report, reporthdr->multireport, finished );
}
if ( stats->info.cntError > stats->info.cntOutofOrder ) {
stats->info.cntError -= stats->info.cntOutofOrder;
}
- stats->info.cntDatagrams = (isUDP(stats) ? stats->PacketID : stats->cntDatagrams);
- stats->info.TotalLen = stats->TotalLen;
+ if (isConnectionLess(stats))
+ stats->info.cntDatagrams = stats->PacketID;
+ else
+ stats->info.cntDatagrams = stats->cntDatagrams;
+ stats->info.TotalLen = stats->TotalLen;
stats->info.startTime = 0;
- stats->info.endTime = TimeDifference( stats->packetTime, stats->startTime );
- stats->info.free = 1;
+ stats->info.endTime = TimeDifference( stats->packetTime, stats->startTime );
+ stats->info.free = 1;
reporter_print( stats, TRANSFER_REPORT, force );
if ( isMultipleReport(stats) ) {
reporter_handle_multiple_reports( multireport, &stats->info, force );
stats->info.cntError -= stats->info.cntOutofOrder;
}
stats->lastError = stats->cntError;
- stats->info.cntDatagrams = (isUDP( stats ) ? stats->PacketID - stats->lastDatagrams :
- stats->cntDatagrams - stats->lastDatagrams);
- stats->lastDatagrams = (isUDP( stats ) ? stats->PacketID : stats->cntDatagrams);
+ if (isConnectionLess(stats)) {
+ stats->info.cntDatagrams = stats->PacketID - stats->lastDatagrams;
+ stats->lastDatagrams = stats->PacketID;
+ } else {
+ stats->info.cntDatagrams = stats->cntDatagrams - stats->lastDatagrams;
+ stats->lastDatagrams = stats->cntDatagrams;
+ }
stats->info.TotalLen = stats->TotalLen - stats->lastTotal;
stats->lastTotal = stats->TotalLen;
stats->info.startTime = stats->info.endTime;
switch ( type ) {
case TRANSFER_REPORT:
statistics_reports[stats->mode]( &stats->info );
- if ( end != 0 && isPrintMSS( stats ) && !isUDP( stats ) ) {
+ if ( end != 0 && isPrintMSS( stats ) && !isConnectionLess( stats ) ) {
PrintMSS( stats );
}
break;
/* -------------------------------------------------------------------
* Report the MSS and MTU, given the MSS (or a guess thereof)
+ * This works for connection-oriented protocols only: it expects
+ * the protocol to be either TCP or DCCP and will give error otherwise
* ------------------------------------------------------------------- */
// compare the MSS against the (MTU - 40) to (MTU - 80) bytes.
#define checkMSS_MTU( inMSS, inMTU ) (inMTU-40) >= inMSS && inMSS >= (inMTU-80)
-void PrintMSS( ReporterData *stats ) {
- int inMSS = getsock_tcp_mss( stats->info.transferID );
+void PrintMSS( ReporterData *stats )
+{
+ int inMSS = stats->mProtocol == kProto_TCP
+ ? getsock_tcp_mss( stats->info.transferID )
+ : getsock_dccp_mps( stats->info.transferID );
if ( inMSS <= 0 ) {
printf( report_mss_unsupported, stats->info.transferID );
}
/* -------------------------------------------------------------------
- * Receieve data from the (connected) TCP/UDP socket.
+ * Receive data from the (connected) socket.
* Sends termination flag several times at the end.
* Does not close the socket.
* ------------------------------------------------------------------- */
void Server::Run( void ) {
long currLen;
- struct UDP_datagram* mBuf_UDP = (struct UDP_datagram*) mBuf;
+ struct dgram_record* dgram_rec = (struct dgram_record*) mBuf;
ReportStruct *reportstruct = NULL;
// perform read
currLen = recv( mSettings->mSock, mBuf, mSettings->mBufLen, 0 );
- if ( isUDP( mSettings ) ) {
+ if ( isPacketOriented( mSettings ) ) {
// read the datagram ID and sentTime out of the buffer
- reportstruct->packetID = ntohl( mBuf_UDP->id );
- reportstruct->sentTime.tv_sec = ntohl( mBuf_UDP->tv_sec );
- reportstruct->sentTime.tv_usec = ntohl( mBuf_UDP->tv_usec );
+ reportstruct->packetID = ntohl( dgram_rec->id );
+ reportstruct->sentTime.tv_sec = ntohl( dgram_rec->tv_sec );
+ reportstruct->sentTime.tv_usec = ntohl( dgram_rec->tv_usec );
}
reportstruct->packetLen = currLen;
// the datagram ID should be correct, just negated
if ( reportstruct->packetID < 0 ) {
reportstruct->packetID = -reportstruct->packetID;
+ // Don't count the FIN message in connection-oriented protocols.
+ if (!isConnectionLess(mSettings))
+ break;
currLen = -1;
}
ReportPacket( mSettings->reporthdr, reportstruct );
gettimeofday( &(reportstruct->packetTime), NULL );
CloseReport( mSettings->reporthdr, reportstruct );
+ // send back an acknowledgement of the terminating datagram
// send a acknowledgement back only if we're NOT receiving multicast
- if ( isUDP( mSettings ) && !isMulticast( mSettings ) ) {
- // send back an acknowledgement of the terminating datagram
- write_UDP_AckFIN( );
- }
+ if ( isPacketOriented( mSettings ) && !isMulticast( mSettings ) )
+ write_dgram_AckFin( );
} else {
FAIL(1, "Out of memory! Closing server thread\n", mSettings);
}
* termination datagrams, so re-transmit our AckFIN.
* ------------------------------------------------------------------- */
-void Server::write_UDP_AckFIN( ) {
+void Server::write_dgram_AckFin( ) {
int rc;
while ( count < 10 ) {
count++;
- UDP_datagram *UDP_Hdr;
+ dgram_record *dgram_rec;
server_hdr *hdr;
- UDP_Hdr = (UDP_datagram*) mBuf;
+ dgram_rec = (dgram_record*) mBuf;
- if ( mSettings->mBufLen > (int) ( sizeof( UDP_datagram )
+ if ( mSettings->mBufLen > (int) ( sizeof( dgram_record )
+ sizeof( server_hdr ) ) ) {
Transfer_Info *stats = GetReport( mSettings->reporthdr );
- hdr = (server_hdr*) (UDP_Hdr+1);
+ hdr = (server_hdr*) (dgram_rec+1);
hdr->flags = htonl( HEADER_VERSION1 );
hdr->total_len1 = htonl( (long) (stats->TotalLen >> 32) );
fprintf( stderr, warn_ack_failed, mSettings->mSock, count );
}
-// end write_UDP_AckFIN
-
const struct option long_options[] =
{
{"singleclient", no_argument, NULL, '1'},
+{"dualtest", no_argument, NULL, '2'},
{"bandwidth", required_argument, NULL, 'b'},
{"client", required_argument, NULL, 'c'},
-{"dualtest", no_argument, NULL, 'd'},
+{"dccp", no_argument, NULL, 'd'},
{"format", required_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
{"interval", required_argument, NULL, 'i'},
const struct option env_options[] =
{
{"IPERF_SINGLECLIENT", no_argument, NULL, '1'},
+{"IPERF_DUALTEST", no_argument, NULL, '2'},
{"IPERF_BANDWIDTH", required_argument, NULL, 'b'},
{"IPERF_CLIENT", required_argument, NULL, 'c'},
-{"IPERF_DUALTEST", no_argument, NULL, 'd'},
+{"IPERF_DCCP", no_argument, NULL, 'd'},
{"IPERF_FORMAT", required_argument, NULL, 'f'},
// skip help
{"IPERF_INTERVAL", required_argument, NULL, 'i'},
#define SHORT_OPTIONS()
-const char short_options[] = "1b:c:df:hi:l:mn:o:p:rst:uvw:x:y:B:CDF:IL:M:NP:RS:T:UVW";
+const char short_options[] =
+ "12b::c:df:hi:l:mn:o:p:rst:uvw:x:y:B:CDF:IL:M:NP:RS:T:UVW";
/* -------------------------------------------------------------------
* defaults
* ------------------------------------------------------------------- */
#define DEFAULTS()
-const long kDefault_UDPRate = 1024 * 1024; // -u if set, 1 Mbit/sec
-const int kDefault_UDPBufLen = 1470; // -u if set, read/write 1470 bytes
+const long kDefault_DgramRate = 1024 * 1024; // -u if set, 1 Mbit/sec
+const int kDefault_UDPBufLen = 1470; // -u if set, read/write 1470 bytes
// 1470 bytes is small enough to be sending one packet per datagram on ethernet
// 1450 bytes is small enough to be sending one packet per datagram on ethernet
// **** with IPv6 ****
+const int kDefault_DCCPBufLen = 1420; // -d
+// old DCCPv4: MPS=1424; new DCCPv4: MPS=1440; new DCCPv6: MPS=1420 (above)
/* -------------------------------------------------------------------
* Initialize all settings to defaults.
// this memset. Only need to set non-zero values
// below.
memset( main, 0, sizeof(thread_Settings) );
- main->mSock = INVALID_SOCKET;
- main->mReportMode = kReport_Default;
+ main->mSock = INVALID_SOCKET;
+ main->mReportMode = kReport_Default;
// option, defaults
main->flags = FLAG_MODETIME | FLAG_STDOUT; // Default time and stdout
- //main->mUDPRate = 0; // -b, ie. TCP mode
- //main->mHost = NULL; // -c, none, required for client
- main->mMode = kTest_Normal; // -d, mMode == kTest_DualTest
+ //main->mDgramRate = 0; // -b, ie. TCP mode
+ main->mProtocol = kProto_TCP; // -u / -d
+ //main->mHost = NULL; // -c, none, required for client
+ main->mMode = kTest_Normal; // -2, mMode == kTest_DualTest
main->mFormat = 'a'; // -f, adaptive bits
// skip help // -h,
//main->mBufLenSet = false; // -l,
main->mBufLen = 8 * 1024; // -l, 8 Kbyte
- //main->mInterval = 0; // -i, ie. no periodic bw reports
+ //main->mInterval = 0; // -i, ie. no periodic bw reports
//main->mPrintMSS = false; // -m, don't print MSS
// mAmount is time also // -n, N/A
//main->mOutputFileName = NULL; // -o, filename
main->mPort = 5001; // -p, ttcp port
- // mMode = kTest_Normal; // -r, mMode == kTest_TradeOff
+ // mMode = kTest_Normal; // -r, mMode == kTest_TradeOff
main->mThreadMode = kMode_Unknown; // -s, or -c, none
main->mAmount = 1000; // -t, 10 seconds
- // mUDPRate > 0 means UDP // -u, N/A, see kDefault_UDPRate
+ // mDgramRate // -u, N/A, see kDefault_DgramRate
// skip version // -v,
- //main->mTCPWin = 0; // -w, ie. don't set window
+ //main->mWinSize = 0; // -w, ie. don't set window
// more esoteric options
- //main->mLocalhost = NULL; // -B, none
+ //main->mLocalhost = NULL; // -B, bind address - none
//main->mCompat = false; // -C, run in Compatibility mode
//main->mDaemon = false; // -D, run as a daemon
//main->mFileInput = false; // -F,
- //main->mFileName = NULL; // -F, filename
+ //main->mFileName = NULL; // -F, filename
//main->mStdin = false; // -I, default not stdin
- //main->mListenPort = 0; // -L, listen port
- //main->mMSS = 0; // -M, ie. don't set MSS
+ //main->mListenPort = 0; // -L, listen port
+ //main->mMSS = 0; // -M, ie. don't set MSS
//main->mNodelay = false; // -N, don't set nodelay
- //main->mThreads = 0; // -P,
+ //main->mThreads = 0; // -P,
//main->mRemoveService = false; // -R,
- //main->mTOS = 0; // -S, ie. don't set type of service
+ //main->mTOS = 0; // -S, ie. don't set type of service
main->mTTL = 1; // -T, link-local TTL
//main->mDomain = kMode_IPv4; // -V,
//main->mSuggestWin = false; // -W, Suggest the window size.
case '1': // Single Client
setSingleClient( mExtSettings );
break;
- case 'b': // UDP bandwidth
- if ( !isUDP( mExtSettings ) ) {
- fprintf( stderr, warn_implied_udp, option );
- }
+ case '2': // Dual-test Mode
if ( mExtSettings->mThreadMode != kMode_Client ) {
fprintf( stderr, warn_invalid_server_option, option );
break;
}
+ if ( isCompat( mExtSettings ) ) {
+ fprintf( stderr, warn_invalid_compatibility_option, option );
+ }
+#ifdef HAVE_THREAD
+ mExtSettings->mMode = kTest_DualTest;
+#else
+ fprintf( stderr, warn_invalid_single_threaded, option );
+ mExtSettings->mMode = kTest_TradeOff;
+#endif
+ break;
- Settings_GetLowerCaseArg(optarg,outarg);
- mExtSettings->mUDPRate = byte_atoi(outarg);
- setUDP( mExtSettings );
+ case 'b':
+ // This sets packet-oriented mode. The optional
+ // argument sets datagram bandwidth (as before).
+ // If not given, a default bandwith is used.
+ setPacketOriented(mExtSettings);
- // if -l has already been processed, mBufLenSet is true
- // so don't overwrite that value.
- if ( !isBuflenSet( mExtSettings ) ) {
- mExtSettings->mBufLen = kDefault_UDPBufLen;
- }
+ if (optarg) {
+ Settings_GetLowerCaseArg(optarg, outarg);
+ mExtSettings->mDgramRate = byte_atoi(outarg);
+ } else
+ mExtSettings->mDgramRate = kDefault_DgramRate;
break;
case 'c': // client mode w/ server host to connect to
}
break;
- case 'd': // Dual-test Mode
- if ( mExtSettings->mThreadMode != kMode_Client ) {
- fprintf( stderr, warn_invalid_server_option, option );
- break;
- }
- if ( isCompat( mExtSettings ) ) {
- fprintf( stderr, warn_invalid_compatibility_option, option );
- }
-#ifdef HAVE_THREAD
- mExtSettings->mMode = kTest_DualTest;
-#else
- fprintf( stderr, warn_invalid_single_threaded, option );
- mExtSettings->mMode = kTest_TradeOff;
-#endif
+ case 'd': // DCCP as transport
+ mExtSettings->mProtocol = kProto_DCCP;
+
+ // if -l has already been processed, mBufLenSet is true
+ // so don't overwrite that value.
+ if ( !isBuflenSet( mExtSettings ) )
+ mExtSettings->mBufLen = kDefault_DCCPBufLen;
+
break;
case 'f': // format to print in
break;
case 'h': // print help and exit
- fprintf( stderr, usage_long );
- exit(1);
- break;
+ die(usage_long);
case 'i': // specify interval between periodic bw reports
mExtSettings->mInterval = atof( optarg );
Settings_GetUpperCaseArg(optarg,outarg);
mExtSettings->mBufLen = byte_atoi( outarg );
setBuflenSet( mExtSettings );
- if ( !isUDP( mExtSettings ) ) {
+
+ if ( !isPacketOriented( mExtSettings ) ) {
if ( mExtSettings->mBufLen < (int) sizeof( client_hdr ) &&
!isCompat( mExtSettings ) ) {
setCompat( mExtSettings );
fprintf( stderr, warn_implied_compatibility, option );
}
} else {
- if ( mExtSettings->mBufLen < (int) sizeof( UDP_datagram ) ) {
- mExtSettings->mBufLen = sizeof( UDP_datagram );
+ if ( mExtSettings->mBufLen < (int) sizeof( dgram_record ) ) {
+ mExtSettings->mBufLen = sizeof( dgram_record );
fprintf( stderr, warn_buffer_too_small, mExtSettings->mBufLen );
}
if ( !isCompat( mExtSettings ) &&
- mExtSettings->mBufLen < (int) ( sizeof( UDP_datagram )
+ mExtSettings->mBufLen < (int) ( sizeof( dgram_record )
+ sizeof( client_hdr ) ) ) {
setCompat( mExtSettings );
fprintf( stderr, warn_implied_compatibility, option );
break;
case 'u': // UDP instead of TCP
+ mExtSettings->mProtocol = kProto_UDP;
+
+ setPacketOriented(mExtSettings);
// if -b has already been processed, UDP rate will
// already be non-zero, so don't overwrite that value
- if ( !isUDP( mExtSettings ) ) {
- setUDP( mExtSettings );
- mExtSettings->mUDPRate = kDefault_UDPRate;
- }
+ if ( mExtSettings->mDgramRate == 0 )
+ mExtSettings->mDgramRate = kDefault_DgramRate;
// if -l has already been processed, mBufLenSet is true
// so don't overwrite that value.
if ( !isBuflenSet( mExtSettings ) ) {
mExtSettings->mBufLen = kDefault_UDPBufLen;
- } else if ( mExtSettings->mBufLen < (int) ( sizeof( UDP_datagram )
+ } else if ( mExtSettings->mBufLen < (int) ( sizeof( dgram_record )
+ sizeof( client_hdr ) ) &&
!isCompat( mExtSettings ) ) {
setCompat( mExtSettings );
break;
case 'v': // print version and exit
- fprintf( stderr, version );
- exit(1);
- break;
+ die(version);
- case 'w': // TCP window size (socket buffer size)
+ case 'w': // TCP window size or socket send-buffer size (UDP/DCCP)
Settings_GetUpperCaseArg(optarg,outarg);
- mExtSettings->mTCPWin = byte_atoi(outarg);
-
- if ( mExtSettings->mTCPWin < 2048 ) {
- fprintf( stderr, warn_window_small, mExtSettings->mTCPWin );
- }
+ mExtSettings->mWinSize = byte_atoi(outarg);
break;
case 'x': // Limit Reports
*client = new thread_Settings;
memcpy(*client, server, sizeof( thread_Settings ));
setCompat( (*client) );
- (*client)->mTID = thread_zeroid();
+ (*client)->mTID = thread_zeroid();
(*client)->mPort = (unsigned short) ntohl(hdr->mPort);
(*client)->mThreads = ntohl(hdr->numThreads);
if ( hdr->bufferlen != 0 ) {
(*client)->mBufLen = ntohl(hdr->bufferlen);
}
if ( hdr->mWinBand != 0 ) {
- if ( isUDP( server ) ) {
- (*client)->mUDPRate = ntohl(hdr->mWinBand);
+ if ( isPacketOriented( server ) ) {
+ (*client)->mDgramRate = ntohl(hdr->mWinBand);
} else {
- (*client)->mTCPWin = ntohl(hdr->mWinBand);
+ (*client)->mWinSize = ntohl(hdr->mWinBand);
}
}
(*client)->mAmount = ntohl(hdr->mAmount);
} else {
hdr->bufferlen = 0;
}
- if ( isUDP( client ) ) {
- hdr->mWinBand = htonl(client->mUDPRate);
+ if ( isPacketOriented( client ) ) {
+ hdr->mWinBand = htonl(client->mDgramRate);
} else {
- hdr->mWinBand = htonl(client->mTCPWin);
+ hdr->mWinBand = htonl(client->mWinSize);
}
if ( client->mListenPort != 0 ) {
hdr->mPort = htonl(client->mListenPort);
// read settings from command-line parameters
Settings_ParseCommandLine( argc, argv, ext_gSettings );
+ if (isPacketOriented(ext_gSettings) &&
+ !(ext_gSettings->mProtocol == kProto_UDP ||
+ ext_gSettings->mProtocol == kProto_DCCP ))
+ die("Can't use packet-oriented mode with these settings.");
+
+ if (isSingleUDP(ext_gSettings) && ext_gSettings->mProtocol != kProto_UDP) {
+ fprintf(stderr, "WARNING: option -U applies to UDP only, ignored!\n");
+ unsetSingleUDP(ext_gSettings);
+ }
+
+ if (!isModeTime(ext_gSettings) &&
+ (isConnectionLess(ext_gSettings) || isPacketOriented(ext_gSettings)))
+ die("Amount-oriented (-n) works only in non-packet-oriented mode.");
+
// Check for either having specified client or server
if ( ext_gSettings->mThreadMode == kMode_Client
|| ext_gSettings->mThreadMode == kMode_Listener ) {
return theMSS;
} /* end getsock_tcp_mss */
+/* -------------------------------------------------------------------
+ * returns the DCCP maximum datagram (payload) size
+ * ------------------------------------------------------------------- */
+int getsock_dccp_mps(int inSock)
+{
+ int rc, theMPS = 0;
+ Socklen_t len = sizeof(theMPS);
+
+ assert(inSock >= 0);
+
+ rc = getsockopt(inSock, SOL_DCCP, DCCP_SOCKOPT_GET_CUR_MPS, &theMPS, &len);
+ WARN_errno(rc == SOCKET_ERROR, "getsockopt DCCP_SOCKOPT_GET_CUR_MPS");
+
+ return theMPS;
+}
+
/* -------------------------------------------------------------------
* Attempts to reads n bytes from a socket.
* Returns number actually read, or -1 on error.
} else if ( inNum < 99.95 ) { /* 99.95 would be rounded to 100 */
format = "%4.1f %s"; /* ##.# */
} else if ( inNum < 999.5 ) { /* 999.5 would be rounded to 1000 */
- format = " %4.0f %s"; /* ### */
+ format = "%4.0f %s"; /* ### */
} else { /* 1000-1024 fits in 4 places
* If not using Adaptive sizes then
* this code will not control spaces*/
extern "C" {
#endif
+/* -------------------------------------------------------------------
+ * Set the socket buffer size in bytes (returns -1 on error, 0 otherwise).
+ * ------------------------------------------------------------------- */
+int set_buffer_sock_size(int inSock, int inWinSize , bool inSend)
+{
+ // note: results are verified after connect() or listen(),
+ // since some OS's don't show the corrected value until then.
+#ifdef SO_SNDBUF
+ if (inWinSize > 0) {
+ return setsockopt(inSock, SOL_SOCKET,
+ inSend ? SO_SNDBUF: SO_RCVBUF,
+ &inWinSize, sizeof(inWinSize));
+ }
+#else
+ fprintf(stderr, "%s: no support for SO_SNDBUF\n", __FUNCTION__);
+#endif
+ return 0;
+}
+
/* -------------------------------------------------------------------
* If inTCPWin > 0, set the TCP window size (via the socket buffer
* sizes) for inSock. Otherwise leave it as the system default.
* This now works on AIX, by enabling RFC1323.
* returns -1 on error, 0 on no error.
* ------------------------------------------------------------------- */
-
-int setsock_tcp_windowsize( int inSock, int inTCPWin, int inSend ) {
-#ifdef SO_SNDBUF
- int rc;
- int newTCPWin;
-
+int setsock_tcp_windowsize(int inSock, int inTCPWin, int inSend)
+{
assert( inSock >= 0 );
- if ( inTCPWin > 0 ) {
-
+ if (inTCPWin > 0) {
+ if (inTCPWin < 2048)
+ fprintf(stderr, "WARNING: A TCP window size of %d bytes is "
+ "considered small and will give poor performance. "
+ "See the Iperf documentation.\n", inTCPWin);
#ifdef TCP_WINSHIFT
-
/* UNICOS requires setting the winshift explicitly */
if ( inTCPWin > 65535 ) {
int winShift = 0;
}
}
#endif /* TCP_RFC1323 */
-
- if ( !inSend ) {
- /* receive buffer -- set
- * note: results are verified after connect() or listen(),
- * since some OS's don't show the corrected value until then. */
- newTCPWin = inTCPWin;
- rc = setsockopt( inSock, SOL_SOCKET, SO_RCVBUF,
- (char*) &newTCPWin, sizeof( newTCPWin ));
- } else {
- /* send buffer -- set
- * note: results are verified after connect() or listen(),
- * since some OS's don't show the corrected value until then. */
- newTCPWin = inTCPWin;
- rc = setsockopt( inSock, SOL_SOCKET, SO_SNDBUF,
- (char*) &newTCPWin, sizeof( newTCPWin ));
- }
- if ( rc < 0 ) {
- return rc;
- }
}
-#endif /* SO_SNDBUF */
-
- return 0;
-} /* end setsock_tcp_windowsize */
+ return set_buffer_sock_size(inSock, inTCPWin, inSend);
+}
/* -------------------------------------------------------------------
- * returns the TCP window size (on the sending buffer, SO_SNDBUF),
+ * returns the send/receive socket buffer size in bytes
* or -1 on error.
* ------------------------------------------------------------------- */
-
-int getsock_tcp_windowsize( int inSock, int inSend ) {
- int theTCPWin = 0;
-
+int get_buffer_sock_size( int inSock, int inSend )
+{
+ int bufSize = 0;
#ifdef SO_SNDBUF
+ Socklen_t len = sizeof(bufSize);
int rc;
- Socklen_t len;
- /* send buffer -- query for buffer size */
- len = sizeof( theTCPWin );
- if ( inSend ) {
+ if ( inSend )
rc = getsockopt( inSock, SOL_SOCKET, SO_SNDBUF,
- (char*) &theTCPWin, &len );
- } else {
+ (char*) &bufSize, &len );
+ else
rc = getsockopt( inSock, SOL_SOCKET, SO_RCVBUF,
- (char*) &theTCPWin, &len );
- }
- if ( rc < 0 ) {
+ (char*) &bufSize, &len );
+ if (rc < 0)
return rc;
- }
-
#endif
-
- return theTCPWin;
-} /* end getsock_tcp_windowsize */
+ return bufSize;
+}
#ifdef __cplusplus
} /* end extern "C" */
#endif
-