728x90
728x170
▶ 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
그리드형(광고전용)
'C# > Common' 카테고리의 다른 글
[C#/COMMON] 너비 우선 탐색하기 (Breadth-First Search) (0) | 2018.04.28 |
---|---|
[C#/COMMON] 깊이 우선 탐색하기 (Depth-First Search) (0) | 2018.04.28 |
[C#/COMMON] 런타임에서 코드로 C# 코드 생성하기 (0) | 2018.04.26 |
[C#/COMMON] CodeDomProvider 클래스 : 런타임에서 C# 코드를 동적으로 컴파일하고 DLL 파일 생성하기 (0) | 2018.04.26 |
[C#/COMMON] CSharpCompilation 클래스 : 런타임에서 C# 코드를 동적으로 컴파일하기 (0) | 2018.04.26 |
[C#/COMMON] 특정 파일을 사용하는 프로세스 리스트 구하기 (0) | 2018.03.29 |
[C#/COMMON] 사용자 계정 변경하기 (0) | 2018.03.24 |
[C#/COMMON] 콘솔(Console) 닫기 버튼 비활성화 하기 (0) | 2018.03.22 |
[C#/COMMON] 디버그 모드에서 프로세스 참조 구하기 (0) | 2018.03.15 |
[C#/COMMON] ITypedList 인터페이스 : TypedCollection<T> 만들기 (0) | 2018.03.11 |