令牌桶C语言代码实现

令牌桶实例

令牌桶三要素

cps 每秒钟传输字节数

burst 令牌桶内最多能传输的字节数,token的最大值

token 令牌的个数

之前是一个令牌(token)对应一个字节,现在将一个token变为一个cps,cps是解码速率,每攒到一个令牌,就token+=cps

如果需要不同的速率,使用不同的令牌桶,将令牌桶存储在一个数组中。

代码

mytbf.h

#ifndef MYTBF__H_
#define MYTBF__H_#define MYTBF_MAX	1024
typedef void mytbf_t;mytbf_t *mytbf_init(int cps,int burst);int mytbf_fetchtoken(mytbf_t * ,int);int mytbf_returntoken(mytbf_t * ,int );int mytbf_destroy(mytbf_t *);#endif

mytbf.c

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>#include "mytbf.h"typedef void (*sighandler_t)(int);//将令牌桶的数据结构存在数组中,由于static修饰,所以未初始化前job[i]是NULL
static struct mytbf_st * job[MYTBF_MAX];
static int inited = 0;
#define MIN(A,B) (A < B ? A : B)
static sighandler_t alrm_handler_save;/*每个token代表一个字节,cps代表解码速率,burst应该是cps的倍数,token=token+cps*/
//这是令牌桶的数据结构,这个数据结构存在数组中
struct mytbf_st 
{int cps;	//每秒钟传输的字节数int burst;	//令牌桶中令牌最大数量int token;	//令牌的个数int pos;	//记录令牌桶在数组的位置下标
};//信号捕捉函数
static void alrm_handler(int s)
{alarm(1);//为数组中的令牌桶中的令牌做累计for(int i = 0;i < MYTBF_MAX; i++){if(job[i] != NULL){job[i]->token += job[i]->cps;//令牌的数量不能超过令牌的最大数量burstif(job[i]->token > job[i]->burst)job[i]->token = job[i]->burst;}}
}
//关闭时钟发送信号,恢复
static void module_unload(void)
{int i;//恢复SIGALRM到之前的功能signal(SIGALRM,alrm_handler_save);//取消时钟发送信号alarm(0);//释放令牌桶for(i = 0;i < MYTBF_MAX;i++){free(job[i]);}}
//第一次发时钟信号的函数,这个函数只执行一次
static void module_load(void)
{//signal的返回值是注册新的行为(alrm_handler)之前的行为alrm_handler_save = signal(SIGALRM,alrm_handler);alarm(1);//注册钩子函数,这个不是函数调用,而是当调用exit的时候才会调用atexit(module_unload);
}//查找数组中空位置下标
static int get_free_pos(void)
{int i = 0;for(i = 0;i < MYTBF_MAX; i++){if(job[i] == NULL)return i;}return -1;
}mytbf_t *mytbf_init(int cps,int burst)
{int pos = 0;struct mytbf_st *me;//在数组中找到空位下标pospos = get_free_pos();if(pos < 0){return NULL;}if( !inited ){module_load();inited = 1;}me = malloc(sizeof(*me));if(me == NULL){return NULL;}//初始化令牌桶结构体成员me->token = 0;me->cps = cps;me->burst = burst;me->pos = pos;//将令牌桶放到数组中job[pos] = me;return me;
}//取令牌
int mytbf_fetchtoken(mytbf_t *ptr ,int size)
{int n;struct mytbf_st *me = ptr;if(size <= 0)return -1;//查看令牌桶中有没有令牌while(me->token <= 0)pause();//当要取的令牌数大于最大令牌数量,给最大令牌数量n = MIN(me->token,size);me->token -=n;return n;}//归还令牌
int mytbf_returntoken(mytbf_t *ptr ,int size)
{struct mytbf_st *me = ptr;if(size <=0 )return -1;me->token +=size;//判断令牌桶中的令牌是否大于令牌的最大数量burstif(me->token > me->burst)me->token = me->burst;return size;
}//销毁令牌桶
int mytbf_destroy(mytbf_t *ptr)
{	//因为mytbf_t 是void类型,转换下struct mytbf_st *me = ptr;job[me->pos] = NULL;free(me);return 0;
}

main.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include "mytbf.h"#define CPS	    10
#define BUFSIZE	1024
#define BURST   100int main(int argc,char**argv)
{int sfd,dfd = 1;int size,errnor;char buf[BUFSIZE];int len,ret,pos;mytbf_t * tbf;if(argc < 2){fprintf(stderr,"Usage ....\n");exit(1);}//初始化令牌桶tbf = mytbf_init(CPS,BURST);//打开要读取的文件do{sfd = open(argv[1],O_RDONLY);if(sfd < 0){if(errno != EINTR){perror("open()");exit(1);}}}while(sfd < 0);while(1){	//从令牌桶中取出BUFSIZE个令牌size = mytbf_fetchtoken(tbf,BUFSIZE);if(size < 0){fprintf(stderr,"mytbf_fetchtoken is error\n");exit(1);}while((len = read(sfd,buf,size)) < 0){if(errno == EINTR)continue;perror("read()");break;}if(len == 0)break;//判断令牌是否用完,因为一个token是一个字节,所以size-len是剩余的字节,也是tokenif(size - len > 0)mytbf_returntoken(tbf,size-len);pos = 0;//使用循环写,写够len个字节的内容while(len > 0){ret = write(dfd,buf+pos,len);if(ret < 0){	//假错,继续写if(ret == EINTR)continue;perror("write()");exit(1);}pos += ret;len -= ret;}}mytbf_destroy(tbf);exit(0);
}

makefile

all:mytbf
mytbf:main.c mytbf.cgcc $^ -o $@
clean:rm *.o mytbf

多线程版本

mytbf.h

#ifndef MYTBF_H__
#define MYTBF_H__//流量控制的实现
#define MYTBF_MAX 1024
typedef void mytbf_t;mytbf_t *mytbf_init(int cps,int burst);int mytbf_fetchtoken(mytbf_t *,int);int mytbf_returntoken(mytbf_t *,int);int mytbf_destory(mytbf_t *);#endif 

mytbf.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>
#include <sys/select.h>
#include "mytbf.h"struct mytbf_st
{int cps;int burst;int pos;int token;pthread_mutex_t mut;	//用于操作令牌时使用的互斥锁pthread_cond_t cond;
};//定时函数,定时sec秒时间
static void select_time(int sec)
{struct timeval tm;tm.tv_sec = sec;tm.tv_usec = 0;//调用select()函数后,程序将阻塞在这里,直到超过指定的等待时间或者有信号中断这个等待。select(0, NULL, NULL, NULL, &tm);
}
//静态初始化用于操作数组的互斥锁
pthread_mutex_t job_mut = PTHREAD_MUTEX_INITIALIZER;
static struct mytbf_st *job[MYTBF_MAX];
static pthread_t tid;
pthread_once_t once = PTHREAD_ONCE_INIT;//这个线程用来将数组中令牌桶中的令牌不断累加
void *thr_alrm(void *args)
{int i;while (1){	//操作令牌桶数组要先加锁pthread_mutex_lock(&job_mut);for(i = 0; i < MYTBF_MAX; i++){if(job[i] != NULL){//操作令牌桶中的令牌时也要加锁pthread_mutex_lock(&job[i]->mut);job[i]->token += job[i]->cps;if (job[i]->token > job[i]->burst)job[i]->token = job[i]->burst;//令牌有了,通知阻塞在等待令牌的线程pthread_cond_broadcast(&job[i]->cond);pthread_mutex_unlock(&job[i]->mut);}}pthread_mutex_unlock(&job_mut);//定时1s时间select_time(1);}}void mode_unload()
{//不会立即终止tid线程,而是发送一个取消请求给tid线程。pthread_cancel(tid);pthread_join(tid, NULL);int i;for(i = 0; i < MYTBF_MAX; i++){free(job[i]);}
}void mode_load()
{int err;//创建一个线程按照定时时间给令牌桶累加err = pthread_create(&tid, NULL, thr_alrm, NULL);if (err < 0){fprintf(stderr, "pthread_create():%s", strerror(errno));exit(1);}atexit(mode_unload);
}//查找数组中空位置的下标
static int get_free_pos()
{int i;for(i = 0; i < MYTBF_MAX; i++){if(job[i] == NULL)return i;}return -1;
}mytbf_t *mytbf_init(int cps,int burst)
{int pos;struct mytbf_st *me = malloc(sizeof(struct mytbf_st));if (me == NULL){return NULL;}//使mod_load这个函数只会执行一次pthread_once(&once, mode_load);me->cps = cps;me->burst = burst;me->token = 0;pthread_mutex_init(&me->mut, NULL);pthread_cond_init(&me->cond, NULL);//操作令牌桶数组前先加锁pthread_mutex_lock(&job_mut);pos = get_free_pos();if (pos < 0){free(me);pthread_mutex_unlock(&job_mut);return NULL;}me->pos = pos;job[me->pos] = me;pthread_mutex_unlock(&job_mut);return me;
}static int mymin(int a, int b)
{return a < b ? a : b;
}int mytbf_fetchtoken(mytbf_t *me,int size)
{struct mytbf_st *ptr = me;//操作令牌桶中的令牌先加锁pthread_mutex_lock(&ptr->mut);if (ptr->token <= 0)//没有令牌就先阻塞在条件变量上pthread_cond_wait(&ptr->cond, &ptr->mut);int n;n = mymin(ptr->token, size);ptr->token -= n;pthread_mutex_unlock(&ptr->mut);return n;
}int mytbf_returntoken(mytbf_t *me,int n)
{struct mytbf_st *ptr = me;//操作令牌桶中的令牌先加锁pthread_mutex_lock(&ptr->mut);ptr->token += n;if (ptr->token >= ptr->burst)ptr->token = ptr->burst;//通知阻塞在条件变量上的线程pthread_cond_broadcast(&ptr->cond);pthread_mutex_unlock(&ptr->mut);return 0;
}int mytbf_destory(mytbf_t *me)
{struct mytbf_st *ptr = me;pthread_mutex_lock(&job_mut);job[ptr->pos] = NULL;pthread_mutex_unlock(&job_mut);pthread_mutex_destroy(&ptr->mut);pthread_cond_destroy(&ptr->cond);free(me);return 0;
}

main.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include "mytbf.h"#define CPS	    10
#define BUFSIZE	1024
#define BURST   100int main(int argc, char **argv)
{int fd;mytbf_t *tbf;if(argc < 2){fprintf(stderr,"Usage:\n");exit(1);}//初始化令牌桶中的令牌数量为BURST,速率为CPStbf = mytbf_init(CPS, BURST);if(tbf == NULL){fprintf(stdout, "mytbfInit() false!\n");exit(1);}do{	fd = open(argv[1],O_RDONLY);if (fd < 0){if (errno != EINTR){perror("open()");exit(1);}}}while(fd < 0);size_t len = 0;size_t pos = 0;size_t ret = 0;int size;int count = 0;char buf[BUFSIZE];while (1){//取BUFSIZE个令牌size = mytbf_fetchtoken(tbf, BUFSIZE);if (size < 0){fprintf(stderr, "mytbfFetchToken():%s\n", strerror(-size));exit(1);}while ((len = read(fd, buf, size)) < 0){if (errno == EINTR)continue;perror("read()");break;}//len为0说明读到文件末尾if (len == 0)break;//判断令牌是否使用完,归还剩余的令牌,因为1个令牌代表1个字节if (size - len > 0)mytbf_returntoken(tbf, size - len);pos = 0;while (len > 0){ret = write(1, buf + pos, len);if (ret < 0){perror("write()");exit(1);}pos += ret;len -= ret;}}close(fd);mytbf_destory(tbf);exit(0);
}

makefile

all:mytbf
mytbf:main.c mytbf.cgcc $^ -o $@ -pthread
clean:rm *.o mytbf

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/49703.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

npm install 安装依赖,报错 Host key verification failed

设置 git 的身份和邮箱 git config --global user.name "你的名字" > 用户名 git config --global user.email “你的邮箱" > 邮箱进入 > 用户 > [你的用户名] > .ssh文件夹下,删除 known_hosts 文件即可 进入之后有可能会看到 known_hosts…

android外卖点餐界面(期末作业)

效果展示&#xff1a; AndroidMainFest.xml <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.com/tools"><a…

Rancher-RKE2-安装流程

一、什么是rke2&#xff1f; 1.rke2是Rancher的下一代k8s发行版&#xff0c; 二、与rke的不同 1.重要的是&#xff0c;RKE2 不像 RKE1 那样依赖 Docker。RKE1 利用 Docker 来部署和管理控制平面组件以及 Kubernetes 的容器运行时间。RKE2 将控制平面组件作为静态 pod 启动&…

前端面试:【网络协议与性能优化】提升Web应用性能的策略

嗨&#xff0c;亲爱的Web开发者&#xff01;构建高性能的Web应用是每个开发者的梦想。本文将介绍一些性能优化策略&#xff0c;包括资源加载、懒加载和CDN等&#xff0c;以帮助你提升Web应用的性能。 1. 性能优化策略&#xff1a; 压缩资源&#xff1a; 使用Gzip或Brotli等压缩…

Vue使用Element的表格Table显示树形数据,多选框全选无法选中全部节点

使用Element的组件Table表格&#xff0c;当使用树形数据再配合上多选框&#xff0c;如下&#xff1a; 会出现一种问题&#xff0c;点击左上方全选&#xff0c;只能够选中一级树节点&#xff0c;子节点无法被选中&#xff0c;如图所示&#xff1a; 想要实现点击全选就选中所有的…

IDEA中导出Javadoc遇到的GBK编码错误的解决思路和应用

IDEA中导出Javadoc遇到的GBK编码错误的解决思路和应用 ​ 当我们在导出自己写的项目的api文档的时候呢&#xff0c;有的时候会出现以下问题&#xff1a;也就是GBK编码错误不可导出 错误描述&#xff1a;编码GBK的不可映射字符无法导出&#xff0c;可以看出这是我们自己写的中文…

Ansible学习笔记(二)

3.ansible的使用示例&#xff08;playbook&#xff09; 1.创建mysql 账户和mysql 组的 playbook ---#create mysql user and group - hosts: allremote_user: roottasks:- name: create groupgroup: namemysql systemyes gid306- name: create useruser: namemysql systemyes…

vue 复制文本

一个常用的库就是 clipboard.js&#xff0c;它可以帮助您实现跨浏览器的复制到剪贴板功能 首先&#xff0c;安装 clipboard.js&#xff1a; cnpm install clipboard 创建一个 Vue 组件并使用 clipboard.js&#xff1a; <template><div><input v-model"…

webpack 从入门到放弃!

webpack webpack于2012年3月10号诞生&#xff0c;作者是Tobias(德国)。参考GWT(Google Web Toolkit)的code splitting功能在webpack中进行实现。然后在2014年Instagram团队分享性能优化时&#xff0c;提出使用webpack的code splitting特性从而大火。 现在webpack的出现模糊了任…

快速提高写作生产力——使用PicGo+Github搭建免费图床,并结合Typora

文章目录 简述PicGo下载PicGo获取Token配置PicGo结合Typora总结 简述PicGo PicGo: 一个用于快速上传图片并获取图片 URL 链接的工具 PicGo 本体支持如下图床&#xff1a; 七牛图床 v1.0腾讯云 COS v4\v5 版本 v1.1 & v1.5.0又拍云 v1.2.0GitHub v1.5.0SM.MS V2 v2.3.0-b…

drools8尝试(加单元测试)

drools8的maven模板项目里没有单元测试, 相比而言drools7有个非常好的test senorios 那就自己弄一个 文件是.http后缀的,写了个简单的例子如下 //测试交通违章 POST http://localhost:8080/Traffic Violation accept: application/json Content-Type: application/json{&q…

LeetCode两数之和

给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返回…

前端面试:【Redux】状态管理的精髓

嘿&#xff0c;亲爱的Redux探险家&#xff01;在前端开发的旅程中&#xff0c;有一个强大的状态管理工具&#xff0c;那就是Redux。Redux是一个状态容器&#xff0c;它以一种可预测的方式管理应用的状态&#xff0c;通过Store、Action、Reducer、中间件和异步处理等核心概念&am…

C++系列-类对象作为类成员

类对象作为类成员 类中的成员可以是另一个类的对象。该成员成为对象成员当其它类的对现作为本类的成员&#xff0c;先构造其它类对象&#xff0c;再构造本身。当其它类的对现作为本类的成员&#xff0c;先析构自身&#xff0c;再析构其它类对象。 code:#include<iostream&g…

玩转单元测试之cppmockfree

引言 前文我们已经讲解了gmock的基本语法,但是gmock只能mock虚函数,如果要mock非虚成员函数、静态成员函数、全局函数、重载函数、模板函数以及其他依赖库的函数时,gmock就很难实现。而cppmockfree可以支持这些函数的mock。 快速入门 1. mock样例 1.1 全局函数 // gloa…

代码pytorch-adda-master跑通记录

前言 最近在学习迁移学习&#xff0c;ADDA算法&#xff0c;由于嫌自己写麻烦&#xff0c;准备先跑通别人的代码。 代码名称&#xff1a;pytorch-adda-master 博客&#xff1a;https://www.cnblogs.com/BlairGrowing/p/17020378.html github地址&#xff1a;https://github.com…

数据备份(手动备份与自动备份)

目录 1. 手动备份 1.使用Windows ServerBackup管理控制台手动备份 2.使用命令行工具手动备份 2. 自动备份 3 备份设置 1.普通备份性能 2.快速备份性能 3.自定义 1. 手动备份 案例9.1某公司有一台WindowsServer2016文件服务器&#xff0c;公司的很多重要文件都保存在新加卷…

恢复NuGet包_解决:System.BadImageFormatException:无法加载文件或程序集

C#工程 主要是开发了一个 web api接口&#xff0c;这个工程源码去年还可以的&#xff0c;今年换了一个电脑打开工程就报错。 错误提示如下&#xff1a; 在 Microsoft.CodeAnalysis.CSharp.CommandLine.Program.Main(String[] args) Test1 System.BadImageFormatEx…

一、数据库基础

数据库 一、数据库基础 1、一些概念 数据库&#xff1a;数据库&#xff08;DataBase &#xff0c;简称DB&#xff09;&#xff0c;就是信息的集合。数据库是由数据库管理系统管理的数据的集合&#xff1b;数据库管理系统&#xff1a;简称DBMS 。是一种操纵和管理数据库的大型…

android核绑定cpuset配置与检测进程所在核cpuset方法

一、开机阶段 开机有如下阶段。抛开开机动画需要的audio、surfaceflinger等进程&#xff0c;大部分android程序是在/data分区加载完整以后开始加载。所以cpuset的配置可以在 post-fs-data之后。注意&#xff0c;init.rc的不同阶段脚本都可能覆盖前面配置的cpuset。配置好检查与…