첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
본 블로그는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 블로그 콘텐츠 향상을 위해 쓰여집니다.

728x90
반응형
728x170

ThrottleStream.cs
다운로드

▶ ThrottleStream.cs

using System;
using System.IO;
using System.Threading;

namespace TestProject
{
    /// <summary>
    /// 스로틀 스트림
    /// </summary>
    public class ThrottleStream : Stream
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 무한 바이트 카운트
        /// </summary>
        /// <remarks>초당 전송할 수 있는 무한 바이트 수를 지정하는 상수</remarks>
        private const long INFINITE_BYTE_COUNT = 0;

        /// <summary>
        /// 기본 스트림
        /// </summary>
        private Stream baseStream;

        /// <summary>
        /// 초당 최대 바이트 카운트
        /// </summary>
        /// <remarks>기본 스트림을 통해 전송될 수 있는 초당 최대 바이트 카운트</remarks>
        private long maximumByteCountPerSecond;

        /// <summary>
        /// 마지막 바이트 카운트
        /// </summary>
        /// <remarks>마지막 스로틀 이후에 전송된 바이트 카운트</remarks>
        private long lastByteCount;

        /// <summary>
        /// 마지막 시작 시간
        /// </summary>
        /// <remarks>마지막 스로틀 시작 시간 (밀리 초)</remarks>
        private long lastStartTime;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Protected

        #region 현재 시간 - CurrentTime

        /// <summary>
        /// 현재 시간
        /// </summary>
        /// <remarks>밀리초 단위</remarks>
        protected long CurrentTime
        {
            get
            {
                return Environment.TickCount;
            }
        }

        #endregion
        #region 초당 최대 바이트 카운트 - MaximumByteCountPerSecond

        /// <summary>
        /// 초당 최대 바이트 카운트
        /// </summary>
        /// <remarks>기본 스트림을 통해 전송될 수 있는 초당 최대 바이트 카운트</remarks>
        public long MaximumByteCountPerSecond
        {
            get
            {
                return this.maximumByteCountPerSecond;
            }
            set
            {
                if(this.maximumByteCountPerSecond != value)
                {
                    this.maximumByteCountPerSecond = value;

                    Reset();
                }
            }
        }

        #endregion

        #region 읽기 가능 여부 - CanRead

        /// <summary>
        /// 읽기 가능 여부
        /// </summary>
        public override bool CanRead
        {
            get
            {
                return this.baseStream.CanRead;
            }
        }

        #endregion
        #region 탐색 가능 여부 - CanSeek

        /// <summary>
        /// 탐색 가능 여부
        /// </summary>
        public override bool CanSeek
        {
            get
            {
                return this.baseStream.CanSeek;
            }
        }

        #endregion
        #region 쓰기 가능 여부 - CanWrite

        /// <summary>
        /// 쓰기 가능 여부
        /// </summary>
        public override bool CanWrite
        {
            get
            {
                return this.baseStream.CanWrite;
            }
        }

        #endregion
        #region 길이 - Length

        /// <summary>
        /// 길이
        /// </summary>
        public override long Length
        {
            get
            {
                return this.baseStream.Length;
            }
        }

        #endregion
        #region 위치 - Position

