发送端系统:ubuntu18.04
接收端系统:ubuntu18.04
最近要做一些socket的实验,我对socket也不大了解,不过socket还算是比较好学的,算是在应用层和传输层中间,给你提供了调用了传输协议的api,还是很友好的哦!
哦吼!我要对socket发送文件的速率进行限制,想要把文件传输速率限制到想要设置的速率。大概原理如下:

一、主要原理

比如说,我要把文件的传输速率限制到10Mbps,他等同于,在一秒钟传输10Mbit的内容。所以我们需要定时器+文件传输限制。大概就这两部分。
socket传输文件可以看一下这篇文章:Linux下基于TCP的简易文件传输(socket编程)

1.1 定时器

定时器的话采用linux C语言中的时间函数clock_gettime,函数原型如下:

int clock_gettime(clockid_t clk_id, struct timespec *tp);

其中,cld_id类型四种:

a、CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变
b、CLOCK_MONOTONIC,从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
c、CLOCK_PROCESS_CPUTIME_ID,本进程到当前代码系统CPU花费的时间
d、CLOCK_THREAD_CPUTIME_ID,本线程到当前代码系统CPU花费的时间

其中,timespec结构包括:

struct timespec {
time_t tv_sec; /* 秒*/
long tv_nsec; /* 纳秒*/
};

1.2 限制文件传输大小

我们采用fread函数传输文件,其传输过程的文件大小是可以指定的,函数原型如下:

size_t   fread(   void   *buffer,   size_t   size,   size_t   count,   FILE   *stream   )

函数从输入文件(数据流)中读取size*count字节 存放到buffer中,并返回内容数量

2 功能描述

不同文章描述的发送端和接收端还有客户端和服务器端不一致,最开始看的很糊涂,这里就只用发送端和接收端描述。
功能:可以实现socket文件传输的速率控制,并输出socket程序运行时间,输出抓取文件的速率。我测试了好多次,情况大概如下:

2.1 文件传输速率设置超过瓶颈带宽时

也就是RATE(设置的限速)大于客户端与接收端的bottleneck bandwidth(瓶颈带宽)的时候,会产生段错误,我也不是很清楚为什么会这样。
关于如何得到瓶颈带宽的大小,可以用iperf对带宽进行测量,iperf命令可以参考:iperf命令详解
但是对网卡进行限速时就不会有这样的情况,比如说,我把网卡限速为100Mbps,即限制出网卡流量速率为100Mbps,传输过程的瓶颈带宽为800Mbps,这时候对socket文件传输设置为200Mbps,那么能够得到的socket文件的传输速率就为100Mbps,因为网卡的限速啊。

2.2 测量瓶颈带宽

在不限制速率的情况下可以测试瓶颈带宽,不过需要删掉“定时器”,让它传就可以了,不用限时。比较耗费带宽资源。而且程序需要更改。不如直接用iperf,哈哈哈!但是如果要自己做测量带宽的工具的话,可以作一下参考。呃!可能也没啥用[捂脸笑]!!!

3 编程

在这篇文章的基础上改动的,所以可能在输出的时候不是很好看。呃,太懒了,不太想改了…太难了…

3.1 发送端:

待传输文件需要和发送端代码(可执行文件)放在同一个文件夹下,因为没有规定路径

