本机UDP收发性能测试

  1. 测试内容
    测试单机版的UPD客户端和服务端之间的性能,UDP客户端发送数据到UDP服务端,并等待服务端返回,计算出UDP的性能

  2. 测试方法
    客户端和服务端部署在同一台虚拟机器上,客户端启动多个线程,同时向服务端发送指定数量的数据,服务端返回同样的数据,客户端同步等待服务端返回后才发送下个数据。为了性能最大化,测试过程中,客户端打印的日志都指向/dev/null,而服务端就通过信号来触发打印当前接收到的数量。

  3. 测试环境
    硬件环境:intel core i7-4810MQ 2.8GHZ 8GB RAM
    软件环境:ubuntu16.04s gcc

  4. 性能测试
    这次测试客户端收发的数据分别是,128B、1024B、2048B、65507B 。作为并发,客户端以1个线程作为1个虚拟客户端,测试分别虚拟 1 、4、16个客户端。服务端也分别启动1、4、16个线程来接收客户端的数据。客户端向服务端发送数据,同步等待服务端返回相同的数据,客户端等待超时时间为2秒。
    客户端发送总数量的表示是,虚拟客户端线程*每个客户端发送的数量。另外,这次代码用的是C语言,服务端用静态数组来接收客户端发送的数据,由于性能比较高,静态数组的大小会影响整体性能,服务端静态分配内存的大小,分别以65507(UDP每个包的最大值)和对应测试数据的大小(比如128、1024)来测试

    4.1 128B的收发数据
    在这里插入图片描述
    通过测试可以发现,服务端在只有1个线程的情况下,TPS的最大值是78111,而在4个线程的情况下,最高可以达到353253。

    4.2 1024B的收发数据
    在这里插入图片描述
    这次测试,只计算了第一次的结果,其他结果和第一次也差不多。

    4.3 2048B的收发数据
    在这里插入图片描述
    4.4 65507的收发数据
    在这里插入图片描述
    65507B是UDP每个包的最大值,到了这里,成功率明显降低了,其实,如果把客户端和服务端部署在不同的机器上,这种成功率更低,也只有90%左右

  5. 测试代码
    5.1 客户端代码

