Socket套接字的速率控制(linux)
發(fā)送端系統(tǒng):ubuntu18.04
接收端系統(tǒng):ubuntu18.04
最近要做一些socket的實驗,我對socket也不大了解,不過socket還算是比較好學(xué)的,算是在應(yīng)用層和傳輸層中間,給你提供了調(diào)用了傳輸協(xié)議的api,還是很友好的哦!
哦吼!我要對socket發(fā)送文件的速率進(jìn)行限制,想要把文件傳輸速率限制到想要設(shè)置的速率。大概原理如下:
一、主要原理
比如說,我要把文件的傳輸速率限制到10Mbps,他等同于,在一秒鐘傳輸10Mbit的內(nèi)容。所以我們需要定時器+文件傳輸限制。大概就這兩部分。
socket傳輸文件可以看一下這篇文章:Linux下基于TCP的簡易文件傳輸(socket編程)
1.1 定時器
定時器的話采用linux C語言中的時間函數(shù)clock_gettime,函數(shù)原型如下:
int clock_gettime(clockid_t clk_id, struct timespec *tp);其中,cld_id類型四種:
a、CLOCK_REALTIME:系統(tǒng)實時時間,隨系統(tǒng)實時時間改變而改變
b、CLOCK_MONOTONIC,從系統(tǒng)啟動這一刻起開始計時,不受系統(tǒng)時間被用戶改變的影響
c、CLOCK_PROCESS_CPUTIME_ID,本進(jìn)程到當(dāng)前代碼系統(tǒng)CPU花費的時間
d、CLOCK_THREAD_CPUTIME_ID,本線程到當(dāng)前代碼系統(tǒng)CPU花費的時間
其中,timespec結(jié)構(gòu)包括:
struct timespec { time_t tv_sec; /* 秒*/ long tv_nsec; /* 納秒*/ };1.2 限制文件傳輸大小
我們采用fread函數(shù)傳輸文件,其傳輸過程的文件大小是可以指定的,函數(shù)原型如下:
size_t fread( void *buffer, size_t size, size_t count, FILE *stream )函數(shù)從輸入文件(數(shù)據(jù)流)中讀取size*count字節(jié) 存放到buffer中,并返回內(nèi)容數(shù)量
2 功能描述
不同文章描述的發(fā)送端和接收端還有客戶端和服務(wù)器端不一致,最開始看的很糊涂,這里就只用發(fā)送端和接收端描述。
功能:可以實現(xiàn)socket文件傳輸?shù)乃俾士刂?#xff0c;并輸出socket程序運行時間,輸出抓取文件的速率。我測試了好多次,情況大概如下:
2.1 文件傳輸速率設(shè)置超過瓶頸帶寬時
也就是RATE(設(shè)置的限速)大于客戶端與接收端的bottleneck bandwidth(瓶頸帶寬)的時候,會產(chǎn)生段錯誤,我也不是很清楚為什么會這樣。
關(guān)于如何得到瓶頸帶寬的大小,可以用iperf對帶寬進(jìn)行測量,iperf命令可以參考:iperf命令詳解
但是對網(wǎng)卡進(jìn)行限速時就不會有這樣的情況,比如說,我把網(wǎng)卡限速為100Mbps,即限制出網(wǎng)卡流量速率為100Mbps,傳輸過程的瓶頸帶寬為800Mbps,這時候?qū)ocket文件傳輸設(shè)置為200Mbps,那么能夠得到的socket文件的傳輸速率就為100Mbps,因為網(wǎng)卡的限速啊。
2.2 測量瓶頸帶寬
在不限制速率的情況下可以測試瓶頸帶寬,不過需要刪掉“定時器”,讓它傳就可以了,不用限時。比較耗費帶寬資源。而且程序需要更改。不如直接用iperf,哈哈哈!但是如果要自己做測量帶寬的工具的話,可以作一下參考。呃!可能也沒啥用[捂臉笑]!!!
3 編程
在這篇文章的基礎(chǔ)上改動的,所以可能在輸出的時候不是很好看。呃,太懶了,不太想改了…太難了…
3.1 發(fā)送端:
待傳輸文件需要和發(fā)送端代碼(可執(zhí)行文件)放在同一個文件夾下,因為沒有規(guī)定路徑
#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 //監(jiān)聽本機8000端口 #define MAX 4096 #define BUF_SIZE 1024 #define Count_buff 10 //最好設(shè)置為10,可以最大化帶寬利用 //設(shè)置需要限制的速率,單位為Mbps #define RATE 50 // Mbpsvoid empty_stdin() {int c;do {c = getchar();} while (c != '\n' && c != EOF); }//定義時間差函數(shù),獲取時間差 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));//地址族協(xié)議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作為可同時鏈接的數(shù)量上線//4. accept阻塞監(jiān)聽客戶端的鏈接請求addrlen = sizeof(clientaddr);confd = accept(sockfd,(struct sockaddr *)&clientaddr,&addrlen);//如果有客戶端連接上服務(wù)器,就輸出客戶端的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"); //以二進(jìn)制方式打開文件if(fp == NULL){printf("Cannot open file, press any key to exit!\n");break;}struct stat statbuf; //這三行代碼獲得文件的大小,得到文件字節(jié)數(shù)stat(fdownload,&statbuf);int size=statbuf.st_size;char t[20];printf("%d \n",size);//文件大小sprintf(t,"%d",size);//轉(zhuǎn)換整數(shù)到字符型數(shù)據(jù)//printf("start transfer\n");send(confd,t,20,0);//把文件大小發(fā)給客戶端char buffer[BUF_SIZE] = {0}; //緩沖區(qū)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為讀取文件的總次數(shù)int n = 0;//計算1s中讀取的次數(shù) 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); //當(dāng)傳輸時間小于1s,并且發(fā)送次數(shù)小于times時,進(jìn)行文件傳輸,而當(dāng)限制的速率大于瓶頸帶寬時,只能跑到帶寬限速while((n < times) && (temp2.tv_sec < 1)){ /*一下內(nèi)容開始發(fā)送文件內(nèi)容 ,因為文件大小會超出發(fā)送緩沖區(qū)大小,因此在這里循環(huán)調(diào)用send()函數(shù)進(jìn)行發(fā)送,每一次發(fā)送nCount個字節(jié),每次讀取Count_buff次,每次讀取字節(jié)大小為BUF_SIZE*/nCount = fread(buffer, Count_buff, BUF_SIZE, fp);n_fread = n_fread + 1;mc=mc+nCount;si=mc*30/size;//這里開始計算傳輸比例,式字計算順序不能更改,否則出現(xiàn)數(shù)據(jù)溢出,而產(chǎn)生錯誤。m=si-sj;// printf("%*s|%d%%",30-si,"",(mc*100/size));//在固定位置打印百分?jǐn)?shù)printf("%*s|%d%%",30-si,"",(Count_buff*mc*100/size));//在固定位置打印百分?jǐn)?shù)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);//計算文件傳輸速率,這里的文件傳輸速率并不是很精準(zhǔn),用的是文件大小除以總文件傳輸時間double rate = (double) size / (1048576 * (temp.tv_sec + temp.tv_nsec/1000000000)); //計算每次讀取文件所需要的時間,即用總時間除以讀取總次數(shù)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";//這是服務(wù)器的地址,使用ifconfig來查看char buf[MAXLINE];//1.創(chuàng)建一個socketconfd = socket(AF_INET,SOCK_STREAM,0);//2.初始化服務(wù)器地址,指明我要連接哪個服務(wù)器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.鏈接服務(wù)器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"); //以二進(jìn)制方式打開(創(chuàng)建)文件if(fp == NULL){printf("Cannot open file, press any key to exit!\n");break;}send(confd,"o",4,0);char buffer[BUF_SIZE] = {0}; //文件緩沖區(qū)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 實驗結(jié)果
限速為100Mbps
限速為20Mbps
限速為50Mbps,網(wǎng)卡限速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
總結(jié)
以上是生活随笔為你收集整理的Socket套接字的速率控制(linux)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NS3-命令行参数
- 下一篇: iOS 13 教程:如何打开 iPhon