using System; using System.Web; namespace SiteCore { public class FrequencyControler { /// /// 访问控制器名称,用于区分其它控制器,支持多个控制器 /// private string Name { get; set; } /// /// 限定时长 /// private int Seconds { get; set; } /// /// 限定次数 /// private int Times { get; set; } public readonly int MAX_TIMES = 100; #region 私有方法 private string SessionNameDatelist { get { return String.Format("fc.{0}.datelist", Name); } } private string SessionNameDatepos { get { return String.Format("fc.{0}.datepos", Name); } } /// /// 取得用于保存每次访问时间点的数组(做队列用) /// /// private long[] GetDateList() { if (HttpContext.Current.Session[SessionNameDatelist] == null) { HttpContext.Current.Session[SessionNameDatelist] = new long[MAX_TIMES]; } return (long[])HttpContext.Current.Session[SessionNameDatelist]; } /// /// 获取时间记录位置,相当于当前队列位置 /// /// private int GetDatepos() { if (HttpContext.Current.Session[SessionNameDatepos] == null) { HttpContext.Current.Session[SessionNameDatepos] = 0; } return (int)HttpContext.Current.Session[SessionNameDatepos]; } /// /// 设置时间记录位置,相当于当前队列位置 /// /// private void SetDatepos(int pos) { HttpContext.Current.Session[SessionNameDatepos] = pos; } #endregion /// /// 构造访问检测器,限定指定时间内最多请求次数 /// /// 名称 /// 限定时间范围(秒数) /// 限定次数 public FrequencyControler(string name, int seconds, int times) { Name = name; Seconds = seconds; Times = times; if (times > MAX_TIMES) throw new Exception("限定次数设置值超出上限!"); } /// /// 记录一次访问,在时间点数组的下一个位置(按最大长度循环覆盖存储) /// public void Record() { long[] ds = GetDateList(); int pos = GetDatepos(); pos = (pos + 1) % ds.Length; ds[pos] = DateTime.Now.Ticks; SetDatepos(pos); } /// /// 判断是否达到访问限制(并默认记录一次请求) /// /// public bool IsTooFrequently() { return IsTooFrequently(true); } /// /// 判断是否达到访问限制(主要外部使用功能) /// /// 是否包含本次请求,为true时,会先记录一次请求再判断 /// public bool IsTooFrequently(bool isRecord) { if (isRecord) Record(); long[] ds = GetDateList(); int pos = GetDatepos(); // 取得之前 限定次数 位置的时间点与最后的时间点比较, // 如果之前的访问次数还没有达到限定次数则忽略 int pre_pos = (pos + ds.Length - Times + 1) % ds.Length; long pre_ticks = ds[pre_pos]; long now_ticks = ds[pos]; // 如果访问的次烤都还没有达到限定次数,pre_ticks 必定为0,所以大于0时才进行比较 if (pre_ticks > 0) { TimeSpan span = new TimeSpan(now_ticks - pre_ticks); if (span.TotalSeconds <= Seconds) { return true; } } return false; } } }