/*
udpClient.c
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/timeb.h>
#define WAIT_OUT 0
#define WAIT_IN  1
#define MAX_PTHREAD 1000
struct dstServer
{
 char pcIp[20];
 char pcPort[6];
 int  iLoopSize;
};
void sendToServer( struct dstServer *savr );
int sendToUdpServer( int iaSocket, struct sockaddr_in *saServerAddr );
int setnonblocking(int fd);
pthread_mutex_t mutex;
struct dstServer svr;
int igTotalSendSize = 0;
int igTotalRecvSize = 0;
char *pcgFileData = NULL;
int  igFileDataLen = 0;
#define TIME_OUT 2 //超时时间
/*
UDP双向客户端
*/
#define MAX_UDP_PACKAGES 65507   //UDP协议定义的每次发送数据的最大值,根据《TCP-IP详解卷1:协议.pdf》中的11.10/122页,IP报文长度最大是65535,去掉20B的IP头和8B的UDP首部,剩下65507
int main( int argc, char *argv[] )
int main( int argc, char *argv[] )
{
  int iTmp;
  int iLoopSize;
  int iPthreadSize;
  pthread_t id[MAX_PTHREAD];
  void *status;
  char pcSendFile[1024];
  FILE *fp;
  if (argc < 5)
 {
  fprintf( stderr, "usage: %s IP PORT LOOPS PTHREAD_SIZE\n", argv[0] );
  return 0;
 }
 if(pthread_mutex_init(&mutex,NULL) != 0 )  
  {  
    printf("Init metux error.");  
    exit(1);  
  }
 iLoopSize = atoi(argv[3]);
 iPthreadSize = atoi(argv[4]);
 if (iPthreadSize > MAX_PTHREAD)
 {
  iPthreadSize = MAX_PTHREAD;
 }
 fprintf( stdout, "total loop %d\n", iLoopSize * iPthreadSize );
 memset( &svr, 0x00, sizeof(svr) );
 strcpy( svr.pcIp, argv[1] );
 strcpy( svr.pcPort, argv[2] );
 svr.iLoopSize = iLoopSize;
 if (argc == 6)
 {
  memset( pcSendFile, 0x00, sizeof(pcSendFile) );
  strcpy( pcSendFile, argv[5] );
  fp = fopen( pcSendFile, "rb" );
  if (fp == NULL)
  {
   fprintf( stderr, "open file [%s] error\n", pcSendFile );
   return 0;
  }
  fseek( fp, 0, SEEK_END );
  igFileDataLen = ftell(fp);
  rewind(fp);
  if (igFileDataLen > MAX_UDP_PACKAGES)
  {
    fprintf( stdout, "单个UDP数据包的长度不能大于[%d]\n", MAX_UDP_PACKAGES );
    fclose(fp);
    exit(0);
  }
  pcgFileData = malloc( igFileDataLen + 1 );
  if (pcgFileData == NULL)
  {
   fprintf( stderr, "get ram error\n" );
   fclose(fp);
   return 0;
  }
  memset( pcgFileData, 0x00, igFileDataLen + 1 );
  fread( pcgFileData, 1, igFileDataLen, fp );
  fclose(fp);
 }
  fprintf( stderr, "bat start:%ld\n", clock() );
  for (iTmp = 0; iTmp < iPthreadSize; iTmp++)
  {
   //启动多个线程,向服务端发送数据
   pthread_create(&(id[iTmp]), NULL, (void *)&sendToServer, &svr);
  }
  for (iTmp = 0; iTmp < iPthreadSize; iTmp++)
  {
   pthread_join( id[iTmp], &status );
  }
  fprintf( stderr, "bat   end:%ld\n", clock() );
  fprintf( stderr, "igTotalRecvSize[%d]\n", igTotalRecvSize );
  return 0;
}
void sendToServer( struct dstServer *savr )
{
 struct sockaddr_in servAddr;
  time_t tp;
  struct timeb tb;
  struct tm *T_Now;
  int iSocket;
  struct timeval tv; 
  tv.tv_sec = TIME_OUT; 
  tv.tv_usec = 0;
  /*建立socket*/
  iSocket = socket(AF_INET, SOCK_DGRAM, 0);
  if (iSocket == -1)
  {
    return;
  }
  servAddr.sin_family = AF_INET;
  servAddr.sin_port = htons(atoi(savr->pcPort));
  servAddr.sin_addr.s_addr = inet_addr(savr->pcIp);
  //设置超时
  if (setsockopt(iSocket, SOL_SOCKET, SO_RCVTIMEO,&tv,sizeof(tv)) < 0)
  {
   return;
  }
 int iLoop;
 for (iLoop = 0; iLoop < savr->iLoopSize; iLoop++)
  {
  //循环向服务端发送数据,并等待接收服务端返回的数据
   sendToUdpServer( iSocket, &servAddr );
  }
  return;
}
int sendToUdpServer( int iaSocket, struct sockaddr_in *saServerAddr )
{
    int    iSocket;
    char  pcBuf[MAX_UDP_PACKAGES+1];
    int  iRet;
    struct sockaddr_in addr;
    int  iAddrLen = sizeof(addr);
    int  iSendLen = 0;
    int  iRecvLen = 0;
    time_t tStart;
    time_t tNow;
    time_t tp;
   /*获得socket*/
    iSocket = iaSocket;
    //发送数据
    if (pcgFileData == NULL)
    {
    memset( pcBuf, 0x00, sizeof(pcBuf) );
    strcpy( pcBuf, "01234567890123456789" );
    iSendLen = sendto( iSocket, pcBuf, strlen(pcBuf), 0, (struct sockaddr *)saServerAddr, sizeof(struct sockaddr_in) ); 
   }
   else
   {
    iSendLen = sendto( iSocket, pcgFileData, igFileDataLen, 0, (struct sockaddr *)saServerAddr, sizeof(struct sockaddr_in) ); 
   }
  fprintf( stdout, "send len[%d]\n", iRet );
  __sync_fetch_and_add(&igTotalSendSize, 1);
   fprintf( stdout, "pthreadId[%ld] total send size[%d]\n", pthread_self(), igTotalSendSize );
   tStart = time(NULL);//开始计时
   while (1)
   {
     tNow = time(NULL);
     if (tNow - tStart > TIME_OUT)
     {
       fprintf( stdout, "recv time out[%d]\n", tNow - tStart );
       break;
     }
    //recv data
    memset( pcBuf, 0x00, iSendLen + 1 );
    iRecvLen = 0;
    iRecvLen = recvfrom( iSocket, pcBuf , iSendLen, 0, (struct sockaddr *)&addr, &iAddrLen );
    if(iRecvLen < 0)//recv error
    {
      //没有数据进来
      if((errno == EAGAIN) || (errno == EWOULDBLOCK))
      {
        continue;
      }
      fprintf( stdout, "recv error\n" );
      //接收异常
      break;
    }
    else
    if (iRecvLen == 0)//连接关闭
    {
      //接收异常
      fprintf( stdout, "recv error\n" );
      break;
    }
    fprintf( stdout, "recv[%d][%s]\n", iRecvLen, pcBuf );
    __sync_fetch_and_add(&igTotalRecvSize, 1);
    break;
   }
   fprintf( stdout, "pthreadId[%ld] total recv size[%d]\n",pthread_self(), igTotalRecvSize );
   //close(iSocket);
   return 0;
}

