/* * Copyright (c) 2009-2015 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * Usage for frame_delay * * Server * ./frame_delay -m server -t -p -n -f * * Client * ./frame_delay -m client -t -i -p -n -f -d -k */ /* * TODO list : * 1. UDP fragmentation and reassembly */ #include #include #include #include #include #include #include #include #include #include #include /* Server Static variable */ static int so, srv_so; static int srv_port = 0; static struct sockaddr_in laddr, dst_addr; /* Client Static variable */ static struct sockaddr_in srv_addr; static uint32_t tc = 0; /* Usage */ void ErrorUsage(void); /* str2svc */ uint32_t str2svc(const char *str); /* Show Stastics */ void ShowStastics(int64_t *DiffsBuf, int num_frames); /* Returns difference between two timevals in microseconds */ int64_t time_diff(struct timeval *b, struct timeval *a); /* tcp server */ void tcpServer(int frame_size, int num_frames, char *buf, int64_t *DiffsBuf); /* udp server */ void udpServer(int frame_size, int num_frames, char *buf, int64_t *DiffsBuf); /* tcp server */ void tcpClient(int num_frames, int frame_size, const char *buf, struct timespec sleep_time); /* udp server */ void udpClient(int num_frames, int frame_size, const char *buf, struct timespec sleep_time); /* Main function */ int main(int argc, char *argv[]) { int num_frames = 0, frame_size = 0, delay_ms = 0, rc = 0; char *buf = NULL, ch, *type = NULL, *mode = NULL, *ip_addr = NULL; int64_t *DiffsBuf; struct timespec sleep_time; while ((ch = getopt(argc, argv, "m:p:f:n:t:d:i:k:")) != -1) { switch (ch) { case 'm': { mode = optarg; break; } case 'p': { srv_port = atoi(optarg); break; } case 'f' : { frame_size = atoi(optarg); break; } case 'n' : { num_frames = atoi(optarg); break; } case 'i': { ip_addr = optarg; bzero(&srv_addr, sizeof(srv_addr)); rc = inet_aton(optarg, &srv_addr.sin_addr); if (rc == 0) { perror("inet_ntoa failed"); exit(1); } } case 'd': { delay_ms = atoi(optarg); break; } case 't' : { type = optarg; break; } case 'k': { tc = str2svc(optarg); break; } default: { printf("Invalid option: %c\n", ch); ErrorUsage(); } } } /* General check for both server and client */ if (srv_port <= 0 || frame_size <= 0 || num_frames <= 0 || !mode || !type) { ErrorUsage(); } if ( strcmp(type, "tcp") != 0 && strcmp(type, "udp") != 0 ) { ErrorUsage(); } /* Allocate memory for buf */ buf = calloc(1, frame_size); if (buf == NULL) { printf("malloc failed\n"); exit(1); } if ( strcmp(mode, "server") == 0 ) { /* Server */ printf(" : Start %s server on port %d with expected frame size of %d\n", type, srv_port, frame_size); DiffsBuf = (int64_t *)calloc(num_frames, sizeof(int64_t)); if (DiffsBuf == NULL) { printf("malloc failed\n"); exit(1); } if( strcmp(type, "tcp") == 0) { /* tcpServer */ tcpServer(frame_size, num_frames, buf, DiffsBuf); } else { /* updServer */ udpServer(frame_size, num_frames, buf, DiffsBuf); } } else if ( strcmp(mode, "client") == 0 ){ if ( !ip_addr || (tc > 0 && (tc < SO_TC_BK_SYS || tc > SO_TC_CTL)) ){ ErrorUsage(); } /* Client */ printf(" : Start sending %d %s frames to %s:%d with a frame size of %d\n", num_frames, type, ip_addr, srv_port, frame_size); /* Resolving sleep time bug : delay_ms should just be calculated once */ bzero(&sleep_time, sizeof(sleep_time)); while (delay_ms >= 1000) { sleep_time.tv_sec++; delay_ms -= 1000; } sleep_time.tv_nsec = delay_ms * 1000 * 1000; if( strcmp(type, "tcp") == 0) { /* Call TCP client */ tcpClient(num_frames, frame_size, buf, sleep_time); } else { /* Call UDP client */ udpClient(num_frames, frame_size, buf, sleep_time); } } else { ErrorUsage(); } } /* Error usage */ void ErrorUsage(void) { printf("Correct Usage"); printf("Server : frame_delay -m server -t -p -n -f \n"); printf("Client : frame_delay -m client -t -i -p -n -f -d -k \n"); exit(1); } /* str2svc */ uint32_t str2svc(const char *str) { uint32_t svc; char *endptr; if (str == NULL || *str == '\0') svc = UINT32_MAX; else if (strcasecmp(str, "BK_SYS") == 0) return SO_TC_BK_SYS; else if (strcasecmp(str, "BK") == 0) return SO_TC_BK; else if (strcasecmp(str, "BE") == 0) return SO_TC_BE; else if (strcasecmp(str, "RD") == 0) return SO_TC_RD; else if (strcasecmp(str, "OAM") == 0) return SO_TC_OAM; else if (strcasecmp(str, "AV") == 0) return SO_TC_AV; else if (strcasecmp(str, "RV") == 0) return SO_TC_RV; else if (strcasecmp(str, "VI") == 0) return SO_TC_VI; else if (strcasecmp(str, "VO") == 0) return SO_TC_VO; else if (strcasecmp(str, "CTL") == 0) return SO_TC_CTL; else { svc = (uint32_t)strtoul(str, &endptr, 0); if (*endptr != '\0') svc = UINT32_MAX; } return (svc); } /* Show Stastics */ void ShowStastics(int64_t *DiffsBuf, int num_frames) { int i = 0; int64_t sum = 0, mean = 0; /* Mean */ while(i < num_frames) sum += DiffsBuf[i++]; mean = sum / num_frames; printf(" : Mean: %.2f usecs\n", sum / (double)num_frames); /* Popular Standard Deviation */ i = 0; sum = 0; while(i < num_frames) { sum += (DiffsBuf[i]-mean)*(DiffsBuf[i]-mean); i++; } printf(" : Popular Standard Deviation: %.2f usecs\n", sqrt(sum/(double)num_frames)); } /* Returns difference between two timevals in microseconds */ int64_t time_diff(struct timeval *b, struct timeval *a) { int64_t usecs; usecs = (a->tv_sec - b->tv_sec) * 1000 * 1000; usecs += (int64_t)(a->tv_usec - b->tv_usec); return(usecs); } /* Server */ /* tcp server */ void tcpServer(int frame_size, int num_frames, char *buf, int64_t *DiffsBuf) { int rc = 0, i = 0, ignore_count = 0; uint32_t dst_len = 0; struct timeval before, after; ssize_t bytes; int64_t usecs; /* New change from Padama */ uint64_t prev_frame_ts = 0, prev_recv = 0, frame_ts = 0, cur_recv = 0; uint64_t min_variation = 0, max_variation = 0, avg_variation = 0; printf(" : TCP Server\n"); so = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (so == -1) { perror("failed to create socket"); exit(1); } bzero(&laddr, sizeof(laddr)); laddr.sin_family = AF_INET; laddr.sin_port = htons(srv_port); rc = bind(so, (const struct sockaddr *)&laddr, sizeof(laddr)); if (rc != 0) { perror("failed to bind"); exit(1); } rc = listen(so, 10); if (rc != 0) { perror("failed to listen"); exit(1); } srv_so = accept(so, (struct sockaddr *)&dst_addr, &dst_len); if (srv_so == -1) { perror("failed to accept"); exit(1); } while (1) { if ( i == num_frames ) { printf(" : Completed\n"); break; } printf(" : Waiting for receiving\n"); bzero(&before, sizeof(before)); bzero(&after, sizeof(after)); rc = gettimeofday(&before, NULL); if (rc == -1) { perror("gettimeofday failed"); exit(1); } bytes = recv(srv_so, buf, frame_size, MSG_WAITALL); if (bytes == -1) { perror("recv failed"); exit(1); } else if (bytes > 0 && bytes != frame_size) { printf("Client exited\n"); printf("Didn't recv the complete frame, bytes %ld\n", bytes); exit(1); } else if (bytes == 0) { break; } rc = gettimeofday(&after, NULL); if (rc == -1) { perror("gettimeofday failed"); exit(1); } cur_recv = after.tv_sec * 1000 * 1000 + after.tv_usec; memcpy((void *)&frame_ts, buf, sizeof(frame_ts)); if (prev_frame_ts > 0) { int64_t d_variation = 0; d_variation = (int64_t)((cur_recv - prev_recv) - (frame_ts - prev_frame_ts)); /* printf("Frame %u ts %llu d_variation %lld usecs\n", i, frame_ts, d_variation);*/ if (d_variation > 0) { if (min_variation == 0) min_variation = d_variation; else min_variation = ((min_variation <= d_variation) ? min_variation : d_variation); max_variation = ((max_variation >= d_variation) ? max_variation : d_variation); avg_variation += d_variation; } else { ignore_count++; } } prev_recv = cur_recv; prev_frame_ts = frame_ts; ++i; /* Compute the time differenc */ usecs = time_diff(&before, &after); DiffsBuf[i] = usecs; printf(" : Frame %d received after %lld usecs\n", i, usecs); } if (i != ignore_count) avg_variation = avg_variation / (i - ignore_count); else avg_variation = 0; printf(" : Received frames: %u\n", i); printf(" : Ignored frames: %u\n", ignore_count); printf(" : Minimum delay variation: %llu usecs\n", min_variation); printf(" : Maximum delay variation: %llu usecs\n", max_variation); printf(" : Average delay variation: %llu usecs\n", avg_variation); ShowStastics(DiffsBuf, num_frames); } /* udp server */ void udpServer(int frame_size, int num_frames, char *buf, int64_t *DiffsBuf) { int rc = 0, i = 0, ignore_count = 0; uint32_t dst_len = 0; ssize_t bytes; struct timeval before, after; int64_t usecs; /* New change from Padama */ uint64_t prev_frame_ts = 0, prev_recv = 0, frame_ts = 0, cur_recv = 0; uint64_t min_variation = 0, max_variation = 0, avg_variation = 0; printf(" : UDP Server\n"); so = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (so == -1) { perror("failed to create socket"); exit(1); } bzero(&laddr,sizeof(laddr)); laddr.sin_family = AF_INET; laddr.sin_addr.s_addr=htonl(INADDR_ANY); laddr.sin_port=htons(srv_port); rc = bind(so, (struct sockaddr *)&laddr,sizeof(laddr)); if (rc != 0) { perror("failed to bind"); exit(1); } while (1) { if ( i == num_frames ) { printf(" : Completed\n"); break; } printf(" : Waiting for receiving\n"); bzero(&before, sizeof(before)); bzero(&after, sizeof(after)); rc = gettimeofday(&before, NULL); if (rc == -1) { perror("gettimeofday failed"); exit(1); } bytes = recvfrom(so, buf, frame_size, 0, (struct sockaddr *)&dst_addr, &dst_len); if (bytes == -1) { perror("recv failed"); exit(1); } else if (bytes > 0 && bytes != frame_size) { printf("Client exited\n"); printf("Didn't recv the complete frame, bytes %ld\n", bytes); exit(1); } else if (bytes == 0) { break; } rc = gettimeofday(&after, NULL); if (rc == -1) { perror("gettimeofday failed"); exit(1); } cur_recv = after.tv_sec * 1000 * 1000 + after.tv_usec; memcpy((void *)&frame_ts, buf, sizeof(frame_ts)); if (prev_frame_ts > 0) { int64_t d_variation = 0; d_variation = (int64_t)((cur_recv - prev_recv) - (frame_ts - prev_frame_ts)); /* printf("Frame %u ts %llu d_variation %lld usecs\n", i, frame_ts, d_variation);*/ if (d_variation > 0) { if (min_variation == 0) min_variation = d_variation; else min_variation = ((min_variation <= d_variation) ? min_variation : d_variation); max_variation = ((max_variation >= d_variation) ? max_variation : d_variation); avg_variation += d_variation; } else { ignore_count++; } } prev_recv = cur_recv; prev_frame_ts = frame_ts; ++i; /* Compute the time differenc */ usecs = time_diff(&before, &after); DiffsBuf[i] = usecs; printf(" : Frame %d received after %lld usecs\n", i, usecs); } if (i != ignore_count) avg_variation = avg_variation / (i - ignore_count); else avg_variation = 0; printf(" : Received frames: %u\n", i); printf(" : Ignored frames: %u\n", ignore_count); printf(" : Minimum delay variation: %llu usecs\n", min_variation); printf(" : Maximum delay variation: %llu usecs\n", max_variation); printf(" : Average delay variation: %llu usecs\n", avg_variation); ShowStastics(DiffsBuf, num_frames); } /* Client */ void tcpClient(int num_frames, int frame_size, const char *buf, struct timespec sleep_time){ int rc = 0, i = 0; ssize_t bytes; printf(" : TCP Client\n"); so = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (so <= 0) { perror("creating socket failed"); exit(1); } srv_addr.sin_port = htons(srv_port); srv_addr.sin_len = sizeof(srv_addr); srv_addr.sin_family = AF_INET; rc = connect(so, (const struct sockaddr *)&srv_addr, sizeof(srv_addr)); if (rc != 0) { perror("connect failed"); exit(1); } if (tc > 0) { rc = setsockopt(so, SOL_SOCKET, SO_TRAFFIC_CLASS, &tc, sizeof(tc)); if (rc == -1) { perror("failed to set traffic class"); exit(1); } } for (i = 0; i < num_frames; ++i) { struct timeval fts; uint64_t frame_ts; /* Add a timestamp to the frame */ rc = gettimeofday(&fts, NULL); if (rc == -1) { perror("faile to get time of day"); exit(1); } frame_ts = fts.tv_sec * 1000 * 1000 + fts.tv_usec; memcpy((void *)buf, (const void *)&frame_ts, sizeof(frame_ts)); bytes = send(so, buf, frame_size, 0); if (bytes == -1) { perror("send failed \n"); exit(1); } if (bytes != frame_size) { printf("failed to send all bytes, sent %ld\n", bytes); exit (1); } rc = nanosleep(&sleep_time, NULL); if (rc == -1) { perror("sleep failed"); exit(1); } printf(" : Sent %u frames as a whole\n", (i + 1)); } } void udpClient(int num_frames, int frame_size, const char *buf, struct timespec sleep_time){ int rc = 0, i = 0; ssize_t bytes; printf(" : UDP Client\n"); so = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (so <= 0) { perror("creating socket failed"); exit(1); } srv_addr.sin_port = htons(srv_port); srv_addr.sin_len = sizeof(srv_addr); srv_addr.sin_family = AF_INET; if (tc > 0) { rc = setsockopt(so, SOL_SOCKET, SO_TRAFFIC_CLASS, &tc, sizeof(tc)); if (rc == -1) { perror("failed to set traffic class"); exit(1); } } for (i = 0; i < num_frames; ++i) { struct timeval fts; uint64_t frame_ts; /* Add a timestamp to the frame */ rc = gettimeofday(&fts, NULL); if (rc == -1) { perror("faile to get time of day"); exit(1); } frame_ts = fts.tv_sec * 1000 * 1000 + fts.tv_usec; memcpy((void *)buf, (const void *)&frame_ts, sizeof(frame_ts)); bytes = sendto(so, buf, frame_size, 0, (struct sockaddr *)&srv_addr, sizeof(srv_addr)); if (bytes == -1) { perror("send failed \n"); exit(1); } if (bytes != frame_size) { printf("failed to send all bytes, sent %ld\n", bytes); exit (1); } rc = nanosleep(&sleep_time, NULL); if (rc == -1) { perror("sleep failed"); exit(1); } printf(" : Sent %u frames as a whole\n", (i + 1)); } }