#include <sys/types.h>         //socket
#include <sys/socket.h>       //socket
#include <arpa/inet.h>        //inet_pton,inet_ntop
#include <stdio.h>        //printf
#include <stdlib.h>       //exit
#include <string.h>       //bzero
#include <netinet/in.h>       //sockaddr_in
#include <unistd.h>
#include <ctype.h>
#include <sys/stat.h>     //struct stat
#include <time.h>     //clock#pragma comment (lib, "ws2_32.lib") //加载 ws2_32.dll
#define SERVER_PORT 8000 //监听本机8000端口
#define MAX 4096
#define BUF_SIZE 1024
#define Count_buff 10 //最好设置为10,可以最大化带宽利用
//设置需要限制的速率,单位为Mbps
#define RATE 50    // Mbpsvoid empty_stdin() {int c;do {c = getchar();} while (c != '\n' && c != EOF);
}//定义时间差函数,获取时间差
struct timespec diff(struct timespec start, struct timespec end)
{struct timespec temp;temp.tv_sec = end.tv_sec - start.tv_sec;temp.tv_nsec = end.tv_nsec - start.tv_nsec;if(temp.tv_sec < 0){printf("time getting error\n");temp.tv_sec = -temp.tv_sec;temp.tv_nsec = -temp.tv_nsec;}return temp;
}int main(void)
{struct sockaddr_in serveraddr,clientaddr;int sockfd,addrlen,confd,len;char ipstr[128];//   struct timespec time1,time2,temp;struct timespec time1 = {0, 0};struct timespec time2 = {0,0};struct timespec temp = {0,0};  struct timespec temp2 = {0,0}; pid_t pid;//1.socketsockfd = socket(AF_INET,SOCK_STREAM,0);//2.bindbzero(&serveraddr,sizeof(serveraddr));//地址族协议ipv4serveraddr.sin_family = AF_INET;//ip地址serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);serveraddr.sin_port = htons(SERVER_PORT);bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));//3.listenlisten(sockfd,20);//128作为可同时链接的数量上线//4. accept阻塞监听客户端的链接请求addrlen = sizeof(clientaddr);confd = accept(sockfd,(struct sockaddr *)&clientaddr,&addrlen);//如果有客户端连接上服务器,就输出客户端的ip地址和端口号printf("client ip %s\tport %d\n",inet_ntop(AF_INET,(struct sockaddr *)&clientaddr.sin_addr.s_addr,ipstr,sizeof(ipstr)),ntohs(clientaddr.sin_port));int flag=1;while(flag){char fdownload[100] = {0};recv(confd,fdownload,100,0);//接收客户端的下载请求获得文件名if(!strcmp(fdownload,"quit")) break;//客户端输入quit退出程序  else{FILE *fp = fopen(fdownload, "rb"); //以二进制方式打开文件if(fp == NULL){printf("Cannot open file, press any key to exit!\n");break;}struct stat statbuf; //这三行代码获得文件的大小,得到文件字节数stat(fdownload,&statbuf);int size=statbuf.st_size;char t[20];printf("%d \n",size);//文件大小sprintf(t,"%d",size);//转换整数到字符型数据//printf("start transfer\n");send(confd,t,20,0);//把文件大小发给客户端char buffer[BUF_SIZE] = {0}; //缓冲区long nCount,mc=0;long si,sj,m,c;recv(confd,t,4,0);//获得开始传输指令if(t[0]=='o'){printf("start transfer\n");clock_gettime(CLOCK_MONOTONIC,&time1);int n_fread = 0;  //n_fread为读取文件的总次数int n = 0;//计算1s中读取的次数 int times = (RATE*1024*1024)/(Count_buff * BUF_SIZE);long transflag = 1;while(transflag){n = 0;//temp2清0temp2 = diff(time1,time1);
//              printf("n is %d,temp2.tv_sec is %d\n", n, temp2.tv_sec);clock_gettime(CLOCK_MONOTONIC,&time2);
//当传输时间小于1s,并且发送次数小于times时,进行文件传输,而当限制的速率大于瓶颈带宽时,只能跑到带宽限速while((n < times) && (temp2.tv_sec < 1)){
/*一下内容开始发送文件内容 ,因为文件大小会超出发送缓冲区大小,因此在这里循环调用send()函数进行发送,每一次发送nCount个字节,每次读取Count_buff次,每次读取字节大小为BUF_SIZE*/nCount = fread(buffer, Count_buff, BUF_SIZE, fp);n_fread = n_fread + 1;mc=mc+nCount;si=mc*30/size;//这里开始计算传输比例,式字计算顺序不能更改,否则出现数据溢出,而产生错误。m=si-sj;//             printf("%*s|%d%%",30-si,"",(mc*100/size));//在固定位置打印百分数printf("%*s|%d%%",30-si,"",(Count_buff*mc*100/size));//在固定位置打印百分数printf("\r\033["); //退格for(int t=0;t<si+1;t++){printf(">");setbuf(stdout, NULL);}send(confd, buffer, nCount, 0);sj=si;transflag = nCount;n++;}while(temp2.tv_sec < 1){  clock_gettime(CLOCK_MONOTONIC,&temp);temp2 = diff(time2,temp);sleep(0.001);}}printf("transfer success!\n");clock_gettime(CLOCK_MONOTONIC,&time2);temp = diff(time1,time2);//计算文件传输速率,这里的文件传输速率并不是很精准,用的是文件大小除以总文件传输时间double rate = (double) size / (1048576 * (temp.tv_sec + temp.tv_nsec/1000000000)); //计算每次读取文件所需要的时间,即用总时间除以读取总次数double fre = (double) (1000 * temp.tv_sec + temp.tv_nsec/1000000)/n_fread;printf("The frequency of extracting file:%-.4f ms per reading\n",fre);printf("The rate of transmission:%-.4f Mbps\n",rate);printf("Time of Program:%-.4Fs\n",(float)(temp.tv_sec + temp.tv_nsec/1000000000));fclose(fp);}}}close(confd);//close(sockfd);return 0;
}

