UDP收发包性能测试

自己写的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;
		}
	}
}

下面是发送端和接收端的截图,其中可以统计丢包情况。



猜你喜欢

转载自blog.csdn.net/jj12345jj198999/article/details/79618461
今日推荐