        /// <summary>
        /// 위치
        /// </summary>
        public override long Position
        {
            get
            {
                return this.baseStream.Position;
            }
            set
            {
                this.baseStream.Position = value;
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - ThrottleStream(baseStream, maximumByteCountPerSecond)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="baseStream">기본 스트림</param>
        /// <param name="maximumByteCountPerSecond">초당 최대 바이트 카운트</param>
        public ThrottleStream(Stream baseStream, long maximumByteCountPerSecond)
        {
            if(baseStream == null)
            {
                throw new ArgumentNullException("baseStream");
            }

            if(maximumByteCountPerSecond < 0)
            {
                throw new ArgumentOutOfRangeException("maximumByteCountPerSecond", maximumByteCountPerSecond, "초당 최대 바이트 카운트는 음수가 될 수 없습니다.");
            }

            this.baseStream                = baseStream;
            this.maximumByteCountPerSecond = maximumByteCountPerSecond;
            this.lastStartTime             = CurrentTime;
            this.lastByteCount             = 0;
        }

        #endregion
        #region 생성자 - ThrottleStream(baseStream)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="baseStream">기본 스트림</param>
        public ThrottleStream(Stream baseStream) : this(baseStream, ThrottleStream.INFINITE_BYTE_COUNT)
        {
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 버퍼 비우기 - Flush()

        /// <summary>
        /// 버퍼 비우기
        /// </summary>
        public override void Flush()
        {
            this.baseStream.Flush();
        }

        #endregion
        #region 읽기 - Read(bufferArray, offset, count)

        /// <summary>
        /// 읽기
        /// </summary>
        /// <param name="bufferArray">버퍼 배열</param>
        /// <param name="offset">오프셋</param>
        /// <param name="count">카운트</param>
        /// <returns>실제 읽은 바이트 카운트</returns>
        public override int Read(byte[] bufferArray, int offset, int count)
        {
            Throttle(count);

            return this.baseStream.Read(bufferArray, offset, count);
        }

        #endregion
        #region 탐색하기 - Seek(offset, seekOrigin)

        /// <summary>
        /// 탐색하기
        /// </summary>
        /// <param name="offset">오프셋</param>
        /// <param name="seekOrigin">탐색 기준</param>
        /// <returns>위치</returns>
        public override long Seek(long offset, SeekOrigin seekOrigin)
        {
            return this.baseStream.Seek(offset, seekOrigin);
        }

        #endregion
        #region 길이 설정하기 - SetLength(value)

        /// <summary>
        /// 길이 설정하기
        /// </summary>
        /// <param name="value">값</param>
        public override void SetLength(long value)
        {
            this.baseStream.SetLength(value);
        }

        #endregion
        #region 쓰기 - Write(bufferArray, offset, count)

        /// <summary>
        /// 쓰기
        /// </summary>
        /// <param name="bufferArray">버퍼 배열</param>
        /// <param name="offset">오프셋</param>
        /// <param name="count">카운트</param>
        public override void Write(byte[] bufferArray, int offset, int count)
        {
            Throttle(count);

            this.baseStream.Write(bufferArray, offset, count);
        }

        #endregion
        #region 문자열 구하기 - ToString()

        /// <summary>
        /// 문자열 구하기
        /// </summary>
        /// <returns>문자열</returns>
        public override string ToString()
        {
            return this.baseStream.ToString();
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Protected

        #region 스로틀 처리하기 - Throttle(bufferSizeInByteCount)

        /// <summary>
        /// 스로틀 처리하기
        /// </summary>
        /// <param name="bufferSizeInByteCount">바이트 카운트 단위 버퍼 크기</param>
        /// <remarks>지정된 버퍼 크기로 통신을 제한한다.</remarks>
        protected void Throttle(int bufferSizeInByteCount)
        {
            if(this.maximumByteCountPerSecond <= 0 || bufferSizeInByteCount <= 0)
            {
                return;
            }

            this.lastByteCount += bufferSizeInByteCount;

            long millisecondCountElapsed = CurrentTime - this.lastStartTime;

            if(millisecondCountElapsed > 0)
            {
                long bps = this.lastByteCount * 1000L / millisecondCountElapsed;

                if(bps > this.maximumByteCountPerSecond)
                {
                    long wakeElapsed = this.lastByteCount * 1000L / this.maximumByteCountPerSecond;

                    int toSleep = (int)(wakeElapsed - millisecondCountElapsed);

                    if(toSleep > 1)
                    {
                        try
                        {
                            Thread.Sleep(toSleep);
                        }
                        catch(ThreadAbortException)
                        {
                        }

                        Reset();
                    }
                }
            }
        }

        #endregion
        #region 리셋하기 - Reset()

        /// <summary>
        /// 리셋하기
        /// </summary>
        /// <remarks>바이트 수를 0으로 재설정하고 시작 시간을 현재 시간으로 재설정합니다.</remarks>
        protected void Reset()
        {
            long difference = CurrentTime - this.lastStartTime;

            if(difference > 1000)
            {
                this.lastByteCount = 0;

                this.lastStartTime = CurrentTime;
            }
        }

        #endregion
    }
}
728x90
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요