使用c语言实现传输的流量控制

8 篇文章 1 订阅

在linux 环境下,利用令牌实现文件的的读取,并把相应的内容写入终端。

在这个过程中通过令牌的机制来实现流量的控制,并且在程序执行的过程中要考虑到执行过程中被打断的情况,同时不能使得数据出现丢失

main.c

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include "sig_token.h"
#define CPS 10 //每隔一秒钟可以获得10个字符的传输权限,定义每个token每次传输一个字符。
#define BURST 100//令牌中最多可以有100个字符
#define MAXSIZE 5 //自己对传输的需要,这里为5个字符
int main(int argc,char **argv){
	char buff[MAXSIZE];
	if(argc < 2){
		fprintf(stderr,"usage...");
		exit(1);
	}
	int sfd,ret,len,path=0,size;
	mtoken *mytoken;
	sfd = open(argv[1],O_RDONLY);
	if(sfd < 0){
		exit(1);
	}
	
	mytoken = mytoken_init(CPS,BURST);//初始化mytoken cps=10 brust=100,token=0 
	if(mytoken == NULL){
	
		exit(1);

	}
	
	while(1){
		size = mytoken_fetch(mytoken,MAXSIZE);//申请token,准备传输数据。
		
		
		if(size < 0){
			exit(0);
		}
		len = read(sfd,buff,size);//在获得相应的令牌之后,读取相应的字符个数
		if(len < 0){
			break;

		}
		if(len == 0){
			break;

		}
		if(size - len > 0){
			mytoken_return(mytoken,size-len);
		}
		path = 0;
		
		while(len>0){
			ret = write(1,buff+path,len);//写入终端
			if(ret < 0){
			
				break;
			}
			

		
			len -= ret;//为了防止某些中断打断了向终端写入数据,而产生的数据丢失,这样数据必须传输完。
			path+=ret;
		}


	}
	close(sfd);
	mytoken_destory(mytoken);


}


sig_token.c

#include <stdio.h>
#include <stdlib.h>
#include "sig_token.h"
#include <errno.h>
#include <signal.h>
#include <unistd.h>
typedef void (*sighandler_t)(int);
static sighandler_t sighandler_save;
#define MTOKEN_MAX 1024
//为了不让用户看到我的数据结构,我把mtoken定义在 .c 文件中
struct mtoken{
	int cps;//每个信号的到来,就+10
	int burst;//最多到100
	int token;//令牌数
	int pos;//记录该mtoken在job中的位置
};
static int init=0;
static struct mtoken* job[MTOKEN_MAX];//假设最多可以又1024个mtoken
static int get_index(void){
	//该函数用于获得malloc到的mtoken需要存在的位置
	
	int i;
	for(i=0;i<MTOKEN_MAX;i++){
		if(job[i] == NULL){
		
			return i;
		}
	}
	return -1;
}
static void signal_handler(int s){
	//信号相应函数
	alarm(1);
	int i;
	for(i=0;i<MTOKEN_MAX;i++){
		if(job[i] != NULL){
		
			job[i]->token +=job[i]->cps;
			if(job[i]->token >job[i]->burst){
				job[i]->token = job[i]->burst;
			}
		}

	}
}
static void module_unload(void){
	//在module_load函数发生故障的时候,调用,用于free掉存放mtoken 的指针数组job
	signal(SIGALRM,sighandler_save);
	alarm(0);
	int i;
	for(i=0;i<MTOKEN_MAX;i++){
		free(job[i]);
	}
}
static void module_load(void){
	//发信号的函数,
	sighandler_save = signal(SIGALRM,signal_handler);
	alarm(1);
	init = 1;
	atexit(module_unload);
}

mtoken *mytoken_init(int cps,int burst){
	struct mtoken * me;
	int pos;
	//为了这个过程中调用mytoken_init 函数只会发生一次module(防止alarm冲突)设置一个全局变量init来管控
	if(!init){
		module_load();
	}
	pos = get_index();
	if(pos < 0){
		return NULL;
	}
	me = malloc(sizeof(*me));
	if(me == NULL){
		return me;
	}
	me->cps = cps;
	me->burst = burst;
	me->token = 0;
	me->pos = pos;
	job[pos] = me;
	return me;
}
int mytoken_return(mtoken *mytoken,int size){
	//归还token的函数,在实际使用token,比申请的少的时候
	struct mtoken *metoken = mytoken;
	if(size <= 0){
		return -EINVAL;
	}
	metoken->token +=size;

	if(metoken->token > metoken->burst){
		metoken->token = metoken->burst;
	}
	
	return 0;
}
int mytoken_fetch(mtoken *mytoken,int size){
	//申请每次能够传输的字符数,
	struct mtoken *metoken = mytoken;
	while(!metoken->token){
		pause();
	}
	if(metoken->token >=size){
		metoken->token -= size;
		return size;
	}
	else{
		size = metoken->token;
		metoken->token = 0;
		return size;
	}



}
int mytoken_destory(mtoken *ptr){
	//释放mtoken
	struct mtoken *metoken = ptr;
	job[metoken->pos] = NULL;
	free(ptr);
	printf("free\n");
	return 0;
}

sig_token.h

#ifndef SIG_TOKEN_H__
#define SIG_TOKEN_H__
typedef void mtoken;


