如何实现限制上传或下载速度

预备知识

1.gettimeofday函数

作用:

在C语言中可以使用函数gettimeofday()函数来得到时间。它的精度可以达到微妙。

函数原型:

#include<sys/time.h>

int gettimeofday(struct  timeval*tv,struct  timezone *tz )

返回值:

函数执行成功后返回0,失败后返回-1,错误代码存于errno中。

gettimeofday()会把目前的时间用tv 结构体返回,当地时区的信息则放到tz所指的结构中。

timeval结构体定义为:

struct  timeval{
       long  tv_sec;/*秒*/
       long  tv_usec;/*微妙*/
};

timezone结构体定义为:

struct  timezone{
        int tz_minuteswest;/*和greenwich 时间差了多少分钟*/
        int tz_dsttime;/*type of DST correction*/
}

注意:

在gettimeofday()函数中tv或者tz都可以为空。如果为空则就不返回其对应的结构体。

2.nanosleep函数

作用:

挂起调用进程/线程的执行,直到至少经过*req中指定的时间,或者传递触发调用线程中处理程序调用或终止进程的信号。

函数原型:

 #include <time.h>

 int nanosleep(const struct timespec *req, struct timespec *rem);

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

返回值:

成功休眠所请求的间隔后,返回0。如果调用被信号处理程序中断或遇到错误,则返回-1,并设置errno指示错误。

限速功能的实现

限速的关键是睡眠,如果发现当前传输速度超过最大传输速度就让进程睡眠。

传输速度=传输字节数/传输时间

如果当前传输速度>最大传输速度

则 睡眠时间=(当前传输速度/最大传输速度-1)*当前传输时间

1.传输之前,先获取当前时间的秒数和微秒数并进行记录,每当传输1024字节之后,就判断当前的传输速度

	char buf[1024];

    //开始传输之前的时间
    //获取当前时间的秒数和微秒数
	sess->bw_transfer_start_sec = get_time_sec();
	sess->bw_transfer_start_usec = get_time_usec();

	//这是一个循环,所以能实现上传大文件
	while (1) {
		//从数据套接字接收数据
		ret = read(sess->data_fd, buf, sizeof(buf));
		if (ret == -1) {
			if (errno == EINTR) {
				continue;
			} else {
				flag = 2;
				break;
			}
		}
		else if (ret == 0) {
			flag = 0;
			break;
		}

		//读取了一定数据之后需要判断是否限速
		limit_rate(sess, ret, 1);
		//睡醒之后判断是否收到了abor,如果是直接break,其实没有也可以,返回去
		//的时候read会返回-1,因为已经关闭了数据连接套接字了
		if (sess->abor_received) {
			flag = 2;
			break;
		}

		//写入文件中
		if (writen(fd, buf, ret) != ret) {
			flag = 1;
			break;
		}
	}

2.判断是否超速,如果超速,进行休眠

当传输了1024字节之后,获取当前时间,通过流过的时间以及在该时间内传输的字节数计算当前的传输速度。

如果没有超过最大传输速度,说明不需要进行休眠限速,但是也要更新时间,以便下次判断。

如果超过最大传输速度,那么计算应该休眠的时间,睡眠时间=(当前传输速度/最大传输速度-1)*当前传输时间,然后进行休眠。

//限速,计算睡眠时间,第二个参数是当前传输的字节数
void limit_rate(session_t *sess, int bytes_transfered, int is_upload)
{
 	//表明处于数据传输的状态
	sess->data_process = 1;

	// 睡眠时间=(当前传输速度/最大传输速度-1)*当前传输时间;
	long curr_sec = get_time_sec();
	long curr_usec = get_time_usec();

	//流过的时间,当前所用的传输时间
	double elapsed;
	elapsed = (double)(curr_sec - sess->bw_transfer_start_sec);
	//秒+微秒
	elapsed += (double)(curr_usec - sess->bw_transfer_start_usec) / (double)1000000;
	if (elapsed <= (double)0) {//等于0的情况有可能,因为传的太快了
		elapsed = (double)0.01;
	}


	// 计算当前传输速度,传输的量除以传输时间,忽略了传输速度的小数部分
	unsigned int bw_rate = (unsigned int)((double)bytes_transfered / elapsed);

	double rate_ratio;
	//上传
	if (is_upload) {
		//当前速度小于上传速度
		if (bw_rate <= sess->bw_upload_rate_max) {
			// 不需要限速,也需要更新时间
			sess->bw_transfer_start_sec = curr_sec;
			sess->bw_transfer_start_usec = curr_usec;
			return;
		}

		//根据公式进行计算
		rate_ratio = bw_rate / sess->bw_upload_rate_max;
	}
	//下载
	else {
		if (bw_rate <= sess->bw_download_rate_max) {
			//不需要限速 
			sess->bw_transfer_start_sec = curr_sec;
			sess->bw_transfer_start_usec = curr_usec;
			return;
		}

		rate_ratio = bw_rate / sess->bw_download_rate_max;
	}

	//计算睡眠时间
	//睡眠时间=(当前传输速度/最大传输速度-1)*当前传输时间��;
	double pause_time;
	//需要睡眠的时间
	pause_time = (rate_ratio - (double)1) * elapsed;

	//进行睡眠
	nano_sleep(pause_time);

	//更新时间,下一次要开始传输的时间更新为当前时间
	sess->bw_transfer_start_sec = get_time_sec();
	sess->bw_transfer_start_usec = get_time_usec();

}

休眠函数的选择

sleep函数中有说明:

而我们在空闲断开中会使用alarm函数,所以这里就不选择sleep函数,而是选择nanosleep函数。

 

发布了161 篇原创文章 · 获赞 60 · 访问量 7万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览