3.2接收端:

#include <netinet/in.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/stat.h>
#include <ctype.h>
#include <stdlib.h>#define HELLO_WORLD_SERVER_PORT       6666
#define BUFFER_SIZE                   1024
#define FILE_NAME_MAX_SIZE            512
#define BUF_SIZE 10240  #define SERVER_PORT 8000
#define MAXLINE 4096int main(void)
{struct sockaddr_in serveraddr;int confd,len;char ipstr[] = "210.26.118.200";//这是服务器的地址,使用ifconfig来查看char buf[MAXLINE];//1.创建一个socketconfd = socket(AF_INET,SOCK_STREAM,0);//2.初始化服务器地址,指明我要连接哪个服务器bzero(&serveraddr,sizeof(serveraddr));serveraddr.sin_family = AF_INET;inet_pton(AF_INET,ipstr,&serveraddr.sin_addr.s_addr);serveraddr.sin_port = htons(SERVER_PORT);//3.链接服务器connect(confd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));int flag=1,size;char t[20];memset(&t,0,sizeof(t));while(flag){char fdownload[100] = {0};printf("Input filename to download: ");gets(fdownload);if(!strcmp(fdownload,"quit")) break;send(confd,fdownload,100,0);char filename[100] = {0}; //文件名len=recv(confd,t,20,0);size=atoi(t);double sizel;sizel=size/1048576.0;printf("filesize; %.2f MB\n ",sizel);printf("Input filename to save: ");gets(filename);if(!strcmp(filename,"quit")) break;else{FILE *fp = fopen(filename, "wb"); //以二进制方式打开(创建)文件if(fp == NULL){printf("Cannot open file, press any key to exit!\n");break;}send(confd,"o",4,0);char buffer[BUF_SIZE] = {0}; //文件缓冲区long nCount,mc=0;long si,sj,m,c;printf("Start receive!\n");while( (nCount = recv(confd, buffer, BUF_SIZE, 0)) > 0 ){mc=mc+nCount;si=mc*30/size;m=si-sj;printf("%*s|%d%%",30-si,"",(mc*100/size));printf("\r\033["); //退格for(int t=0;t<si+1;t++){printf(">");setbuf(stdout, NULL);}fwrite(buffer, nCount, 1, fp);if(mc==size) break;}printf("Transfer success!\n");fclose(fp);}}
close(confd);
return 0;
}

3.3 实验结果

限速为100Mbps

限速为20Mbps

限速为50Mbps,网卡限速20Mbps

之所以比20Mbps大一丢丢,我觉得是因为测速率的算法太粗糙了。上面都是显示的文件传输99%,其实是传输完的,只不过输出显示并不是很适配。

啊!以上!顺心顺意!!!

参考:
https://blog.csdn.net/qq_43212988/article/details/106901493?&spm=1001.2101.3001.4242
https://blog.csdn.net/weixin_30588907/article/details/99359801
https://www.cnblogs.com/melons/p/5791874.html

