自己写的UDP收发包性能测试工具,代码如下:
首先是两个头文件
#ifndef _TEST_HEADER_H #define _TEST_HEADER_H #include <unistd.h> #include <sys/time.h> #include <sys/types.h> #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <stdlib.h> #include <errno.h> #include <malloc.h> #include <memory.h> #include <arpa/inet.h> #include <string.h> #include <sys/stat.h> #include <fcntl.h> #define __USE_GNU #include <sched.h> #include <pthread.h> #include <semaphore.h> //@@sem #define DEBUG 0 #if DEBUG #define debug(...) printf(__VA_ARGS__) #else #define debug(...) #endif #define RECV_BUFF_SIZE 64 //定义环形缓冲区的大小,单位为可存储的报文个数 //#define SEND_BUFF_SIZE 100 //目前没有使用到发送环形缓冲,后期的数据产生模块中可能会使用 #define PORTNUM 10000 //定义socket的发送或监听端口 #define ALLOWIP_ADDR "0.0.0.0" //接收端使用recvfrom时指定的接收地址 #define PRINTINTERVAL 3 //打印线程打印信息的间隔,单位seconds #define WINDOWSIZE 1024 //使用滑动窗口机制时的窗口大小,当报文接收很快时,需要提高此值,否则会引起coredump #define FRAGMAXSIZE 65507 //定义应用层进行分片操作的报文临界点,由于UDP协议长度16bits,不能超过65507 #ifdef MULTICASTTEST #define PEERNUM 2 //Multicast peer node number #endif //@end MULTICASTTEST //TCP defination #define MAX_LISTEN_NUM 5 /*适配曾目前主要用于应用层使用UDP发送大报文时的分片和重组*/ typedef enum{ false, true }bool; //适配层的报文头部 typedef struct { bool isFrag; unsigned int numFrag; unsigned int indexFrag; unsigned int lenFrag; }fragment; //应用层的报文头部登记的信息 #if SENDNODEOTHER||RECVNODEOTHER typedef struct { int flag; //0x0001--正常数据报文 x0010--通信终止请求报文,0x0100--通信终止响应报文 unsigned int packetseq; //报文序号 unsigned int packetrecvlen1; //远端节点登记接收的报文长度,用于接收端正确记录此次报文接收的长度 unsigned int packetrecvlen2; //本地节点登记接收的报文长度,用于发送段正确记录此次报文接收的长度,一般在回送业务中使用 struct timeval sendtime1; //发送端发送的时间戳 struct timeval recvtime1; //接收端接收的时间戳 struct timeval sendtime2; //接收端发送的时间戳 struct timeval recvtime2; //发送端接收的时间戳 fragment fragInfo; }packet; #elif MULTICASTTEST typedef struct { int flag; unsigned int packetseq; struct sockaddr_in srcaddr; unsigned int packetrecvlen; fragment fragInfo; }packet; #else typedef struct { int flag; unsigned int packetseq; unsigned int packetrecvlen; fragment fragInfo; }packet; #endif //单个缓冲区节点的结构 typedef struct loopnode_TAG{ bool isValid; //figure out whether the node has been coped with packet *packetptr; //通常指向报文存放的地址 struct loopnode_TAG *next; //下一个缓冲区节点的地址 }loopnode; //用于统计环形缓冲区信息的结构体 typedef struct { int buffsize; //缓冲区大小 loopnode *wbuffptr; //当前可写指针的地址 loopnode *rbuffptr; //当前可读指针的地址 loopnode *pbuffptr; //cope pointer int packetnum; //当前存放的报文数目,实时更新 }loopbuff; //应用程序的统计全局变量 typedef struct { struct sockaddr_in hostaddr; //登记节点自身的地址 unsigned int packetsize; //登记由命令行输入的发送或接收应用层报文大小 unsigned int sendinterval; //登记发送端由命令行输入的发送应用层报文间隔,涉及usleep和for延迟的实现 unsigned int recvnum; //登记总共接收的应用层报文数目 unsigned int sendnum; //登记总共发送的应用层报文数目 unsigned int misspktnum; //关于丢包的统计变量 unsigned int duplpktnum; //关于duplicate包的统计变量 unsigned long totalrecvlen; //登记关于总共记录下的接收字节数 unsigned long totalrelaylen; //relay total send length #if SENDNODEOTHER unsigned int delaycount; //关于计算往返时延的统计变量 unsigned int delaycumulate; #elif RECVNODEOTHER unsigned long trembcumulate; //关于抖动时间的统计变量 unsigned int trembnum; #endif }recordInfo; #ifdef MULTICASTTEST typedef struct{ struct sockaddr_in clientaddr; unsigned int sendnum; unsigned int recvnum; //unsigned long totalrecvlen; }multiNodeInfo; #endif //@end MULTICASTTEST //TCP function declaration int setSendBuffsize(int socket,unsigned int size); void initpthreadmutex(void); void destorypthreadmutex(void); void initPthreadAttr(pthread_attr_t *attr); int setCPUAffinity(int cpu_id); unsigned long timeval2ul(struct timeval *time); int campareIPaddr(struct sockaddr_in *firstaddr,struct sockaddr_in *secondaddr); void initbuff( loopbuff *buff, int number, int size); int recv2buff(int socket,loopbuff *buff, struct sockaddr_in *clientaddr,int length); void recyclebuff(loopbuff *buff); void slidingWinscheme(int *minseq,int *maxseq,int *curseq,unsigned int array[],recordInfo * recordptr); //unsigned long sendtoAdaption(int socket,void *sendbuff, unsigned long packetsize,int flag,struct sockaddr_in *remoteaddr); unsigned long sendtoAdaption(int socket,void *sendbuff, unsigned long packetsize,int flag,struct sockaddr_in *remoteaddr,unsigned int counts); int recvfromAdaption(int socket,void *recvbuf,unsigned long packetsize,int flag,struct sockaddr_in *clientaddr); //全局变量,注意有些程序未必会使用到全部的变量 pthread_cond_t qready; sem_t loopbuffsem; //@@sem pthread_mutex_t mutex; //对环形缓冲区的缓存报文数目进行加锁同步,loopbuff->packetnum pthread_rwlock_t rwmutex; //slidingWinscheme函数中更新丢失包和重复包使用 pthread_rwlock_t rwmutex1; //send & recv thread 更新全局参数结构体时使用 pthread_rwlock_t rwmutex2; //deal thread 更新全局参数结构体时使用 pthread_attr_t attr; /* void *sendbuf=NULL; //线程发送应用层报文的缓存区地址 char *recvbuf=NULL; //线程接收应用层报文的缓冲区地址 char *fragSendbuff=NULL; //线程发送适配层报文分片的缓存地址 char *fragRecvbuff=NULL; //线程接收适配层报文分片的缓存地址 */ #ifndef TCPTEST unsigned int fragSeq; //线程接收分片处理的参数 int fragNum; //线程接收分片处理的参数 int fragIndex; //线程接收分片处理的参数 int fragFlag; //线程接收分片处理的参数 unsigned long totalrecvFragsize; int fragRecvNum; #endif recordInfo recordvar; #endif
#include "Testheader.h" extern char *sendbuf; extern char *recvbuf; extern char *fragSendbuff; extern char *fragRecvbuff; extern unsigned int fragSeq; extern int fragNum; extern int fragIndex; extern int fragFlag; extern unsigned long totalrecvFragsize; extern recordInfo recordvar; extern int fragRecvNum; extern sem_t loopbuffsem;//@@sem //统一初始化线程同步的互斥锁、条件变量、读写锁等 void initpthreadmutex(void) { pthread_cond_init(&qready, NULL); pthread_mutex_init(&mutex, NULL); pthread_rwlock_init(&rwmutex,NULL); pthread_rwlock_init(&rwmutex1,NULL); pthread_rwlock_init(&rwmutex2,NULL); return; } void destorypthreadmutex(void) { pthread_cond_destroy(&qready); pthread_mutex_destroy(&mutex); pthread_rwlock_destroy(&rwmutex); pthread_rwlock_destroy(&rwmutex1); pthread_rwlock_destroy(&rwmutex2); return; } //主线程调用,设置线程属性为实时抢占式调度,更改线程优先级,供分支线程使用 void initPthreadAttr(pthread_attr_t *attr) { int policy,inher; struct sched_param param; pthread_attr_init(attr); pthread_attr_setinheritsched(attr,PTHREAD_EXPLICIT_SCHED); pthread_attr_setschedpolicy(attr,SCHED_FIFO); //param.__sched_priority=sched_get_priority_max(SCHED_FIFO); param.__sched_priority=90; pthread_attr_setschedparam(attr,¶m); return; } //分支线程调用,设置本线程对应CPU的亲和力 int setCPUAffinity(int cpu_id) { if(sysconf(_SC_NPROCESSORS_CONF)<1) { perror("sysconf error, no cpu exists!"); return -1; } cpu_set_t mask; CPU_ZERO(&mask); CPU_SET(cpu_id,&mask); if(-1==sched_setaffinity(0,sizeof(mask),&mask)) { perror("sched_setaffinity fails!"); return -1; } return 0; } unsigned long timeval2ul(struct timeval *time) { unsigned long value=((time->tv_sec)*1000000+time->tv_usec); return value; } int setSendBuffsize(int socket,unsigned int size) { unsigned int recvbufsize; int size11 = sizeof(int); int resultret; resultret = getsockopt(socket, SOL_SOCKET, SO_SNDBUF, &recvbufsize,&size11); if (resultret != 0) { perror("ERROR!getsockopt"); return -1; } int size12 = sizeof(int); resultret = setsockopt(socket, SOL_SOCKET, SO_SNDBUF, &size,size12); if (resultret != 0) { perror("ERROR!setsockopt"); return -1; } return 0; } int campareIPaddr(struct sockaddr_in *firstaddr,struct sockaddr_in *secondaddr) { char addr1[20],addr2[20]; memset(addr1,0,20); memset(addr2,0,20); memcpy(addr1,inet_ntoa(firstaddr->sin_addr),20); memcpy(addr2,inet_ntoa(secondaddr->sin_addr),20); return strcmp(addr1,addr2); } void maketimeout(struct timespec *timespec, int second) { struct timeval now; gettimeofday(&now, NULL); timespec->tv_sec = now.tv_sec + second; timespec->tv_nsec = now.tv_usec * 1000; } //初始化环形缓冲区函数 void initbuff( loopbuff *buff, int number, int size) { int i; loopnode *node1, *node2; sem_init(&loopbuffsem,0,0);//@@sem node1 = malloc(sizeof(loopnode)); node1->isValid=false; node1->packetptr = malloc(sizeof(char) * size); node1->next = NULL; buff->wbuffptr = node1; buff->rbuffptr = node1; buff->pbuffptr = node1; buff->buffsize = number; buff->packetnum = 0; for (i = 2; i <= number; i++) { node2 = malloc(sizeof(loopnode)); node2->isValid=false; node2->packetptr = malloc(sizeof(char) * size); if (i != number) { node2->next = NULL; } else { node2->next = buff->wbuffptr; } node1->next = node2; node1 = node2; } return; } //环形缓冲区回收处理函数 void recyclebuff(loopbuff * buff) { //释放动态分配的缓冲区空间 int i; loopnode *temp=(loopnode *)(buff->wbuffptr); loopnode *ptr; for(i=0;i<buff->buffsize;i++) { free(temp->packetptr); ptr=temp; temp=temp->next; free(ptr); } return; } #ifdef TCPTEST //TCP接收线程的接收操作函数 int recv2buff(int socket, loopbuff *buff, struct sockaddr_in *clientaddr,int length) { if ((buff->wbuffptr == buff->rbuffptr)&&(buff->rbuffptr->isValid==true)) { return 2; } loopnode *tempptr=buff->wbuffptr; tempptr->isValid=true; packet *pktptr=NULL; int len = recv(socket, tempptr->packetptr,recordvar.packetsize, MSG_WAITALL); if (len > 0) { pktptr = tempptr->packetptr; if ((pktptr->flag) == 1) { pktptr->packetrecvlen=len; #ifdef MULTICASTTEST pktptr->srcaddr=*clientaddr; #endif //@end MULTITEST buff->wbuffptr = tempptr->next; /* pthread_mutex_lock(&mutex); if (buff->packetnum == 0) { pthread_cond_signal(&qready); } buff->packetnum++; pthread_mutex_unlock(&mutex); */ pthread_mutex_lock(&mutex); buff->packetnum++; pthread_mutex_unlock(&mutex); sem_post(&loopbuffsem); return 0; } } else if (len == 0) { //peer socket disconnect return 1; } else if (len < 0) { perror("error! recvfrom error!"); return -1; } } #else //UDP接收线程的接收操作函数 int recv2buff(int socket,loopbuff *buff, struct sockaddr_in *clientaddr,int length) { if ((buff->wbuffptr == buff->rbuffptr)&&(buff->rbuffptr->isValid==true)) { //缓冲区溢出 //printf(" @# %d ",buff->packetnum); return 2; } loopnode *tempptr=buff->wbuffptr; packet *pktptr=NULL; debug("@@recv2buff a packet1!\n"); int label = recvfromAdaption(socket, tempptr->packetptr, recordvar.packetsize,0, clientaddr); debug("@@recv2buff a packet2 label=%d!\n",label); if (label == 0) { tempptr->isValid=true; pktptr = tempptr->packetptr; if ((pktptr->flag) == 1) { //正确接收一个应用层报文并判断报文类型正常后,进行报文头部处理,以便处理线程操作 #ifdef SENDNODEOTHER gettimeofday(&(pktptr->recvtime2), NULL); pktptr->packetrecvlen2=totalrecvFragsize; #elif RECVNODEOTHER gettimeofday(&(pktptr->recvtime1), NULL); pktptr->packetrecvlen1=totalrecvFragsize; #else pktptr->packetrecvlen=totalrecvFragsize; #endif //@end SENDNODEOTHER #ifdef MULTICASTTEST pktptr->srcaddr=*clientaddr; #endif //@end MULTITEST buff->wbuffptr = tempptr->next; //method one condition vars /* pthread_mutex_lock(&mutex); if(buff->packetnum == 0) { //通过条件变量通知当前empty缓冲区有新的报文写入 pthread_cond_signal(&qready); } buff->packetnum++; pthread_mutex_unlock(&mutex); */ //method two sem mechanism pthread_mutex_lock(&mutex); buff->packetnum++; // printf("%d",buff->packetnum); pthread_mutex_unlock(&mutex); sem_post(&loopbuffsem); } debug("@@recv2buff a packet6!\n"); return 0; } else if (label == 1) { //正确接收一个应用层碎片报文 return 1; } else if (label < 0) { //接收出错 perror("error! recvfrom error!"); return -1; } } //应用层报文发送处理函数 unsigned long sendtoAdaption(int socket,void *sendbuff, unsigned long packetsize,int flag,struct sockaddr_in *remoteaddr,unsigned int counts) { packet *pktptr=NULL; packet fragHeader; int fragHLen=sizeof(packet); unsigned int mm=0; unsigned int NumFrag=0; unsigned int LengthFrag=0; unsigned int sendFragsize=0; unsigned long sendbuffOffset=0; unsigned long totalsendFragsize=0; if(packetsize<=FRAGMAXSIZE) { LengthFrag=packetsize; pktptr=(packet *)sendbuff; pktptr->fragInfo.isFrag=false; pktptr->fragInfo.numFrag=0; pktptr->fragInfo.indexFrag=0; pktptr->fragInfo.lenFrag=packetsize; //应用层报文长度小于分片规定的大小,不进行分片操作 sendFragsize=sendto(socket, sendbuff, LengthFrag, flag,(struct sockaddr *) remoteaddr, sizeof(struct sockaddr)); debug("@@sendtoAdaption seq=%d len=%d\n",pktptr->packetseq,sendFragsize); totalsendFragsize=sendFragsize; int i; for(i=0;i<counts;i++) { } }else { NumFrag=(packetsize-FRAGMAXSIZE)/(FRAGMAXSIZE-fragHLen)+2; memcpy(fragSendbuff,sendbuff,fragHLen); pktptr=(packet *)fragSendbuff; pktptr->fragInfo.isFrag=true; pktptr->fragInfo.numFrag=NumFrag; for(mm=1;mm<=NumFrag;mm++) { pktptr->fragInfo.indexFrag=mm; if(mm!=NumFrag) { pktptr->fragInfo.lenFrag=FRAGMAXSIZE; }else { pktptr->fragInfo.lenFrag=(packetsize-FRAGMAXSIZE)-(mm-2)*(FRAGMAXSIZE-fragHLen)+fragHLen; } LengthFrag=pktptr->fragInfo.lenFrag; debug("@@sendtoAdaption fraglen=%u\n",LengthFrag); if(mm!=1) { sendbuffOffset=(mm-1)*FRAGMAXSIZE-(mm-2)*sizeof(packet); } debug("@@sendtoAdaption sendbuffOffset=%u\n",sendbuffOffset); memcpy(fragSendbuff+fragHLen,sendbuff+sendbuffOffset,LengthFrag-fragHLen); sendFragsize=sendto(socket, fragSendbuff, LengthFrag, 0,(struct sockaddr *) remoteaddr, sizeof(struct sockaddr)); debug("@@sendtoAdaption frag seq=%d index=%d[%d] len=%d[%d]\n",pktptr->packetseq,pktptr->fragInfo.indexFrag,NumFrag,LengthFrag,sendFragsize); totalsendFragsize+=sendFragsize; int i; for(i=0;i<5000;i++) { } } debug("@@sendtoAdaption totalsendFragsize=%u\n",totalsendFragsize); } return totalsendFragsize; } //应用层报文接收处理函数,主要用于分片重组 int recvfromAdaption(int socket,void *recvbuf,unsigned long packetsize,int flag,struct sockaddr_in *clientaddr) { packet *pktptr; int fragHLen=sizeof(packet); unsigned long recvFragsize=0; unsigned int ptrOffset=0; int length=sizeof(struct sockaddr); //memset(fragRecvbuff,0,FRAGMAXSIZE); recvFragsize = recvfrom(socket, fragRecvbuff, FRAGMAXSIZE,flag,(struct sockaddr *)clientaddr,&length); if(packetsize<recvFragsize) { //接收缓冲的报文大小设置不够大 printf("receive a packet which length is larger than the given defined size!recvlen=%u\n",recvFragsize); return -1; }else if(recvFragsize<0) { perror("ERROR!recvform error"); return -1; } deal_TAG: pktptr=(packet *)fragRecvbuff; //每次收完一个完整的应用层数据报文后fragseq会被清零,据此判断此次接收到的分片数据是否为新的应用层数据报文 if(fragSeq==0) { fragSeq=pktptr->packetseq; fragFlag=pktptr->fragInfo.isFrag; fragNum=pktptr->fragInfo.numFrag; fragRecvNum=0; debug("@@new packet fragSeq=%d\n",fragSeq); } /* * * 此次接收到的报文序号等于登记的fragseq,则: * 1. 本次处理的报文仍然为登记的应用firstaddr层报文中一个分片, * 2. 上一次处理的应用层报文完整接收,此次为新的应用报文 * */ if(fragSeq==pktptr->packetseq) { if(fragFlag==false) { //该应用层报文未分片 //remove the header of adaption layer,and deliver the packet to application layer buff memcpy(recvbuf,fragRecvbuff,recvFragsize); //fragSeq=0; totalrecvFragsize=recvFragsize; debug("@@recvfromAdaption a packet!\n"); return 0; }else if(fragFlag==true) { //该应用层报文分片 fragIndex=pktptr->fragInfo.indexFrag; debug("@@new packet fragSeq=%d index=%d[%d]\n",fragSeq,fragIndex,fragNum); //ptrOffset locate the storage position for the fragment in this time ptrOffset=(fragIndex-1)*(FRAGMAXSIZE-fragHLen); memcpy(recvbuf+ptrOffset,fragRecvbuff+fragHLen,recvFragsize-fragHLen); totalrecvFragsize+=recvFragsize-fragHLen; fragRecvNum++; if(fragRecvNum==fragNum) { //All fragments received return 0; } //there will be more fragment to be come return 1; } }else { //上一次处理的应用层报文没有完整接收,此次为新的应用报文到来 //reset global variables,clear recvbuf debug("@@miss packet\n"); fragSeq=0; totalrecvFragsize=0; memset(recvbuf,0,packetsize); goto deal_TAG; } } #endif //@end TCPTEST //滑动窗口函数,主要用于丢包和重复包的统计,实现机制是动态拷贝滑动窗口的标识至滑动窗口开始的位置 /* void slidingWinscheme(int *minseq,int *maxseq,int *curseq,unsigned int array[],recordInfo * recordptr) { int k,i,j,dd1,dd2,index; //判断当前的报文序号与窗口可接受的序号大小关系 if (*minseq > *curseq) { //当前序号小于窗口可接受最小序号,则不做处理,函数已经判断为丢包 printf("packet arrives too late!\n"); } else if (((*curseq > *minseq) && (*curseq < *maxseq)) || (*curseq == *minseq) || (*curseq == *maxseq)) { //当前序号处于窗口可接受序号范围 k = (*curseq - *minseq) % WINDOWSIZE;//当前报文序号所在的窗口索引,进行标识和重复包判断 if (array[k] == 0) { array[k] = 1; } else { pthread_rwlock_wrlock(&rwmutex); recordptr->duplpktnum++; pthread_rwlock_unlock(&rwmutex); } } else if ((*curseq > *maxseq)) { //当前序号大于窗口可接受序号最大序号,进行窗口移动 //dd1用于窗口从最小序号开始,第一个标识不为1(已接收)的索引位置 for (dd1 = 0; dd1 < WINDOWSIZE; dd1++) { if (array[dd1] != 1) break; } //dd2用于以当前序号为新窗口的最大序号时,新窗口的最小序号的索引位置 dd2 = *curseq - *minseq - WINDOWSIZE + 1; //根据dd1与dd2进行窗口的回拢操作 if (dd2 > dd1) { for (i = dd1; i < dd2; i++) { if (array[i] == 0) { pthread_rwlock_wrlock(&rwmutex); recordptr->misspktnum++; pthread_rwlock_unlock(&rwmutex); } } } //根据旧窗口的标识,更新部分新窗口的标识 index = (dd1 <= dd2) ? dd2 : dd1; *minseq = *minseq + index; *maxseq = *minseq + WINDOWSIZE - 1; for (j = index; j < WINDOWSIZE; j++) { array[j - index] = array[j]; } for (j = 1; j <=index; j++) { array[WINDOWSIZE - j] = 0; } k = (*curseq - *minseq) % WINDOWSIZE; array[k] = 1; int offset,realIndex,virIndex; offset= index = (dd1 <= dd2) ? dd2 : dd1; *minseq = *minseq + offset; *maxseq = *minseq + WINDOWSIZE - 1; virIndex = (*curseq - *minseq) % WINDOWSIZE; realIndex=(virIndex+offset)%WINDOWSIZE; array[realIndex]=1; } } */ //滑动窗口函数,主要用于丢包和重复包的统计,使用循环数组的方式进行实现,避免了循环拷贝操作,相比动态拷贝接收标识具有更高的运行效率 void slidingWinscheme(int *minseq,int *maxseq,int *curseq,unsigned int array[],recordInfo * recordptr) { int i,dd1,dd2,index,temp; debug("@@slidingWinscheme minseq=%d,maxseq=%d,curseq=%d\n",*minseq,*maxseq,*curseq); //判断当前的报文序号与窗口可接受的序号大小关系 if (*minseq > *curseq) { //当前序号小于窗口可接受最小序号,则不做处理,函数已经判断为丢包 //printf("packet arrives too late!\n"); } else if (((*curseq > *minseq) && (*curseq < *maxseq)) || (*curseq == *minseq) || (*curseq == *maxseq)) { //定位curseq报文在滑动窗口中的下标 index = (*curseq-1) % WINDOWSIZE; if (array[index] == 0) { array[index] = 1; } else { pthread_rwlock_wrlock(&rwmutex); recordptr->duplpktnum++; pthread_rwlock_unlock(&rwmutex); } debug("@@slidingWinscheme index=%d\n",index); } else if ((*curseq > *maxseq)) { //dd1与dd2来判断丢包率,都代表所指的报文序号,进而能够确定报文在滑动窗口的下标位置 //dd2表示以curseq为滑动窗口可接收最大报文序号时,滑动窗口开始处的报文序号 int dd2=*curseq-WINDOWSIZE+1; //dd1根据滑动窗口的标识,当前可进行回拢操作的结束位置 for(dd1=*minseq;dd1<=*maxseq;dd1++) { if(1!=array[(dd1-1)%WINDOWSIZE]) { break; } array[(dd1-1)%WINDOWSIZE]=0; } debug("@@slidingWinscheme dd1=%d,dd2=%d\n",dd1,dd2); //根据dd1和dd2,计算丢包情况 if(dd2>dd1) { for (i = dd1; i < dd2; i++) { if (array[(i-1)%WINDOWSIZE] == 0) { pthread_rwlock_wrlock(&rwmutex); recordptr->misspktnum++; pthread_rwlock_unlock(&rwmutex); } } *minseq=dd2; }else{ *minseq=dd1; } *maxseq = *minseq + WINDOWSIZE - 1; index = (*curseq-1) % WINDOWSIZE; debug("@@slidingWinscheme index=%d\n",index); array[index]=1; } }
下面是发送文件
#include "Testheader.h" /*********************************************************************************************** Program target: caculate VPX blade server's bandwidth(principle 40Gbps)----UDP sender Command format: ./aout [Local IP address] [Receiver IP address] [Interval(us)] [Packet Size(Bytes)][counts] Structure Description:Single-thread the main thread --------- initial operation the send thread --------- send the periodic packet the print thread --------- print the info in real-time Outcome: when we set the interval to send data, time cost for execute the operating of sending a packet is exclusive, in that way, the actual number of packets which is sent by the program is less than the number we expected. ***********************************************************************************************/ //Global variables pthread_t thread_id1,thread_id2; unsigned int count = 1; struct sockaddr_in remoteaddr; struct sockaddr_in hostaddr; int sock; void *sendbuf=NULL; //线程发送应用层报文的缓存区地址 void *recvbuf=NULL; //线程接收应用层报文的缓冲区地址 void *fragSendbuff=NULL; //线程发送适配层报文分片的缓存地址 void *fragRecvbuff=NULL; //线程接收适配层报文分片的缓存地址 extern pthread_attr_t attr; extern recordInfo recordvar; void *process_print(void *arg); void *process_send(void *arg); int counts=0; int main(int argc, char *argv[]) { memset((void *) &remoteaddr, 0, sizeof(remoteaddr)); memset((void *) &hostaddr, 0, sizeof(remoteaddr)); remoteaddr.sin_family = AF_INET; if(1==argc||(2==argc&&strcmp(argv[1],"-help"))) { printf("CMD Format: ./a.out [Local IP] [Remote IP ] [Interval(us)] [Packet Size(Bytes)] [counts]\n"); return -1; } if(6==argc) { counts=atoi(argv[5]); } if (5>argc) { printf("Invalid Parameter Number, Please cheack the Input parameter! ,argc = %d\n",argc); printf("CMD Format: ./a.out [Local IP] [Remote IP ] [Interval(us)] [Packet Size(Bytes)] [counts]\n"); return -1; } if (INADDR_NONE == (remoteaddr.sin_addr.s_addr = inet_addr(argv[2]))) { printf("Receiver IP address is invalid!"); exit(1); } else { printf("Receiver IP address:%s ", argv[2]); } printf("Send Interval:%sus Single Packet Size:%s Bytes couns:%s\n",argv[3], argv[4],argv[5]); remoteaddr.sin_port = htons(PORTNUM); if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { printf("ERROR!create socket failed!\n"); exit(1); } memset(&recordvar,0,sizeof(recordInfo)); recordvar.hostaddr.sin_addr.s_addr=inet_addr(argv[1]); recordvar.sendinterval=atoi(argv[3]); recordvar.packetsize=atoi(argv[4]); recordvar.sendnum=0; recordvar.recvnum=0; fragSendbuff=malloc(FRAGMAXSIZE); sendbuf = malloc(recordvar.packetsize); memset(sendbuf, '1',recordvar.packetsize); fragSendbuff=malloc(FRAGMAXSIZE); printf("@@There are %d cpus\n",sysconf(_SC_NPROCESSORS_CONF)); initpthreadmutex(); initPthreadAttr(&attr); pthread_create(&thread_id1, &attr, process_send, NULL); pthread_create(&thread_id2, NULL, process_print, NULL); //收尾处理,等待send_thread print_thread的结束,回收线程占用的系统资源 if (!pthread_join(thread_id1, NULL)) { perror("child thread terminal exceptionally"); } if (!pthread_join(thread_id2, NULL)) { perror("child thread terminal exceptionally"); } destorypthreadmutex(); free(sendbuf); free(fragSendbuff); close(sock); return 0; } void *process_send(void *arg) { setCPUAffinity(9); packet pkt; while (1) { pkt.flag=0x01; pkt.packetseq = count; memcpy(sendbuf, &pkt, sizeof(packet)); //len1=sendto(sock, sendbuf, atoi(argv[4]), 0,(struct sockaddr *) &remoteaddr, sizeof(remoteaddr)); if (-1 ==sendtoAdaption(sock, sendbuf, recordvar.packetsize, 0,&remoteaddr,counts)) { //if (-1 ==sendto(sock, sendbuf, recordvar.packetsize, 0,(struct sockaddr *)&remoteaddr,sizeof(remoteaddr))) { perror("ERROR! send error!"); exit(1); } count++; pthread_rwlock_wrlock(&rwmutex1); recordvar.sendnum++; pthread_rwlock_unlock(&rwmutex1); if(recordvar.sendinterval!=0) { usleep(recordvar.sendinterval); } } } void *process_print(void *arg) { setCPUAffinity(0); struct timeval tt1,tt2; float datarate; float rate; int flags=0; unsigned long difftime; unsigned long printtime=PRINTINTERVAL*1000000; unsigned int lastsendnum=0; unsigned long lasttotalrecvlen=0; gettimeofday(&tt1,NULL); while(1) { gettimeofday(&tt2,NULL); difftime=timeval2ul(&tt2)-timeval2ul(&tt1); if(difftime>printtime) { pthread_rwlock_rdlock(&rwmutex1); datarate=1000000*(8.0)*recordvar.packetsize*(recordvar.sendnum-lastsendnum)/difftime; if(!flags) { if(datarate/1024>1.0&&datarate/1024<1024) flags=1; else if(datarate/(1024*1024)>1.0&&datarate/(1024*1024)<1024) flags=2; else if(datarate/(1024*1024*1024)>1.0) flags=3; } //rate =100.0*(recordvar.sendnum-lastsendnum)/(printtime/recordvar.sendinterval); //printf("%d [%5.2f\%]packets send to %s during %u us : data rate comes to %10.3fbps\n",(recordvar.sendnum-lastsendnum),rate,inet_ntoa(remoteaddr.sin_addr),difftime,datarate); printf("%d Packets send to %s During %uus ",(recordvar.sendnum-lastsendnum),inet_ntoa(remoteaddr.sin_addr),difftime); if(flags==0) { printf("BandWidths come up to %7.3fbps\n",datarate); }else if(flags==1) { printf("BandWidths come up to %7.3f Kbps\n",datarate/1024); }else if(flags==2) { printf("BandWidths come up to %7.3f Mbps\n",datarate/(1024*1024)); }else if(flags==3) { printf("BandWidths come up to %7.3f Gbps\n",datarate/(1024*1024*1024)); } lastsendnum=recordvar.sendnum; pthread_rwlock_unlock(&rwmutex1); tt1=tt2; } } }
下面是接收端函数
#include "Testheader.h" /********************************************************************** Program target: caculate VPX blade server's bandwidth(principle 40Gbps)--UDP receiver Command format: ./aout [Local IP address] [packet Size(Bytes)] Structure Description:Single-thread the main thread --------- initial operation the recv thread --------- recv the periodic packet the print thread --------- print the info in real-time **********************************************************************/ //Global variables unsigned int array[WINDOWSIZE] = { 0 }; pthread_t thread_id1,thread_id2; struct sockaddr_in addr, clientaddr; int sock; void *sendbuf=NULL; //线程发送应用层报文的缓存区地址 void *recvbuf=NULL; //线程接收应用层报文的缓冲区地址 void *fragSendbuff=NULL; //线程发送适配层报文分片的缓存地址 void *fragRecvbuff=NULL; //线程接收适配层报文分片的缓存地址 extern pthread_attr_t attr; void *process_print(void *arg); void *process_recv(void *arg); int main(int argc, char *argv[]) { memset((void *) &addr, 0, sizeof(addr)); memset((void *) &clientaddr, 0, sizeof(addr)); addr.sin_family = AF_INET; if(1==argc||(2==argc&&(!strcmp(argv[1],"-help")))) { printf("CMD Format: ./aout [Local IP address] [packet Size(Bytes)]\n"); return -1; } if (3 != argc) { printf("Invalid Parameter Number, Please cheack the Input parameter!\n"); exit(1); } addr.sin_addr.s_addr = inet_addr(ALLOWIP_ADDR); addr.sin_port = htons(PORTNUM); if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { printf("ERROR!create socket failed!\n"); exit(1); } if (bind(sock,(struct sockaddr *)&addr,sizeof(struct sockaddr))==-1) { perror("ERROR!"); exit(1); } //initial the recordInfo structure before threads running memset(&recordvar,0,sizeof(recordInfo)); recordvar.hostaddr.sin_addr.s_addr=inet_addr(argv[1]); recordvar.packetsize=atoi(argv[2]); recordvar.sendnum=0; recordvar.recvnum=0; recordvar.misspktnum=0; recordvar.duplpktnum=0; recordvar.totalrecvlen=0; recvbuf = malloc(recordvar.packetsize); memset(recvbuf,0,recordvar.packetsize); fragRecvbuff=malloc(FRAGMAXSIZE); fragSeq=0; totalrecvFragsize=0; fragNum=0; fragIndex=0; fragFlag=0; printf("@@there are %d CPUs\n",sysconf(_SC_NPROCESSORS_CONF)); initpthreadmutex(); initPthreadAttr(&attr); pthread_create(&thread_id1,&attr,process_recv,NULL); pthread_create(&thread_id2, NULL,process_print, NULL); if (!pthread_join(thread_id1, NULL)) { perror("child thread terminal exceptionally"); } if (!pthread_join(thread_id2, NULL)) { perror("child thread terminal exceptionally"); } destorypthreadmutex(); free(recvbuf); free(fragRecvbuff); close(sock); return 0; } void *process_recv(void *arg) { setCPUAffinity(9); fd_set fd; struct timeval tt; int ret; tt.tv_sec = 3; tt.tv_usec = 0; packet *recvinfo; unsigned int minseq, maxseq, curseq; minseq = 1; maxseq = minseq + WINDOWSIZE-1; int label; int len; int length=sizeof(struct sockaddr); printf("Ready to recv packets!\n"); while (1) { FD_ZERO(&fd); FD_SET(sock, &fd); ret = select(sock + 1, &fd, NULL, NULL, &tt); if (-1 == ret) { //perror("select error!"); //exit(1); printf("select error!\n"); } else if (0 == ret) { //printf("No packet has been arrived!\n"); continue; } else { if (FD_ISSET(sock, &fd)) { label =recvfromAdaption(sock,recvbuf,recordvar.packetsize,0,&clientaddr); /*len= recvfrom(sock,recvbuf,65500,0,(struct sockaddr *)&clientaddr,&length); if(len>recordvar.packetsize) { printf("recvbuf is smaller than received packet\n"); continue; }else if(len>0) { recvinfo=(packet *)recvbuf; if (recvinfo->flag==1) { curseq = recvinfo->packetseq; slidingWinscheme(&minseq,&maxseq,&curseq,array,&recordvar); recordvar.recvnum++; recordvar.totalrecvlen+=len; } }*/ if (label == 0) { recvinfo=(packet *)recvbuf; if (recvinfo->flag==1) { curseq = recvinfo->packetseq; slidingWinscheme(&minseq,&maxseq,&curseq,array,&recordvar); recordvar.recvnum++; recordvar.totalrecvlen+=totalrecvFragsize; } fragSeq=0; totalrecvFragsize=0; } else if (label ==1) { //fragment isn't over continue; }else if(label<0) { printf("Recv error!\n"); } } } } } void *process_print(void *arg) { setCPUAffinity(0); struct timeval tt1,tt2; float datarate; float rate; int flags=0; float localmissrate; float globalmissrate; unsigned long difftime; unsigned long printtime=PRINTINTERVAL*1000000; unsigned int lastrecvnum=0; unsigned int lastmisspktnum=0; unsigned int lastduplpktnum=0; unsigned long lasttotalrecvlen=0; gettimeofday(&tt1,NULL); while(1) { gettimeofday(&tt2,NULL); difftime=timeval2ul(&tt2)-timeval2ul(&tt1); if(difftime>printtime) { pthread_rwlock_rdlock(&rwmutex); pthread_rwlock_rdlock(&rwmutex1); float datarate=1000000*8.0*(recordvar.totalrecvlen-lasttotalrecvlen)/difftime; if(datarate/1024>1.0&&datarate/1024<1024) flags=1; else if(datarate/(1024*1024)>1.0&&datarate/(1024*1024)<1024) flags=2; else if(datarate/(1024*1024*1024)>1.0) flags=3; localmissrate=100.0*(recordvar.misspktnum-lastmisspktnum)/((recordvar.recvnum - lastrecvnum)+(recordvar.misspktnum-lastmisspktnum)); globalmissrate=100.0*recordvar.misspktnum/(recordvar.recvnum+recordvar.misspktnum); printf("%d Packets received from %s :Duplicated[%u], Lost[%u],[%5.3f%/%5.3f%]",(recordvar.recvnum - lastrecvnum), inet_ntoa(clientaddr.sin_addr),(recordvar.duplpktnum-lastduplpktnum), (recordvar.misspktnum-lastmisspktnum),localmissrate,globalmissrate); if(flags==0) { printf("BandWidths come up to %7.3fbps\n",datarate); }else if(flags==1) { printf("BandWidths come up to %7.3f Kbps\n",datarate/1024); }else if(flags==2) { printf("BandWidths come up to %7.3f Mbps\n",datarate/(1024*1024)); }else if(flags==3) { printf("BandWidths come up to %7.3f Gbps\n",datarate/(1024*1024*1024)); } lastmisspktnum=recordvar.misspktnum; lastduplpktnum=recordvar.duplpktnum; pthread_rwlock_unlock(&rwmutex); lastrecvnum = recordvar.recvnum; lasttotalrecvlen=recordvar.totalrecvlen; pthread_rwlock_unlock(&rwmutex1); tt1 = tt2; } } }
下面是发送端和接收端的截图,其中可以统计丢包情况。