本机UDP收发性能测试

article/2025/4/28 7:37:38
  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 datamemset( 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;}elseif (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 portmemset( pcPort, 0x00, sizeof(pcPort) );strcpy( pcPort, argv[1] );//create socketiSocket = 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 socketiSocket = *iaSocket;iClientAddrLen = sizeof(sClientAddr);//根据客户端的IP地址和发起端口来确认唯一SOCKETwhile (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 0memset( 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


http://chatgpt.dhexx.cn/article/w2ddaOZE.shtml

相关文章

2020复旦大学计算机夏令营机试题

2020复旦大学计算机夏令营机试题 这个是开卷编程&#xff0c;不计入成绩&#xff0c;但是面试会问你做的情况。

复旦大学机试题题解(2017~2021)

前言 尝试把复旦大学历年的机试题做一遍&#xff0c;能做多少是多少了&#xff0c;因为没有数据集所以也不好确认是不是对的~&#xff08;题目来自王道往届学长学姐回忆以及某位热心的同学整理&#xff0c;如果了解来源后&#xff0c;我一定备注来源&#xff09; &#xff08;…

复旦大学:专硕没住宿?我们帮你建!还给补助!

去年&#xff0c;小编发过一篇文章&#xff0c;里面爆出了复旦大学等学校的专硕可能不提供住宿了&#xff1a; 北京大学 上海交通大学 复旦大学 专硕可能不提供住宿&#xff01; 后来&#xff0c;在复旦大学2019年招收攻读硕士学位研究生简章中&#xff0c;学校官方公布了不为专…

复旦大学机试题(2017-2021)

计院2021 1.完全二叉树 给定一颗二叉树&#xff0c;树的每个节点的值为一个正整数。如果从根节点到节点 N 的路径上不存在比节点 N 的值大的节点&#xff0c;那么节点 N 被认为是树上的关键节点。求树上所有的关键节点的个数。请写出程序&#xff0c;并解释解题思路。 例子&a…

三战上岸复旦工研院考研回忆(无干货)

干货链接&#xff1a; 复旦085400电子信息专硕初试经验分享 复旦085400电子信息专硕复试经验分享 复旦软院电子信息初试961真题回忆 说说一下我的考研经历&#xff0c;都是流水账&#xff0c;不涉及干货&#xff08;干货参照其他博客&#xff09;&#xff0c;如果读者能有所借…

2020年复旦大学夏令营机试题及代码(VS2019 C++)

注:代码是本菜鸡自己个儿写的,没有找到参考答案,欢迎各位大佬批评指正. 题目如下图所示&#xff08;图片来源网上&#xff09;: 第1题 该题主要考察拓扑排序.其实该算法有通用模板,但我写的时候没有意识到使用,代码不是很规范. #define _CRT_SECURE_NO_WARNINGS#include &…

复旦大学研究生机试(2019)

1. 计算机学院 今年的题目可以说是挺难的&#xff0c;第一题虽然像是送分题&#xff0c;实际上也不是很简单。第二题第三题是动态规划问题&#xff0c;而且复旦据说会卡大数&#xff0c;今年150人考生据说只有一个AC&#xff0c;大部分人只做出第一题&#xff0c;个别零分。 …

【20保研】2019年复旦大学工程与应用技术研究院全国优秀大学生夏令营通知

点击文末的阅读原文或者公众号界面左下角的保研夏令营或者公众号回复“夏令营”是计算机/软件等专业的所有保研夏令营信息集合&#xff0c;会一直更新的。 为了促进我国高校优秀大学生之间的交流、加强学生对复旦大学工研院的了解、特别是吸引优秀学生继续深造&#xff0c;探索…

复旦大学计算机专业硕士平均工资,在复旦大学当教授“月薪”是多少?这个工资条,让网友非常羡慕!...

文章原创&#xff0c;版权归本作者所有&#xff0c;欢迎个人转发分享 随着中国的高等教育发展的重视&#xff0c;很多的高校也是不负众望&#xff0c;不仅在国内知名度很高&#xff0c;在国外也享有盛誉的。 在中国知名度最高的大学就是清华、北大、复旦等高校&#xff0c;是受…

复旦计院、工研院2019机试真题及答案详解

计院 A 相隔天数 输入一个 yyyymmdd 格式的时间&#xff0c;如 20190318&#xff0c;计算与 20190205 相差的天数&#xff0c; 取绝对值&#xff0c;所有输入在 19000101 和 21000101 之间。 样例输入&#xff1a;20190208 输出&#xff1a;3 #include<iostream>us…

复旦计院、工研院2018机试真题及答案详解

计院 A 求众数 众数就是一个序列中出现次数最多的数字。 如果不唯一&#xff0c;则输出小的那个值。 给定第一个代表有几个数字。 1<n<10^5 每个数字在 int 范围内样例&#xff1a; 输入&#xff0c; &#xff08;第一个代表有几个数字&#xff09;8 10 3 8 8 …

2019年复旦大学机试题

机试&#xff1a; 一、计算机学院&#xff1a; 1、 算法笔记上有类似题&#xff0c;且time与time2都是输入。 而本体time是输入&#xff0c;time2是题目给的。 设置time与time2&#xff0c;为计算方便设定让time恒小于time2。 方法&#xff1a;从time&#xff08;较早的日期…

2019 复旦大学工研院上机题-计算通讯代价

题目&#xff1a; 给出一个树&#xff0c;计算每个节点到其他节点的通讯代价的总和&#xff0c;假如树为 1————2————3 1 则结点 1&#xff0c;2&#xff0c;3 通讯代价分别为&#xff1a;3&#xff0c;2&#xff0c;3 例&#xff1a; 输入&#xff1a; 3 1 2 2 3 输出&…

更正:复旦大学工研院计算机学硕不是第一年招生

首先对大家说一声抱歉&#xff01; 昨天弄错了复旦大学工研院的情况。 据复旦大学工研院的在读同学描述&#xff0c;实际上复旦大学工研院计算机学硕是第二年招生&#xff0c;专硕是第一年招生。 专硕的招生目录&#xff1a; 考试内容&#xff1a;①101思想政治理论;②204英语二…

研究生院校推荐——复旦大学工研院

概述 过去的一年几乎都在准备考研&#xff0c;现在差不多勉强上岸&#xff0c;写一点经验和教训。 我最初的目标院校是上海交大电院计算机&#xff0c;最后上岸是复旦大学工研院计算机。 今年的上交计算机专硕分数线是325&#xff0c;复旦工研院学硕分数线是340&#xff0c;题…

入营人数线性增长,录取人数保持稳定,复旦工研院有点抢手

1、院校介绍 复旦大学工程与应用技术研究院是复旦大学发挥文理医综合优势&#xff0c;聚焦解决国家重大需求的工程与应用研发&#xff0c;发展具有复旦特色工程学科的一项重要举措。其下设有智能机器人研究院、生物医学工程技术研究所和超越照明研究所三个研究机构。近几年来&…

链路聚合实验

目录 1 链路聚合配置实验 1.1 实验内容 1.2 实验原理 1.3 关键命令 1.4 配置过程 2 链路聚合与VLAN配置实验 2.1 实验内容 2.2 实验原理 2.3 配置过程 3 链路聚合与生成树配置实验 3.1 实验内容 3.2 实验原理 3.3 配置过程 4 链路聚合与RSPAN配置实验 4.1 实验…

链路聚合配置

链路聚合 链路聚合介绍 链路聚合模式 链路聚合配置 链路聚合介绍 链路聚合&#xff1a;将多个以太网链路捆绑为一条逻辑的以太网链路 作用&#xff1a; 1.提高带宽 2.节省IP地址 链路聚合组 二层聚合组&#xff1a;随着二层聚合端口的创建自动生成的&#xff0c;只包含二…

为什么会有链路聚合这种技术?

这里写目录标题 前言链路聚合是什么&#xff1f;二层交换机链路聚合三层交换机链路聚合总结 前言 在企业网络中&#xff0c;所有设备的流量在转发到其他网络前都会汇聚到核心层&#xff0c;再由核心区设备转发到其他网络&#xff0c;或者转发到外网。因此&#xff0c;在核心层…

Linux链路聚合

CSDN话题挑战赛第2期https://marketing.csdn.net/p/7b6697fd9dd3795a268d1a6f2fe75012 参赛话题&#xff1a;学习笔记https://activity.csdn.net/creatActivity?id10213 一、概念 指的是将多个物理端口汇聚在一起&#xff0c;形成一个逻辑端口&#xff0c;以实现出、入流量吞…