필독!! MS 개발 공유 운동의 목적과 이용방법
MS 개발 공유 운동은

MS 플랫폼에서 개발한 소스를 공개함으로써 한국 개발자들의 경쟁력과 위상이 높아졌으면 합니다.

하지만 Copy & Past 죄악이며 개발자 스스로 무덤파는 행위입니다.

그래서 몇가지 규칙을 정했습니다.

1) 공개되는 소스나 강좌는 모두 작성자에게 권리가 있습니다.

2) 소스 사용은 무조건 자유이나 소스를 사용 할 때에는 꼭 제 이메일이나 덧글로 어디에 어떻게 쓸것인지 알려주세요

3) 소스를 수정하여 쓸 경우 개선한 소스를 이 블로그에 반드시 공개를 해야합니다.

4) 소스트 포스트 내용에 문제가 있을경우 저에게과감하게 지적 해주십시요



ps : 왜 MS 개발 공유 운동이냐면 본인은 프리랜서개발자로써 주로 MS 기반의 개발을 많이 해서 그렇습니다.
딴이유는 없어요 ^^
by 불늑대 | 2007/06/01 20:49 | MS 개발공유운동은... | 트랙백 | 덧글(0)
1) 닷넷리모팅 대략 맛보기

작성자 : 불늑대
작성일 : 2007/5/29
수정일 :
소스환경 : .netframework 2.0 , VS2005


1) 닷넷리모팅 대략 맛보기

2) 클라이언트 활성화 모드로 실제 원격 데이터베이스 처리 모듈 사례 개발

3) 서버 활성화 모드로 실제 원격 데이터베이스 처리 모듈 사례 개발


다른 프로세스의 어떤 객체를 이용할려면 어떻게 해야 할까...
혹은 서로 다른 위치의 pc에 들어있는 객체를 사용해야 한다면?

과거에는 DCOM이란 것을 이용했다고 하지만 블늑대는 DCOM 에 대해 잘모르니깐 패스하고
우리가 알아봐야할 닷넷리모팅에 대해 알아보겠습니다.

쉽게 말하면 원격 클래스의 메소드나 원격의 자원들을 로컬에서 사용하듯 쉽게 프로그래밍 할수있습니다.
원격 인스턴스를 생성하고 메소드등을 호출하여 사용하기만 하면되는거죠.

쉽게 사용할수있지만 이런일들 어떻게 가능하지 내부적인 것을 한번 보겠습니다.





대략 이런 그림입니다.
순서를 보면

1단계) 우선 클라이언트 프로세스는 원격프시를 생산합니다. 클라이언트 프로세스가 원격프로세스에 바로 연결은 불가능하기때문에
프락시를 생성합니다. 닷넷프레임워크에서 자동으로 지원해줍니다. 원객객체 DLL를 로칼프로세스에 참조추가 해주는 이유입니다.

2단계)
포맷터를 이용하여 메세지를 직렬화 시킵니다. 사실 닷넷 리모팅도 내부적으로는 원격소켓통신을 기반하기때문에
데이터를 메세지회시켜 바이너리 또는 SOAP 메세지로 전송합니다.

3단계) 채널을 통해서 실질적으로 원격객체를 호출하게 됩니다. TCP 또는 HTTP 프로토콜을 선택할수있습니다

다음에 또 알아봐야할 것은  리모트 객체의 종류입니다.
우선 리모트 객체를 만들려면 MarshalByRefObject 클래스의 상속받은 파생클래스로 만들어야합니다.
일단 두가지 모드가있씁니다
Well-Known 모드와
클라이언트 활성화 모드입니다.

첫번째 Well-Known 은 주도권?을 서버가 가지고있다고 표현해야 한다. 클라이언트에서 원격객체를 생성 할때 생성자가 호출되면서 활성화되는게 아니라 원격객체의 메소드를 호출할때 원격객체는 활성되어진다.
Well-Kown 에서 또 두가지로 모드로 분기되는데 SingleCall과 Singleton 이다
- SingleCall은 매번 원격 메소드가 호출될때마다 새롭게 생성된다.. 그러므로 원격객체의 상태가 없다.
즉 전역변수나 프로퍼티를 사용하지 못하는것이다.
- Singleton 은 이와 반대로
정적 메소드나 클래스 필드와 같이 모든 클라이언트 프로세스들이 원격객체의 상태를 공유한다. 나중에 샘플을 보시면
알겠지만 메소드 호출로 첫번째 객체가 만들어지면 모든 프로세스는 상태를 공유하게 된다.
그리고 객체가 활성화되는 시간을 제어할수도있다.



다음에 계속....

by 불늑대 | 2007/06/01 20:35 | 닷넷리모팅을 적용하자 | 트랙백 | 덧글(0)
1) .net(C#) 으로 구현한 심플 파일전송 소켓

작성자 : 불늑대
작성일 : 2007/5/29
수정일 :

소스환경 : .netframework 2.0 , VS2005


filewolf.zip


1) C#으로 파일전송 소켓을 만들었습니다.


