预备知识
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函数。