编译: gcc -o udpClient udpClient.c -lpthread
执行: ./udpClient 服务端IP地址 服务端侦听端口 每个客户端发送的数量 虚拟客户端数量 文件数据 。比如 ./udpClient 0.0.0.0 20000 10000 100 send.txt,这里,0.0.0.0是服务端的IP地址,也就是本机,20000 是服务端的侦听端口,10000是每个客户端发送的数量,100是客户端的数量,send.txt是包含数据的文件,如果没有send.txt,默认是发送20B,这个send.txt的数据量不能超过65507

5.2 服务端代码

/*
udpServe.c
*/
/*
实现UDP双向服务
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <fcntl.h>
#define ERROR -1
#define MAX_BUF 2048   //静态内存设置,测试过程中需要改变这个大小,最大值是65507
int createServerSocket( int iaPort );
void showMessage(int iaSigNum);
void recvWorker( int *iaSocket );
void setSignal();
#define PTHREAD_SIZE 16  //服务端线程数量
int igRecvSize = 0;      //服务端接收到的总数量
int main( int argc, char *argv[] )
{
 char pcPort[5+1];
 int  iSocket;
 int  iEpollFd;
 int  iRet;
 int  iRetNum;
 char pcBuf[MAX_BUF];
 int  iRecvLen;
 struct sockaddr_in sClientAddr;
  int  iClientAddrLen;
  char pcClientIp[20];
  int  iPort;
  int  iTmp;
  void *status;
  pthread_t recvId[1000];
 if (argc == 1)
 {
  fprintf( stdout, "usage: %s port\n", argv[0] );
  return 0;
 }
 //get port
 memset( pcPort, 0x00, sizeof(pcPort) );
 strcpy( pcPort, argv[1] );
 //create socket
 iSocket = createServerSocket(atoi(pcPort));
 if (iSocket == -1)
 {
  fprintf( stderr, "create server socket error\n" );
  return 0;
 }
 iClientAddrLen = sizeof(sClientAddr);
 setSignal();
 fprintf( stdout, "pid[%d]\n", getpid() );
 //启动多个线程,接收客户端过来的数据
  for (iTmp = 0; iTmp < PTHREAD_SIZE; iTmp++)
  {
   if (pthread_create(&(recvId[iTmp]), NULL, (void *)(&recvWorker), &iSocket ) != 0)
   {
    fprintf( stderr, "start accept worker error[%d][%s]\n", errno, strerror(errno) );  fflush(stderr);
      exit(1);
   }
  }
  for (iTmp = 0; iTmp < PTHREAD_SIZE; iTmp++)
  {
    pthread_join(recvId[iTmp], &status);
  }
  close( iSocket );
  return 0;
}
void recvWorker( int *iaSocket )
{
 int  iSocket;
 int  iRet;
 char pcBuf[MAX_BUF+1];
 int  iRecvLen;
 struct sockaddr_in sClientAddr;
  int  iClientAddrLen;
  char pcClientIp[20];
  int  iPort;
 //create socket
 iSocket = *iaSocket;
 iClientAddrLen = sizeof(sClientAddr);
  //根据客户端的IP地址和发起端口来确认唯一SOCKET
  while (1)
  {
   memset( pcBuf, 0x00, sizeof(pcBuf) );
   iRecvLen = recvfrom(iSocket, pcBuf, sizeof(pcBuf) - 1, 0, (struct sockaddr *)&sClientAddr, &iClientAddrLen);
    if(iRecvLen < 0)//recv error
  {
      if((errno == EAGAIN) || (errno == EWOULDBLOCK))
   {
        fprintf( stdout, "read later\n" );
        continue;
      }
      continue;
    }
    if (iRecvLen == 0)//连接关闭
    {
      continue;
    }
    //get ip and port
    #if 0
    memset( pcClientIp, 0x00, sizeof(pcClientIp) );
    inet_ntop( AF_INET, &(sClientAddr.sin_addr), pcClientIp, sizeof(pcClientIp) );
    iPort = ntohs(sClientAddr.sin_port);
    #endif
    __sync_fetch_and_add(&igRecvSize, 1);
    //fprintf( stdout, "recvfrom[%s:%d]\n", pcClientIp, iPort );
    //fprintf( stdout, "recv total[%d]iRecvLen[%d][%s]\n", igRecvSize, iRecvLen, pcBuf );fflush(stdout);
    iRet = sendto( iSocket, pcBuf, iRecvLen, 0, (struct sockaddr *)&sClientAddr, iClientAddrLen ); 
  }
}
/**********************************************************************
函数名称: createServerSocket 
函数功能: 创建SOCKET
参    数:
第    一:端口         I
**********************************************************************/
int createServerSocket( int iaPort )
{
  struct sockaddr_in server;   /*服务器地址信息结构体*/
  int    iOpt;
  int    iSocket;
  fprintf( stdout, "start create socket\n" );
  /*建立socket*/
  iSocket = socket(AF_INET, SOCK_DGRAM, 0);
  if (iSocket == -1)
  {
    fprintf( stderr, "create socket error[%d][%s]\n", errno, strerror(errno) );   
    return ERROR;
  }
  /*设置socket属性*/
  //iOpt = SO_REUSEADDR;
  //setsockopt(iSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&iOpt, sizeof(iOpt));
  memset(&server, 0x00, sizeof(server));
  server.sin_family = AF_INET;
  server.sin_port = htons(iaPort);
  server.sin_addr.s_addr = htonl(INADDR_ANY);
  /*调用bind绑定地址*/
  if (bind(iSocket, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1)
  {
    fprintf( stderr, "bind socket error[%d][%s]", errno, strerror(errno) );
    return ERROR;
  }
  fprintf( stdout, "create socket success\n" );
  return iSocket;
}
/**********************************************************************
函数名称: showMessage
函数功能: 捕捉信号值 12,显示进程信息
参    数:
返    回:异常直接退出程序
**********************************************************************/
void showMessage(int iaSigNum)
{
 fprintf( stderr, "igRecvSize[%d]\n", igRecvSize );fflush(stderr);
 return;
}
/**********************************************************************
函数名称: setSignal
函数功能: 设置信号量
参    数:
返    回:异常直接退出程序
**********************************************************************/
void setSignal()
{
 //捕捉信号
 signal(SIGUSR2, showMessage);//信号值12,显示进程信息,该信号量为备用
 return;
}

编译: gcc -o udpServer udpServer.c
执行: ./udpServer 侦听端口。比如 ./udpServer 20000

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值