2) 비동기 소켓 3)쓰레드와 풀을 이용한 소켓


일단 소켓은 다들 알다시피 서로다른 위치에 있는 프로그램들을 네트워크안에서 서로 통신이 가능하도록 하는

링크단자 혹은 계층이라고 합니다.

이번에 구현된 소켓은 .NET 에서 제공되는 네트워크 클래스를 이용하여 고수준의 소켓을 제작을 하였습니다.

이용된 클래스는 TcpListener 와 TcpClient 두 클래스 입니다.

여기서 고수준이라 하면 저수준 소켓구현방법도 있겠죠.
 
저수준 소켓이란 소켓 연결에 대해 세세한 컨트롤이 필요할때 저수준 소켓으로 구현합니다.

하지만 일반적인 소켓통신에 있어서는 닷넷에서 제공되는 고수준의 TcpListener 와 TcpClient 가

그런데로 잘 작동되며 안정적이고 쉽게 구현할수있습니다.

일단 소켓과 전송프로토콜등에 대해 알고싶다면 인터넷에 많은 정보가 있으니깐 보시고... 구현을 해보겠습니다.

소켓을 구현할려면 당연 서버와 클라이언트가 있어야겠죠

서버화면입니다.


특별한건 없고 콘솔 실행파일로 만들었습니다. 다음은

클라이언화면 입니다.

클라이언트 화면은 업로드 시킬 파일을 선택하고 업로드 버튼을 클릭하여 서버에 파일을 전송하는 윈폼입니다.
밑에 텍스트 박스는 실행결과를 보여줍니다.

그럼 서버측 소스를 볼까요

일단 app.config 파일에 포트와 파일이 저장되게될 서버측 경로를 설정했습니다.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 <appSettings>
  <add key="PORT" value="1234"/>
  <add key="FILEPATH" value="C:\FILE_DOWN\"/>
 </appSettings>
</configuration>

요렇게요.
포트는 그냥 1234로 했고 FILEPATH 는 파일이 전송되어 저장될 경로입니다. 서버를 실행하게 되면 C드라이브 밑에 FILE_DOWN
폴더가 만들어지게 했습니다.

전역변수로
static StreamWriter _log = null;
static string _Path = ConfigurationSettings.AppSettings["FILEPATH"].ToString();

요렇게 두개를 선언했습니다. _log는 로그기록를 파일에 남기는 StreamWriter 변수입니다.
보통 배치성 프로그램에서는 상태나 오류발생을 보기위해 파일로 로그를 남기죠.
_Path 변수는 파일이 저장될 경로와 로그파일이 들어갈 경로입니다.

자 이제 첫프로그램의 진입 메소드인 Main 메소를 보겠습니다.
 TcpListener listener = new TcpListener(portNum);

            try
            {
                listener.Start();
                Console.WriteLine("=== 불늑대 서버를 시작합니다.{0}==================",DateTime.Now.ToString("yyyy년 MM월 dd일 HH:mm"));

                _log.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm") + " : 불늑대 서버를 시작 하였습니다.");
                _log.Flush();

                while (true)
                {

                    TcpClient handler = listener.AcceptTcpClient();

                    if (handler != null)
                    {
                        try
                        {
                            p.ClientHandle(handler);
                        }
                        catch (Exception ex)
                        {
                            _log.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm") + " : ***ERROR***************");
                            _log.WriteLine("- MESSAGE -");
                            _log.WriteLine(ex.Message);
                            _log.WriteLine("- Client -");
                            _log.WriteLine(handler.Client.AddressFamily.ToString());
                            _log.WriteLine("*****************************");
                            _log.Flush();
                        }
                    }
                }
               
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                _log.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm") + " : 불늑대 이미징 서버를 종료 하였습니다.");
                _log.Flush();
                listener.Stop();
                _log.Close();
            }
        }

굉장히 간단합니다. 이것이 닷넷 클래스인 TcpListener 의 편리함입니다.
TcpListener listener = new TcpListener(portNum);
일단 TcpListener에 포트번호를 인자로 주고 생성합니다.
그리고
listener.Start();
Start 메소드를 호출하여 클라이언트의 접속을 기달립니다.
그리고 다음
TcpClient handler = listener.AcceptTcpClient();
루프안에서 클라이언트가 접속을 시도하면 클라이언트를 받습니다. 연결된 거죠
그리고 클라이언트의 통신을 처리하고(이건 밑에 설명해드리겠습니다)
다음 클라이언트의 접속을 받기위해 루프문이 다시 돌게 됍니다.
이게 끝입니다. 간단하죠.

