Larry McVoy wrote:
A short summary is "can someone please post a test program that sources
and sinks data at the wire speed?"  because apparently I'm too old and
clueless to write such a thing.

Here's a simple reference tcp source/sink that's I've used for years. For example, on a couple gigabit machines:

$ ./tcpsend -t10 dew
Sent 1240415312 bytes in 10.033101 seconds
Throughput: 123632294 B/s

  -John

/*
 * discard.c
 * A simple discard server.
 *
 * Copyright 2003 John Heffner.
 */

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/param.h>
#include <netinet/in.h>

#if 0
#define RATELIMIT
#define RATE            100000  /* bytes/sec */
#define WAIT_TIME       (1000000/HZ-1)
#define READ_SIZE       (RATE/HZ)
#else
#define READ_SIZE       (1024*1024)
#endif

void child_handler(int sig)
{
        int status;
        
        wait(&status);
}

int main(int argc, char *argv[])
{
        int port = 9000;
        int lfd;
        struct sockaddr_in laddr;
        int newfd;
        struct sockaddr_in newaddr;
        int pid;
        socklen_t len;
        
        if (argc > 2) {
                fprintf(stderr, "usage: discard [port]\n");
                exit(1);
        }
        if (argc == 2) {
                if (sscanf(argv[1], "%d", &port) != 1 || port < 0 || port > 
65535) {
                        fprintf(stderr, "discard: error: not a port number\n");
                        exit(1);
                }
        }
        
        if (signal(SIGCHLD, child_handler) == SIG_ERR) {
                perror("signal");
                exit(1);
        }
        
        memset(&laddr, 0, sizeof (laddr));
        laddr.sin_family = AF_INET;
        laddr.sin_port = htons(port);
        laddr.sin_addr.s_addr = INADDR_ANY;
        
        if ((lfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
                perror("socket");
                exit(1);
        }
        if (bind(lfd, (struct sockaddr *)&laddr, sizeof (laddr)) != 0) {
                perror("bind");
                exit(1);
        }
        if (listen(lfd, 5) != 0) {
                perror("listen");
                exit(1);
        }
        
        for (;;) {
                if ((newfd = accept(lfd, (struct sockaddr *)&newaddr, &len)) < 
0) {
                        if (errno == EINTR)
                                continue;
                        perror("accept");
                        exit(1);
                }
                
                if ((pid = fork()) < 0) {
                        perror("fork");
                        exit(1);
                } else if (pid == 0) {
                        int n;
                        char buf[READ_SIZE];
                        int64_t data_rcvd = 0;
                        struct timeval stime, etime;
                        float time;
                        
                        gettimeofday(&stime, NULL);
                        while ((n = read(newfd, buf, READ_SIZE)) > 0) {
                                data_rcvd += n;
#ifdef RATELIMIT
                                usleep(WAIT_TIME);
#endif
                        }
                        gettimeofday(&etime, NULL);
                        close(newfd);
                        
                        time = (float)(1000000*(etime.tv_sec - stime.tv_sec) + 
etime.tv_usec - stime.tv_usec) / 1000000.0;
                        printf("Received %lld bytes in %f seconds\n", (long 
long)data_rcvd, time);
                        printf("Throughput: %d B/s\n", (int)((float)data_rcvd / 
time));
                        
                        exit(0);
                }
                
                close(newfd);
        }
        
        return 1;
}
/*
 * tcpsend.c
 * Send pseudo-random data through a TCP connection.
 *
 * Copyright 2003 John Heffner.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/stat.h>
#ifdef __linux__
#include <sys/sendfile.h>
#endif

#define SNDSIZE (1024 * 10)
#define BUFSIZE (1024 * 1024)

#define max(a,b)        (a > b ? a : b)
#define min(a,b)        (a < b ? a : b)

int time_done = 0;
int interrupt_done = 0;

struct timeval starttime;

void int_handler(int sig)
{
        interrupt_done = 1;
}

void alarm_handler(int sig)
{
        time_done = 1;
}

static void usage_error(int err) {
        fprintf(stderr, "usage: tcpsend [-z] [-b max_bytes] [-t max_time] 
hostname [port]\n");
        exit(err);
}

static void cleanup_exit(int fd, char *filename, int status)
{
        if (fd > 0)
                close(fd);
        if (filename)
                unlink(filename);
        exit(status);
}

int main(int argc, char *argv[])
{
        char *hostname = "localhost";
        int port = 9000;
        int max_time = -1;
        int max_bytes = -1;
        int zerocopy = 0;
        
        int sockfd;
        struct sockaddr_in addr;
        struct hostent *hent;
        struct sigaction act;
        int i;
        int arg_state;
        char *tmp;
        int add;
        char *buf;
        int64_t data_sent;
        int n;
        off_t start;
        int amt;
        struct timeval etime;
        float time;
        int err;
        char *namebuf = NULL;
        int fd = -1;
        
        /* Read in args */
        if (argc == 2 && strcmp(argv[1], "-h") == 0)
                usage_error(0);
        
        for (arg_state = 0, i = 1; i < argc; i++) {
                if (argv[i][0] == '-') {
                        if (arg_state != 0)
                                usage_error(1);
                        if (strlen(argv[i]) < 2)
                                usage_error(1);
                        
                        add = 0;
                        if (argv[i][1] == 'z') {
                                zerocopy = 1;
                        } else if (argv[i][1] == 'b' ||
                                   argv[i][1] == 't') {
                                if (strlen(argv[i]) > 2) {
                                        tmp = &(argv[i][2]);
                                } else {
                                        add = 1;
                                        if (i + 1 >= argc)
                                                usage_error(1);
                                        tmp = argv[i + 1];
                                }
                                
                                if (argv[i][1] == 'b') {
                                        if (sscanf(tmp, "%d", &max_bytes) != 1 
||
                                            max_bytes < 0)
                                                usage_error(1);
                                } else {
                                        if (sscanf(tmp, "%d", &max_time) != 1 ||
                                            max_time < 0)
                                                usage_error(1);
                                }
                        } else {
                                usage_error(1);
                        }
                        
                        i += add;
                } else {
                        switch (arg_state) {
                        case 0:
                                arg_state = 1;
                                hostname = argv[i];
                                break;
                        case 1:
                                arg_state = 2;
                                if (sscanf(argv[i], "%d", &port) != 1 ||
                                    port < 0 || port > 65535)
                                        usage_error(1);
                                break;
                        default:
                                usage_error(1);
                        }
                }
        }
        if (arg_state < 1)
                usage_error(1);
        
#ifndef __linux__
        if (zerocopy) {
                fprintf(stderr, "Zero-copy is only supported under Linux.\n");
                exit(1);
        }
#endif
        
        /* Set up addr struct from hostname and port */
        if ((hent = gethostbyname(hostname)) == NULL) {
                fprintf(stderr, "tcpsend: gethostbyname error\n");
                exit(1);
        }
        memset(&addr, 0, sizeof (addr));
        addr.sin_family = AF_INET;
        memcpy(&addr.sin_addr, hent->h_addr_list[0], 4);
        addr.sin_port = htons(port);
        
        
        /* Create buffer and fill with random data */
        if (gettimeofday(&starttime, NULL) < 0) {
                perror("gettimeofday");
                exit(1);
        }
        srand((unsigned int)(starttime.tv_usec + 1000000 * starttime.tv_sec));
        if ((buf = (char *)malloc(BUFSIZE)) == NULL) {
                fprintf(stderr, "malloc failed\n");
                exit(1);
        }
        for (i = 0; i < BUFSIZE; i += sizeof (int)) {
                *(int *)&buf[i] = rand();
        }
        if (zerocopy) {
                if ((namebuf = malloc(64)) == NULL) {
                        fprintf(stderr, "malloc failed\n");
                        exit(1);
                }
                sprintf(namebuf, "/tmp/tcpsend%d", getpid());
                if ((fd = open(namebuf, O_RDWR | O_CREAT, 0600)) < 0) {
                        perror("open");
                        exit(1);
                }
                for (amt = BUFSIZE; amt > 0; ) {
                        if ((n = write(fd, buf, amt)) < 0) {
                                perror("write");
                                cleanup_exit(fd, namebuf, 1);
                        }
                        amt -= n;
                }
        }
        
        
        /* Open connection */
        if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
                perror("socket");
                cleanup_exit(fd, namebuf, 1);
        }
        if (connect(sockfd, (struct sockaddr *)&addr, sizeof (addr)) != 0) {
                perror("connect");
                cleanup_exit(fd, namebuf, 1);
        }
        
        
        /* Set up signal handlers */
        if (max_time >= 0) {
                if (sigaction(SIGALRM, NULL, &act) != 0) {
                        perror("sigaction: SIGALRM");
                        cleanup_exit(fd, namebuf, 1);
                }
                act.sa_handler = alarm_handler;
                act.sa_flags = 0;
                if (sigaction(SIGALRM, &act, NULL) != 0) {
                        perror("sigaction: SIGALRM");
                        cleanup_exit(fd, namebuf, 1);
                }
                alarm(max_time);
        }
        if (sigaction(SIGINT, NULL, &act) != 0) {
                perror("sigaction: SIGINT");
                cleanup_exit(fd, namebuf, 1);
        }
        act.sa_handler = int_handler;
        act.sa_flags = 0;
        if (sigaction(SIGINT, &act, NULL) != 0) {
                perror("sigaction: SIGINT");
                cleanup_exit(fd, namebuf, 1);
        }
        
        
        /* Send random data until we hit a max */
        data_sent = 0;
        while ((max_bytes < 0 ? 1 : data_sent < max_bytes) &&
               !time_done && !interrupt_done) {
                start = rand() / (RAND_MAX / (BUFSIZE - SNDSIZE) + 1);
                if (max_bytes < 0)
                        amt = SNDSIZE;
                else
                        amt = min(SNDSIZE, max_bytes - data_sent);
                if (zerocopy) {
#ifdef __linux__                        
                        if ((n = sendfile(sockfd, fd, &start, amt)) < 0 && 
errno != EINTR) {
                                perror("sendfile");
                                cleanup_exit(fd, namebuf, 1);
                        } else if (n == 0) {
                                fprintf(stderr, "tcpsend: socket unexpectedly 
closed\n");
                                cleanup_exit(fd, namebuf, 1);
                        }
#endif
                } else {
                        if ((n = write(sockfd, &buf[start], amt)) < 0 && errno 
!= EINTR) {
                                perror("write");
                                cleanup_exit(fd, namebuf, 1);
                        } else if (n == 0) {
                                fprintf(stderr, "tcpsend: socket unexpectedly 
closed\n");
                                cleanup_exit(fd, namebuf, 1);
                        }
                }
                
                data_sent += n;
        }
        
        /* Close the socket and wait for the remote host to close */
        if (shutdown(sockfd, SHUT_WR) != 0) {
                perror("shutdown");
                cleanup_exit(fd, namebuf, 1);
        }
        err = read(sockfd, buf, 1);
        if (err < 0) {
                perror("read");
                cleanup_exit(fd, namebuf, 1);
        } else if (err > 0) {
                fprintf(stderr, "warning: data read on socket\n");
        }
        
        gettimeofday(&etime, NULL);
        time = (float)(1000000*(etime.tv_sec - starttime.tv_sec) +
               etime.tv_usec - starttime.tv_usec) / 1000000.0;
        printf("Sent %lld bytes in %f seconds\n", (long long)data_sent, time);
        printf("Throughput: %d B/s\n", (int)((float)data_sent / time));
        
        cleanup_exit(fd, namebuf, 0);
        return 0;
}
CFLAGS = -g -O2 -Wall

all: tcpsend discard

clean:
        rm -f tcpsend discard

Reply via email to