[C# 강좌 - 28] 스트림


Study/C#  2012. 9. 30. 09:00

안녕하세요 개발자 명월입니다.


이번 포스팅에서는 스트림에 대해서 알아보도록 하겠습니다. 스트림이란 뜻은 원래는 작은 시냇물이라는 뜻을 가지고 있습니다. 즉 프로그램에서의 스트림이란 소리는 데이터가 끊기지 않고 연속적으로 데이터가 전송되는 것을 스트림이라고 합니다.

 

우리가 닷넷에서 스트림중에서 많이 사용하게 될 것이 먼저 FileStream 입니다. 파일의 입출력을 관리하고 그것을 또 StreamWriter 던가 StreamReader 를 통해서 파일에 데이터를 넣고 읽는 클래스를 사용하여 컨트럴을 하게 됩니다.

그리고 중요한 것은 특히 통신의 부하를 줄일 수 있는 MemoryStream 을 사용할 수도 있고 이 뿐 아니라 스트림간의 부하를 줄일수 있는 BufferStream 이 있습니다.

 

먼저 FileStream 과 StreamWriter, StreamReader 에 대해 MSDN에서 살펴보도록 하겠습니다.

MSDN - FileStream 바로가기

MSDN - StreamWriter 바로가기

MSDN - StreamReader 바로가기

 

 

 

 

역시 설명보다는 예제를 통하는 편이 이해가 빠르겠습니다.

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Blog_1__20120929
{
    class Program
    {
        static void Main(string[] args)
        {
            FileStream FS = new FileStream("Text.txt",FileMode.Append,FileAccess.Write);
            StreamWriter SW = new StreamWriter(FS);
            SW.WriteLine("명월 사이트입니다.");
            SW.Close();
            FS.Close();
       }
   }
}

 

결과화면은 Test.txt 라는 파일이 생성되었고 그 안에는 WriteLine의 값이 들어가 있습니다.

반대로 파일을 읽어 들여 보겠습니다.

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Blog_1__20120929
{
    class Program
    {
        static void Main(string[] args)
        {
            FileStream FS = new FileStream("Text.txt",FileMode.Open,FileAccess.Read);
            StreamReader SR = new StreamReader(FS);
            String _buf = SR.ReadLine();
            SR.Close();
            FS.Close();
            Console.WriteLine(_buf);
            Console.WriteLine("Press Any Key...");
            Console.ReadLine();
        }
    }
}

 

반대로 StreamReader 로 읽어 들여서 Console 화면에 뿌려봤습니다.

 

다음은 BufferStream 입니다. Buffer 스트림은 말그대로 Buffer에 입출력을 한다는 의미입니다.

위의 예제로 보시다시피 입출력을 할때 File을 Open 하는 과정이 숨어있습니다만 하나의 스레드에서 작업을 하면 문제가 없겠으나 여러곳에서 입출력을 빈번히 작동을하다 보면 충돌이 발생하게 되겠습니다.

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;

namespace Blog_1__20120929
{
    class Program
    {
        static void Main(string[] args)
        {
            new Program();
        }
        public Program() 
        {
            Timer tm1 = new Timer(new TimerCallback(Thread_Exam1),null,0,1000);
            Timer tm2 = new Timer(new TimerCallback(Thread_Exam2), null, 0, 1000);
            Console.ReadLine();
        }
        public void Thread_Exam1(Object obj)
        {
            for (int i = 0; i < 100; i++)
            {
                FileStream FS = new FileStream("Test.txt", FileMode.Append, FileAccess.Write);
                StreamWriter SW = new StreamWriter(FS);
                SW.WriteLine("Thread_Exam1 {0}",i);
                SW.Close();
                FS.Close();
            }
        }
        public void Thread_Exam2(Object obj) 
        {
            for (int i = 0; i < 100; i++)
            {
                FileStream FS = new FileStream("Test.txt", FileMode.Open, FileAccess.Read);
                StreamReader SR = new StreamReader(FS);
                Console.WriteLine(SR.ReadLine());
                SR.Close();
                FS.Close();
            }
        }
    }
}

 

위에 보시는 바와 같이 충돌이 발생하게 되겠습니다. 물론 버퍼를 쓴다고 충돌이 안 일어 나는 건 아니지만 위와 같은 잦은 접속이 충돌의 확율이 높다고 할 수 있겠습니다.

이럴 때는 버퍼에 쓸 파일을 모두 저장했다가 한꺼번에 출력하는 방식이 충돌을 방지할 수 있겠습니다.

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;

namespace Blog_1__20120929
{
    class Program
    {
        static void Main(string[] args)
        {
            new Program();
        }
        public Program() 
        {
            FileStream FS = new FileStream("Test.txt", FileMode.Append, FileAccess.Write);
            BufferedStream BS = new BufferedStream(FS);
            String _buf = "개발자명월\n";
            for (int i = 0; i < 100; i++)
            {
                BS.Write(Encoding.Default.GetBytes(_buf),0,Encoding.Default.GetByteCount(_buf));
            }
            BS.Close();
            FS = new FileStream("Test.txt", FileMode.Open, FileAccess.Read);
            BS = new BufferedStream(FS);
            Byte[] _data = new byte[1024];
            BS.Read(_data,0,1024);

            Console.WriteLine(Encoding.Default.GetString(_data));
            Console.ReadLine();
        }
    }
}
>

 

마지막으로 Memory Stream 입니다.

앞서 익힌 FileStream BufferStream과는 큰 차이가 없습니다. 다만 그 대상이 파일이 아니고 메모리인 점이 다를 뿐입니다.

역시 예제로 살펴보도록하겠습니다.

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;

namespace Blog_1__20120929
{
    class Program
    {
        static void Main(string[] args)
        {
            new Program();
        }
        public Program() 
        {
            MemoryStream MS = new MemoryStream();
            MS.Capacity = 1024;
            MS.Position = 0;

            String _buf = "명월 사이트";
            byte[] _data = Encoding.Default.GetBytes(_buf);

            MS.Write(_data, 0, _data.Length);

            byte[] _data2 = MS.GetBuffer();
            String _buf2 = Encoding.Default.GetString(_data2);

            Console.WriteLine(_buf2);
        }
    }
}

 

BufferStream 과 Memory Stream 은 보면 byte로 인코딩을 하여 사용하는 함수가 보입니다. 이것은 흔히 이야기 하는 바이너리로 코드를 변환하는 일련의 과정인데 Stream 에서는 굳히 바이너리의 차이를 느끼기 힘드나 앞으로 GDI, 소켓통신을 할때에는 이 바이너리 변환이 대단히 중요한 과정 중에 하나 입니다.

 

그 뜻은 BufferStream은 특히 GDI, MemoryStream은 소켓을 통한 전송시스템을 사용할 때 사용하는 것으로 컴퓨터와 통신속도 즉슨, 통신속도는 컴퓨터의 속도를 따라가지 못하니 그 가운데서 스트림이 완충 작용을 하는데 많이 사용하고 있습니다. BufferStream 과 Memory Stream은 후에 Socket 강좌할 때 더 자세히 알아보도록 하겠습니다.