Socket套接字的速率控制(linux)相关推荐

  1. linux tcp连接计算机,计算机基础知识——linux socket套接字tcp连接分析

    2016.7.4 今天晚上对项目顶层文件(daemon)进行了分析,对其中的TCP连接进行具体的代码级分析. 1.需求分析 首先得知道我们这里为什么要用TCP连接,我们的整个测试系统是由上位机作为客户 ...

  2. linux下使用fread读socket套接字的注意点

    linux下使用fread读socket套接字的注意点 linux总所周知,一切皆文件.因此我们在读写文件时觉得fread,recv这些可以随便用.下面看看有哪些注意事项呢. 因为C库的文件处理函数较 ...

  3. 【Linux网络编程】网络基础 和 socket套接字 服务器与客户端 详细案例说明

    目录 前言 一.网络编程三要素 1.IP地址 2.通信协议 3.端口号 二.SOCKET套接字 SOCKET概述 SOCKET分类 三.代码实现 1.编程思路 2.建立服务器 服务器完整代码 3.建立 ...

  4. [Linux网络编程]Socket套接字

    socket 的原意是"插座",在计算机通信领域,socket 被翻译为"套接字",它是计算机之间进行通信的一种约定或一种方式.     通过 socket 这 ...

  5. Linux网络编程之Socket套接字

    一.Socket到底是什么 socket 这个英文单词的原意是"插口""插槽", 在网络编程中,它的意思是可以通过插口接入的方式,快速完成网络连接和数据收发.你 ...

  6. alin的学习之路(Linux网络编程:一)(网络模型、帧格式、socket套接字、服务器端实现)

    alin的学习之路(Linux网络编程:一)(网络模型.帧格式.socket套接字.服务器端实现) 1. 协议 协议是一组规则,规定了如何发送数据.通信的双发都需要遵守该规则 2. 网络分层结构模型 ...

  7. 基于UDP协议的socket套接字编程 基于socketserver实现并发的socket编程

    基于UDP协议 的socket套接字编程 1.UDP套接字简单示例 1.1服务端 import socketserver = socket.socket(socket.AF_INET,socket.S ...

  8. Python开发基础----异常处理、socket套接字基础1

    异常处理 错误 程序里的错误一般分为两种: 1.语法错误,这种错误,根本过不了python解释器的语法检测,必须在程序执行前就改正 2.逻辑错误,人为造成的错误,如数据类型错误.调用方法错误等,这些解 ...

  9. linux 服务器间通信,Linux 下的进程间通信:套接字和信号 | Linux 中国

    原标题:Linux 下的进程间通信:套接字和信号 | Linux 中国 学习在 Linux 中进程是如何与其他进程进行同步的. -- Marty Kalin 本篇是 Linux 下(IPC)系列的第三 ...

最新文章

  1. 搭建多语言外文网站需要注意三个细节问题
  2. Firebird日期时间操作
  3. swift SDWebImage 与 UIButton 出现的细节bug 不显示
  4. pip install 安装报错:UnicodeEncodeError: 'ascii' codec can't encode characters in position XX的解决办法...
  5. activemq消息丢失_Kafka or RabbitMQ:消息中间件选型深入分析
  6. C/C++ debug(四)
  7. 红黑树与平衡二叉树_图解“红黑树”原理,一看就明白!
  8. 使用runtime跳转界面
  9. linux分辨率变低了,解决 ubuntu 装N卡驱动后开机分辨率变低 转
  10. python绘制折线图保存_利用python向excel文件写数据并绘制折线图
  11. 深度学习模型知识产权保护怎么做?看看IJCAI 2021这场Workshop说了什么
  12. Unity实现Angry Bird弹弓发射功能
  13. matlab 两个数中取小,matlab中取两个数中的较小值
  14. 资料分享:送你一本《数据结构与算法:Python语言描述》电子书!
  15. 用ChatGPT做嵌入式应用开发
  16. Java操作Word图表
  17. burpsuite破解webshell密码+国内黑阔shell密码收集
  18. 微光医疗/软件测试工程师实习面试
  19. 如何设置鼠标”的速度、滚动方向等配置?
  20. CV | Feature Space Optimization for Semantic Video Segmentation - 基于特征空间优化的视频语义分割

热门文章

  1. Java Calendar使用指南
  2. 关于VMware虚拟机的上网
  3. 个人成长:2021年中随想记
  4. esri geometry-api-java的maven创建
  5. CMS之promotion failedconcurrent mode failure
  6. Linux nginx 会话保持(session)
  7. 机器学习基础——支持向量机
  8. JS中的prototype、__proto__与constructor(图解)
  9. visitor-访问模式
  10. idea如何彻底删除一个项目