mtoken *mytoken_init(int cps,int burst);
int mytoken_fetch(mtoken *,int);
int mytoken_return(mtoken *,int);
int mytoken_destory(mtoken*);
#endif

使用signal+alarm函数是不安全的(在程序的执行过程中,如果我从终端发送while true ;kill -ALRM **进程号**;done,会使得流量控制失效),所以我使用sigsaction +setitimer函数代替,改进了程序,使得程序只接受内核的时间,通过内核的发送的信号进行流量控制。

程序如下(只要改sig_token.c)

#include <stdio.h>
#include <stdlib.h>
#include "sig_token.h"
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>
// typedef void (*sighandler_t)(int);
// static sighandler_t sighandler_save;
static struct sigaction sigaction_save;
#define MTOKEN_MAX 1024
//为了不让用户看到我的数据结构,我把mtoken定义在 .c 文件中
struct mtoken{
	int cps;//每个信号的到来,就+10
	int burst;//最多到100
	int token;//令牌数
	int pos;//记录该mtoken在job中的位置
};
static int init=0;
static struct mtoken* job[MTOKEN_MAX];//假设最多可以又1024个mtoken
static int get_index(void){
	//该函数用于获得malloc到的mtoken需要存在的位置
	
	int i;
	for(i=0;i<MTOKEN_MAX;i++){
		if(job[i] == NULL){
		
			return i;
		}
	}
	return -1;
}
static void sigaction_handler(int s,siginfo_t *info,void *nouse){
	//信号相应函数
	if(info->si_code !=SI_KERNEL)
		return ;
		struct itimerval itv;
	itv.it_interval.tv_sec = 1;
	itv.it_interval.tv_usec = 0;
	itv.it_value.tv_sec = 1;
	itv.it_value.tv_usec = 0;
	setitimer(ITIMER_REAL,&itv,NULL);
	int i;
	
	for(i=0;i<MTOKEN_MAX;i++){
		if(job[i] != NULL){
		
			job[i]->token +=job[i]->cps;
			if(job[i]->token >job[i]->burst){
				job[i]->token = job[i]->burst;
			}
		}

	}
}
static void module_unload(void){
	//在module_load函数发生故障的时候,调用,用于free掉存放mtoken 的指针数组job
	// signal(SIGALRM,sighandler_save);
	// alarm(0);
	// struct sigaction sa;
	// sigemptyset(&sa.sa_mask);
	// sa.sa_sigaction = sigaction_handler;
	// sa.sa_flags = SA_SIGINFO;
	sigaction(SIGALRM,&sigaction_save,NULL);
	struct itimerval itv;
	itv.it_interval.tv_sec = 0;
	itv.it_interval.tv_usec = 0;
	itv.it_value.tv_sec = 0;
	itv.it_value.tv_usec = 0;
	setitimer(ITIMER_REAL,&itv,NULL);
	int i;
	for(i=0;i<MTOKEN_MAX;i++){
		free(job[i]);
	}
}
static void module_load(void){
	//发信号的函数,
	//sighandler_save = signal(SIGALRM,signal_handler);
	//alarm(1);
	struct sigaction sa;
	sigemptyset(&sa.sa_mask);
	sa.sa_sigaction = sigaction_handler;
	sa.sa_flags = SA_SIGINFO;
	sigaction(SIGALRM,&sa,&sigaction_save);
	struct itimerval itv;
	itv.it_interval.tv_sec = 1;
	itv.it_interval.tv_usec = 0;
	itv.it_value.tv_sec = 1;
	itv.it_value.tv_usec = 0;
	setitimer(ITIMER_REAL,&itv,NULL);
	init = 1;
	atexit(module_unload);
}

mtoken *mytoken_init(int cps,int burst){
	struct mtoken * me;
	int pos;
	//为了这个过程中调用mytoken_init 函数只会发生一次module(防止alarm冲突)设置一个全局变量init来管控
	if(!init){
		module_load();
	}
	pos = get_index();
	if(pos < 0){
		return NULL;
	}
	me = malloc(sizeof(*me));
	if(me == NULL){
		return me;
	}
	me->cps = cps;
	me->burst = burst;
	me->token = 0;
	me->pos = pos;
	job[pos] = me;
	return me;
}
int mytoken_return(mtoken *mytoken,int size){
	//归还token的函数,在实际使用token,比申请的少的时候
	struct mtoken *metoken = mytoken;
	if(size <= 0){
		return -EINVAL;
	}
	metoken->token +=size;

	if(metoken->token > metoken->burst){
		metoken->token = metoken->burst;
	}
	
	return 0;
}
int mytoken_fetch(mtoken *mytoken,int size){
	//申请每次能够传输的字符数,
	struct mtoken *metoken = mytoken;
	while(!metoken->token){
		pause();
	}
	if(metoken->token >=size){
		metoken->token -= size;
		return size;
	}
	else{
		size = metoken->token;
		metoken->token = 0;
		return size;
	}



}
int mytoken_destory(mtoken *ptr){
	//释放mtoken
	struct mtoken *metoken = ptr;
	job[metoken->pos] = NULL;
	free(ptr);
	printf("free\n");
	return 0;
}

写的没有那么详细,请见谅,如有疑问欢迎留言或者邮件755465928@qq.com.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值