그럼 접속된 클라언트와 실제 통신맺고 파일 전송을 하는 부분을 보겠습니다.
TcpClient ClientSocket;
       
     NetworkStream networkStream;

           

            handler.ReceiveTimeout = 100;
            ClientSocket = handler;
            networkStream = ClientSocket.GetStream();

            try
            {

                if (networkStream.CanRead && networkStream.CanWrite)
                {
                    byte[] ReadByte;
                    ReadByte = new byte[ClientSocket.ReceiveBufferSize];
                    int BytesRead = networkStream.Read(ReadByte, 0, (int)ReadByte.Length);
                    string filename = Encoding.GetEncoding("ks_c_5601-1987").GetString(ReadByte, 0, BytesRead);

                    Byte[] sendBytes = Encoding.GetEncoding("ks_c_5601-1987").GetBytes(_Path + @"\" + filename);
                    networkStream.Write(sendBytes, 0, sendBytes.Length);


                    /*전송받을 이미지 사이즈를 받음*/
                    int ByteSize = 0;
                    Byte[] FileSizeBytes = new byte[ClientSocket.ReceiveBufferSize];
                    ByteSize = networkStream.Read(FileSizeBytes, 0, FileSizeBytes.Length);
                    int MaxFileLength = Convert.ToInt32(Encoding.ASCII.GetString(FileSizeBytes, 0, ByteSize));

 

                    /*전송준비작업을 완료했다고 클라이언트에 전해줌*/
                    byte[] ReadyTransBytes = new byte[ClientSocket.ReceiveBufferSize];
                    ReadyTransBytes = Encoding.ASCII.GetBytes("READY");
                    networkStream.Write(ReadyTransBytes, 0, ReadyTransBytes.Length);

                   
                 
   FileStream fs = new FileStream(_Path + @"\" +  filename, FileMode.Create, FileAccess.Write, FileShare.None);
                    if (filename != string.Empty)
                    {

                        byte[] myReadBuffer = new byte[1024];
                        int numberOfBytesRead = 0;

                        do
                        {
                            numberOfBytesRead = networkStream.Read(myReadBuffer, 0, myReadBuffer.Length);
                            fs.Write(myReadBuffer, 0, numberOfBytesRead);
                        }
                        while (fs.Length < MaxFileLength);
                        //while(networkStream.DataAvailable)
                    }
                    fs.Flush();
                    fs.Close();
               
                }

                return true;
            }
            catch
            {
                throw;
            }
            finally
            {
               
                networkStream.Flush();
                networkStream.Close();
                ClientSocket.Close();
            }


붉은색으로 칠한부분을 주의깊게 보세요
클라이언트로 부터 파일크기를 받는다 ->
파일받을준비가되었다고 READY를 보넨다 - >
받은 파일크기 만큼 루프를 돌면서 클라이언트로 부터 바이너리형태의 파일을 받아 로컬에 저장한다


중요한 부분이다. 어떤 분들은
while(networkStream.DataAvailable)  #DataAvailable 프로퍼티는 데이터 수신이끝나면 false를 반환
"이렇게 루프를 돌면 안되요?"
혹은  그냥
"networkStream.Read로 다 받으면안되나요?"
사실 안되는건 아니다 로컬에서 클라이언트 서버 테스트 해보면 잘될것이다. 하지만 소켓통신이란게
네트워크 상황에 따라 데이터를 다보내어도 받는쪽에서 다받지 못하고 그냥 넘어가게 되는 경우가 있습니다.
이런식으로 파일사이즈를 받아서 그 사이즈 만큼 루프를 돌며 1024정도 되는 버퍼에 담아 합치는 방법있습니다.
불늑대도 이 예제를 만들면서 닷넷소켓클래스를 너무 믿은 탓일까? 짧게나마 삽질을 했다는....


자 그럼 클라이언트 소스를 보자

  /*파일 사이즈를 클라이언트로 전달*/
                    networkStream.Write(Encoding.ASCII.GetBytes(file.Length.ToString()), 0, Encoding.ASCII.GetBytes(file.Length.ToString()).Length);

                    /*클라이언트 측에서 준비되었는지 확인하고 준비되었다면 파일전송*/
                    int BytesRead2 = 0;
                    byte[] ConfirmByte = new byte[tcpClient.ReceiveBufferSize];
                    BytesRead2 = networkStream.Read(ConfirmByte, 0, (int)ConfirmByte.Length);


                    if (serverFileName.Length > 0 && BytesRead2 > 0)
                    {
                        byte[] FileBytes;
                        FileBytes = new byte[fs.Length];
                        fs.Read(FileBytes, 0, FileBytes.Length);
                        networkStream.Write(FileBytes, 0, FileBytes.Length);
                        textBox1.Text += "*** 불늑대 서버에 파일 업로드를 완료했습니다.\r\n";
                    }

클라이언트도 간단하다. 서버에서 데이터를 수신할때 처럼
발신하는 파일의 사이즈를 먼저 보넨다 ->
송신준비가 되었다는 서버의 메세지를 받는다->
파일을 전송한다.

이로써 심플한 서버/클라이언트 파일 전송 프로그램을 보았습니다...
앞으로

2) 비동기 소켓 3)쓰레드와 풀을 이용한 소켓

순으로 계속 올릴 예정입니다.


by 불늑대 | 2007/05/25 23:19 | 소켓데이터 통신 | 트랙백 | 덧글(4)
< 이전페이지 다음페이지 >