posted by 써니루루 2007. 3. 28. 16:34
기존 리모팅 예제코드를 보셨다면 추가적으로 다음 예제를 해보면 도움이 될 것이다.
http://net2.tistory.com/entry/XFile-4


1. 리모팅을 이용하여 계산을 처리하는 Calc.dll을 윈도우즈 Service에 등록시키고,
서비스를 시작시킨 상태에서 윈 폼으로 리모팅을 호출하여 계산값을 얻어낸다.

(계산을 간단히 코드상에서 처리할 수 있지만 예제로 리모팅을 사용하는 방법을 배워보도록 하자.)

invalid-file

리모팅, 윈도우즈 서비스, 계산기



2. 리모팅을 이용하여 Database에 ADO.NET으로 연결해 쿼리 데이터를 DataSet으로 반환하는 *.dll 을 작성한다. 이 dll을 윈도우즈 서비스에 등록시키고 시작시킨 상태에서 윈폼에 DataGridView 컨트롤에 리모팅으로 받아온 DataSet을 뿌려주는 예제이다.
예제코드의 쿼리문과 Database 연결 코드는 직접 수정해야 할 것이다.

invalid-file

리모팅, 윈도우즈 서비스, DataSet

posted by 써니루루 2007. 3. 28. 12:53

.NET Remoting이란?

  • RPC(Remote Procedure Call)

    • 클라이언트가 원격지에 존재하는 함수를 호출한 후, 그 결과를 원격지로부터 받아내는 기술
  • 리모팅(Remotion)
    • 리모팅은 서비스 개념을 포함한다
    • 원격 서버가 클라이언트에게 서비스(Service)를 제공한다.
  • 닷넷 리모팅에서 소개되는 기술
    • 원격 객체(Remote Object)
    • 원격 객체를 대신하는 클라이언트 프록시 객체(Proxy Object)
    • 마샬링(Marshaling), 언마샬링(Unmarshaling)
    • 직렬화(Serialization), 역직렬화(Deserialization)
    • 네트워크 통신을 위한 체널(Channel)
    • 네트워크로 전송되는 데이터를 인코딩하는 포멧터(Formatter)

.NET Remoting의 구성요소

  • 서버원격 시스템의 구성
    • 원격 클래스와 원격 객체
    • 채널(Channel)
    • 포멧터(Formatter)
      • 데이터를 해석하기 편리하고 전송하기 좋은 형식으로 인코딩 또는 디코딩해야 하는데 이 역할을 담당하는 것이 포멧터이다.
  • .NET Remoting에서 제공해주는 포멧터
    • 포멧터(SoapFormatter)
      • 데이터를 XML 형식의 SOAP 방식으로 인코딩
    • Binary 포멧터(BinaryFormatter)
      • 말그대로 Binary 형식으로 인코딩하기 때문에 인코딩 속도면에서 효율적이지만, 이기종간의 통신 포멧인 SOAP을 이용하는 것이 좋다.
  • 클라이언트 원격 시스템의 구성 요소
    • 프록시 객체(Proxy Object)
      • 원격 시스템에서 클라이언트와 서버 사이를 넘나드는 참조값
    • 채널(Channel)
      • 클라이언트와 원격 서버와 통신하기 위해 존재
    • 포멧터(Formatter)
  • .NET Remoting에서 지원하는 채널의 종류
    • HTTP 채널(HttpChannel)
    • TCP 채널(TcpChannel)
원격 시스템의 구조

원격 시스템의 구조 다이어그램




.NET Remoting의 가장 간단한 예(서버)

  • 원격 서버 프로그램 작성 과정
    • 원격 클래스 작성
    • 원격 서버 프로그램 작성
    • 클라이언트 프로그램 작성
  • 원격 클래스 작성
    • 일단 클래스와 동일하지만 MarshalByRefObject 를 상속받는 것만 다르다

예제 : 원격 클래스 Server/Hello.cs

/**

서비스할 원격 클래스

**/

using System;

public class Hello : MarshalByRefObject

{

public Hello()

{

Console.WriteLine("Hello 생성자 호출");

}

public String SayHello(String name)

{

Console.WriteLine("Hello의 SayHello() 메서드 호출, 매개변수:{0}", name);

return "안녕하세요! [" + name + "] 님";

}

~Hello()

{

Console.WriteLine("Hello 소멸자 호출");

}

}

/***

c:csharpchap14ex01Server> csc /target:library /out:Hello.dll Hello.cs

***/



예제 : 리모트 서버 /Server/HelloServer.cs

/**

원격 클래스를 서비스하는 간단한 서버 프로그램

**/

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Tcp;

public class HelloServer

{

public static void Main()

{

TcpChannel channel = new TcpChannel(9009);

ChannelServices.RegisterChannel(channel, false);

RemotingConfiguration.RegisterWellKnownServiceType(

Type.GetType("Hello, Hello"),

"BaboHello",

WellKnownObjectMode.SingleCall);

System.Console.WriteLine("서버를 멈추려면 Enter를 누르세요!");

System.Console.ReadLine();

}

}

/***

c:csharpchap14ex01Server> csc /r:Hello.dll HelloServer.cs

c:csharpchap14ex01Server> HelloServer

서버를 멈추려면 Enter를 누르세요!

[참고] .NET Framework 1.0과 1.1에서는 ChannelServices.RegisterChannel(channel)과 같은 방식으로 사용하였다. 하지만 채널만을 입력받는 이 함수는 2.0에서 Obsolete되었다.

이 함수대신 ChannelServices.RegisterChannel(channel, false)와 같이 사용해야 한다. 첫번째 매개변수는 이전과 같이 채널이며 두번째 매개변수는 보안설정이 있는지 없는지를 나타낸다.

***/





예: 원격 클라이언트 Client/HelloClient.cs

/**

원격 서비스를 이용하는 간단한 클라이언트 프로그램

**/

using System;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Tcp;

public class HelloClient

{

public static int Main(string[] args)

{

TcpChannel channel = new TcpChannel();

ChannelServices.RegisterChannel(channel, false);

object obj = Activator.GetObject(typeof(Hello),

"tcp://localhost:9009/BaboHello");

Hello h = (Hello)obj;

Console.WriteLine(h.SayHello("홍길동"));

return 0;

}

}

/***

1. 원격 클라이언트 실행

c:csharpchap14ex01Client> csc /r:Hello.dll HelloClient.cs

c:csharpchap14ex01Client> HelloClient

안녕하세요! [홍길동] 님

c:csharpchap14ex01Client> HelloClient

안녕하세요! [홍길동] 님

2. 원격 서버 프로그램 (클라이언트 접속후)

c:csharpchap14ex01Server> HelloServer

서버를 멈추려면 Enter를 누르세요!

Hello 생성자 호출

Hello의 SayHello() 메서드 호출, 매개변수:홍길동

Hello 생성자 호출

Hello의 SayHello() 메서드 호출, 매개변수:홍길동

3. 원격 서버 프로그램이 종료했을 때

c:csharpchap14ex01Server> HelloServer

서버를 멈추려면 Enter를 누르세요!

Hello 생성자 호출

Hello의 SayHello() 메서드 호출, 매개변수:홍길동

Hello 생성자 호출

Hello의 SayHello() 메서드 호출, 매개변수:홍길동

Hello 소멸자 호출

Hello 소멸자 호출

[참고]

클라이언트 프로그램을 컴파일하고 실행하기 위해서는 서버의 Hello.dll이 필요하다. 여기서는 여러분이 수동으로 클라이언트의 디렉토리에 복사해주어야 한다.

***/




예제를 사용하는 법 :

1. Hello 원격 클래스를 먼저 컴파일해서 dll 파일로 만든다.
2. 리모트 서비스를 작성해서 띄워놓는다.
3. 리모트 클라이언트를 띄워서 리모트 서비스측에 원격 객체가 생성되는지 확인한다.
posted by 권오성의 Biomedical Engineering 2007. 3. 27. 16:46

아래의 소스코드를 작성한 후, 빌드시켜 에러가 없게 합니다.
종전에 작성한 Server 측 소스코드와 거의 유사하지만 가상으로 서비스를 실행하기 위해,
버튼으로 작동되는 부분이 제거되었습니다.

1) 모두 작성한 후, Visual Studio 명령 프롬프트를 실행합니다.

사용자 삽입 이미지


2) 빌드가 끝난 Chatting Server의 실행파일을 아래의 명령어를 입력해서 서비스에 등록합니다.
참고로, 여기서는

사용자 삽입 이미지


----- Web ChattingService (Service1.CS) -----

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;

using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections;

namespace ChattingService
{
    public partial class Service1 : ServiceBase
    {
        // 기본 포트
        public static int _svrPort = 2005;

        // 서버 TCP 리스너
        private TcpListener _server = null;

        // 쓰레드를 중지하고자 할때 true 입니다.
        private bool _isStop = false;

        // 클라이언트를 삭제 쓰레드를 멈출것인지를 결정합니다.
        private bool _isStopRemove = false;

        // 클라이언트의 접속을 받아들일 쓰레드 입니다.
        private Thread _serverThread = null;

        // 접속이 끊긴 사용자를 제거하는 쓰레드
        private Thread _removeThread = null;

        // 접속된 클라이언트를 담아 놓을 리스트
        private ArrayList _arrClientLister = null;

        // 생성한 폼 어플리케이션에 로그를 찍기위해 선언한 delegate
        public delegate void LogWriteDelegate(string msg);

        // 서버의 TCP 리스너를 초기화 합니다.
        private void Init()
        {
          try
          {
            // 서버를 실행하는 컴퓨터의 아이피를 찾아 종점을 생성합니다.
            IPHostEntry localHostEntry = Dns.GetHostEntry(Dns.GetHostName());
            // TcpListener 로 서버 객체를 생성합니다.
            _server = new TcpListener(localHostEntry.AddressList[0], _svrPort);
          }
          catch
          {
            _server = null;
          }
        }

        //// 어플리 케이션의 쓰레드에 포함되기 위해 델리게이트 이용
        //public void LogWrite(string msg)
        //{
        //    // 소켓에 관련된 쓰레드가 돌고 있으므로 application 과의 충돌을 피하기위해 델리게이트를 이용합니다.
        //    LogWriteDelegate deleLogWirte = new LogWriteDelegate(AppendLog);
        //    // 생성한 델리게이트를 이용하여 Invoke 를 실행합니다.
        //    this.Invoke(deleLogWirte, new object[] { msg });
        //}

        //// 로그를 찍고 스크롤된 위치에 위치하도록 합니다.
        //public void AppendLog(string msg)
        //{
        //    try
        //    {
        //        // 메세지를 추가하고 개행을 합니다.
        //        txtLog.AppendText(msg + "\r\n");
        //        // 로그상제에 포커스를 설정합니다.
        //        txtLog.Focus();
        //        // 추가로 인해 늘어난 라인까지 보여지도록 합니다.
        //        txtLog.ScrollToCaret();
        //    }
        //    catch (Exception ex)
        //    {
        //        Console.WriteLine(ex.ToString());
        //    }
        //}


        public void StartServer()
        {
            // 서버가 제대로 생성이 되었다면
            if (_server != null)
            {
                // 리스너의 목록을 갖는 ArrayList
                _arrClientLister = new ArrayList();

                // 클라이언트의 요청을 받는 쓰레드를 시작합니다.
                _server.Start();

                // 클라이언트의 접속을 받아들일수 있는 쓰레드를 생성
                _serverThread = new Thread(new ThreadStart(ServerThreadStart));
                // 쓰레드를 백그라운드로 설정합니다.
                _serverThread.IsBackground = true;
                // 쓰레드를 가동시킵니다.
                _serverThread.Start();
                // 가동할때 약간의 여유를 줍니다.
                Thread.Sleep(300);

                //접속이 끊긴 클라이언트 소켓을 삭제하는 쓰레드
                _removeThread = new Thread(new ThreadStart(RemoveThreadStart));
                // 쓰레드를 백그라운드로 설정합니다.
                _removeThread.IsBackground = true;
                // 쓰레드를 가동시킵니다.
                _removeThread.Start();
                // 가동할때 약간의 여유를 줍니다.
                Thread.Sleep(300);
            }
        }


        public void StopServer()
        {
            // 서버가 정상적이라면
            if (_server != null)
            {
                // 서버가 정지임을 알립니다.
                _isStop = true;
                // 리스닝을 중지합니다.
                _server.Stop();

                // 쓰레드가 멈출때까지 1초정도 기다립니다.
                _serverThread.Join(1000);

                // 쓰레드가 살아 있다면 중지 시킵니다.
                if (_serverThread.IsAlive)
                {
                    // 쓰레드가 종료되도록 합니다.
                    _serverThread.Abort();
                }
                // 쓰레드를 완전히 해제합니다.
                _serverThread = null;

                // 제거 쓰레드를 중지를 표시하기위해 _isStopRemove을 true로
                _isStopRemove = true;

                // 쓰레드 중지를 위한 1초
                _removeThread.Join(1000);

                // 쓰레드가 살아 있다면 중지 시킴
                if (_removeThread.IsAlive)
                {
                    // 쓰레드가 종료되도록 합니다.
                    _removeThread.Abort();
                }
                // 쓰레드를 완전히 해제합니다.
                _removeThread = null;

                // Stop All clients.
                StopAllSocketListers();
            }
        }


        private void StopAllSocketListers()
        {
            // 클라이언트와 접속하고 있는 모든 쓰레드를 중지시킵니다.
            foreach (TCPSocketListener socketListener in _arrClientLister)
            {
                // 각각의 리스너를 중지시키기 위한 메소드를 실행
                socketListener.StopSocketListener();
            }

            // 모든 클라이언트 목록를 제거 합니다.
            _arrClientLister.Clear();
            // 목록 관리 리스트를 해제합니다.
            _arrClientLister = null;
        }


        private void ServerThreadStart()
        {
            // 클라이언트 소켓객체를 선언합니다.
            Socket clientSocket = null;
            // 소켓 리스트 객체를 선언합니다.
            TCPSocketListener socketListener = null;

            while (!_isStop)
            {
                try
                {
                    // 서버에 접속된 클라이언트 소켓을 받습니다.
                    clientSocket = _server.AcceptSocket();
                    // 연결된 소켓값으로 클라이언트에 대응할 소켓 리스너를 생성합니다.
                    socketListener = new TCPSocketListener(clientSocket, this);
                    // 목록으로 락을 겁니다
                    lock (_arrClientLister)
                    {
                        // 연결리스트에 새로운 리스너를 추가시킵니다.
                        _arrClientLister.Add(socketListener);
                    }

                    // 클라이언트와 통신할 쓰레드를 실행 시킵니다.
                    socketListener.StartSocketListener();
                    // 연결된 로그를 남깁니다.
                    //LogWrite("[" + clientSocket.RemoteEndPoint.ToString() + "]" + "이 연결되었습니다.");

                }
                catch
                {
                    _isStop = true;
                }
            }
        }


        private void RemoveThreadStart()
        {
            // 종료하기 위해 멈춘것이 아니라면 삭제하는 작업을 계속 합니다.
            while (!_isStopRemove)
            {

                // 연결 목록에서 제거할 리스트를 담을 리스트 객체 선언
                ArrayList deleteList = new ArrayList();

                // 현재의 리스트가 변동이 없도록 락을 걸어 줍니다.
                lock (_arrClientLister)
                {
                    foreach (TCPSocketListener socketListener in _arrClientLister)
                    {
                        // 연결되어 있지 않아 삭제 표시가 된 리스너를 찾아 내고 중지시킵니다.
                        if (socketListener.IsMarkedForDeletion())
                        {
                            // 마킹 되었다면 삭제 리스트에 추가시킵니다.
                            deleteList.Add(socketListener);
                            // 소켓에서 실행하고 있던 리스너를 종료 시킵니다.
                            socketListener.StopSocketListener();
                            // 종료하는동안 잠시 쉽니다.
                            Thread.Sleep(300);
                        }

                    }
                    // 마킹된 리스트를 종료시킵니다.
                    foreach (TCPSocketListener delListener in deleteList)
                    {
                        // 종료된 메세지를 로그에 남깁니다.
                        //LogWrite(delListener.NickName + " 님이 접속 종료되었습니다.");
                        // 연결 목록에서 삭제 합니다 .
                        _arrClientLister.Remove(delListener);
                    }
                }
                Thread.Sleep(300);

                deleteList = null;
            }// end while
        }


        // 연결되어 있는 모든 클라이언트에게 메세지를 보냅니다.
        public void BroadcastMsg(string msg)
        {
            // 현재의 리스트값이 변동 없는 조건으로 메세지를 보내도록 합니다.
            lock (_arrClientLister)
            {
                // 각각의 클라이언트마다 체크하도록 합니다.
                foreach (TCPSocketListener client in _arrClientLister)
                {
                    try
                    {
                        // 각각의 클라인언트의 메소드 값을 통해 메세지를 보냅니다.
                        client.SendMessage(msg);
                    }
                    catch
                    {
                        //LogWrite("[error]" + ex.ToString());
                    }
                }
            }
        }

        public Service1()
        {
            InitializeComponent();
            Init();
        }

        protected override void OnStart(string[] args)
        {
            // 멈춤 상태를 false 로 설정합니다.  
            _isStop = false;
            // 접속이 끊긴 클라이언트 삭제 작업이 멈춤이 아님을 설정합니다.
            _isStopRemove = false;
            // 로그창에 시작을 알립니다.
            //LogWrite("서버를 시작합니다.");
            // 쓰레드를 시작할 수 있도록 메소드를 호출합니다.
            this.StartServer();
        }

        protected override void OnStop()
        {
            // 창에 서버 중지를 알립니다.
            //LogWrite("서버를 중지합니다.");
            // 서버를 중지할 메소드를 호출합니다.
            this.StopServer();
        }
    }
}


마지막으로 웹 서비스를 위해 작성한 부분입니다.

종전에 Server를 동작시키는 동작을 하지 않고, 똑같은 소스를 [관리도구 -> 서비스]에서

동작시킵니다.

1) 코드작성을 마치고, 디자인모드로 돌아가서 설치관리자를 추가합니다.

사용자 삽입 이미지

2) 아래와 같이 서비스 아이콘이 생성됩니다.
DisplayName은 서비스에 등록되는 이름입니다.
알맞게 사용하시면 되겠습니다.

사용자 삽입 이미지

3) 채팅서비스가 동작하는 화면입니다.

사용자 삽입 이미지

4) 서비스가 백그라운드로 돌아가고 있는 상태에서 사용자측 채팅화면을 띄우고, 채팅하는 모습입니다.

사용자 삽입 이미지


posted by 권오성의 Biomedical Engineering 2007. 3. 27. 16:45

사용자측 화면예시.



사용자 삽입 이미지



-----사용자측 소스 (Client.CS) -----

posted by 권오성의 Biomedical Engineering 2007. 3. 27. 16:43

기본 동작원리는 다음과 같습니다.

먼저 클라이언트가 접속하기 위해서는 서버가 동작하고 있어야 합니다.

채팅서버를 사용하는 화면예시..

1) 서버측 연결화면.

사용자 삽입 이미지

아래의 파일은 .NET2005(Framework 2.0), Windows2003 Server, C#으로 구현되었습니다.

압축을 풀고 솔루션을 열면 서버(Server), 클라이언트(Client), ChattingService(웹기반)솔루션이

차례로 열릴 것입니다.
사용자 삽입 이미지
사용자 삽입 이미지

----- Server (Server.CS) => -----

using System;
using System.IO;
using System.Net;
using System.Data;
using System.Text;
using System.Drawing;
using System.Net.Sockets;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;

namespace Server
{
  public partial class Server : Form
  {
    // 기본 포트
    public static int _svrPort = 2005;

    // 서버 TCP 리스너
    private TcpListener _server = null;

    // 쓰레드를 중지하고자 할때 true 입니다.
    private bool _isStop = false;

    // 클라이언트를 삭제 쓰레드를 멈출것인지를 결정합니다.
    private bool _isStopRemove = false;

    // 클라이언트의 접속을 받아들일 쓰레드 입니다.
    private Thread _serverThread = null;

    // 접속이 끊긴 사용자를 제거하는 쓰레드
    private Thread _removeThread = null;

    // 접속된 클라이언트를 담아 놓을 리스트
    private ArrayList _arrClientLister = null;

    // 생성한 폼 어플리케이션에 로그를 찍기위해 선언한 delegate
    public delegate void LogWriteDelegate(string msg);

    // 서버 생성자.
    public Server()
    {
      InitializeComponent();
      Init();
    }

    // 서버의 TCP 리스너를 초기화 합니다.
    private void Init()
    {
      try
      {
        // 서버를 실행하는 컴퓨터의 아이피를 찾아 종점을 생성합니다.
        IPHostEntry localHostEntry = Dns.GetHostByName(Dns.GetHostName());
        // TcpListener 로 서버 객체를 생성합니다.
        _server = new TcpListener(localHostEntry.AddressList[0], _svrPort);
      }
      catch (Exception ex)
      {
        _server = null;
      }
    }

    // 시작 버튼을 누르면 서버를 시작합니다.
    private void btnStart_Click(object sender, EventArgs e)
    {
      // 멈춤 상태를 false 로 설정합니다.  
      _isStop = false;
      // 접속이 끊긴 클라이언트 삭제 작업이 멈춤이 아님을 설정합니다.
      _isStopRemove = false;
      // 로그창에 시작을 알립니다.
      LogWrite("서버를 시작합니다.");
      // 쓰레드를 시작할 수 있도록 메소드를 호출합니다.
      this.StartServer();
    }

    // 중짐 버튼을 누르면 서버의 서비스를 중지합니다.
    private void btnStop_Click(object sender, EventArgs e)
    {
      // 창에 서버 중지를 알립니다.
      LogWrite("서버를 중지합니다.");
      // 서버를 중지할 메소드를 호출합니다.
      this.StopServer();
    }


    // 어플리 케이션의 쓰레드에 포함되기 위해 델리게이트 이용
    public void LogWrite(string msg)
    {
      // 소켓에 관련된 쓰레드가 돌고 있으므로 application 과의 충돌을 피하기위해 델리게이트를 이용합니다.
      LogWriteDelegate deleLogWirte = new LogWriteDelegate(AppendLog);
      // 생성한 델리게이트를 이용하여 Invoke 를 실행합니다.
      this.Invoke(deleLogWirte, new object[] { msg });
    }

    // 로그를 찍고 스크롤된 위치에 위치하도록 합니다.
    public void AppendLog(string msg)
    {
      try
      {
        // 메세지를 추가하고 개행을 합니다.
        txtLog.AppendText(msg + "\r\n");
        // 로그상제에 포커스를 설정합니다.
        txtLog.Focus();
        // 추가로 인해 늘어난 라인까지 보여지도록 합니다.
        txtLog.ScrollToCaret();
      }
      catch (Exception ex)
      {
        Console.WriteLine(ex.ToString());
      }
    }


    public void StartServer()
    {
      // 서버가 제대로 생성이 되었다면
      if (_server != null)
      {
        // 리스너의 목록을 갖는 ArrayList
        _arrClientLister = new ArrayList();

        // 클라이언트의 요청을 받는 쓰레드를 시작합니다.
        _server.Start();

        // 클라이언트의 접속을 받아들일수 있는 쓰레드를 생성
        _serverThread = new Thread(new ThreadStart(ServerThreadStart));
        // 쓰레드를 백그라운드로 설정합니다.
        _serverThread.IsBackground = true;
        // 쓰레드를 가동시킵니다.
        _serverThread.Start();
        // 가동할때 약간의 여유를 줍니다.
        Thread.Sleep(300);

        //접속이 끊긴 클라이언트 소켓을 삭제하는 쓰레드
        _removeThread = new Thread(new ThreadStart(RemoveThreadStart));
        // 쓰레드를 백그라운드로 설정합니다.
        _removeThread.IsBackground = true;
        // 쓰레드를 가동시킵니다.
        _removeThread.Start();
        // 가동할때 약간의 여유를 줍니다.
        Thread.Sleep(300);
      }
    }


    public void StopServer()
    {
      // 서버가 정상적이라면
      if (_server != null)
      {
        // 서버가 정지임을 알립니다.
        _isStop = true;
        // 리스닝을 중지합니다.
        _server.Stop();

        // 쓰레드가 멈출때까지 1초정도 기다립니다.
        _serverThread.Join(1000);

        // 쓰레드가 살아 있다면 중지 시킵니다.
        if (_serverThread.IsAlive)
        {
          // 쓰레드가 종료되도록 합니다.
          _serverThread.Abort();
        }
        // 쓰레드를 완전히 해제합니다.
        _serverThread = null;

        // 제거 쓰레드를 중지를 표시하기위해 _isStopRemove을 true로
        _isStopRemove = true;

        // 쓰레드 중지를 위한 1초
        _removeThread.Join(1000);

        // 쓰레드가 살아 있다면 중지 시킴
        if (_removeThread.IsAlive)
        {
          // 쓰레드가 종료되도록 합니다.
          _removeThread.Abort();
        }
        // 쓰레드를 완전히 해제합니다.
        _removeThread = null;

        // Stop All clients.
        StopAllSocketListers();
      }
    }


    private void StopAllSocketListers()
    {
      // 클라이언트와 접속하고 있는 모든 쓰레드를 중지시킵니다.
      foreach (TCPSocketListener socketListener in _arrClientLister)
      {
        // 각각의 리스너를 중지시키기 위한 메소드를 실행
        socketListener.StopSocketListener();
      }

      // 모든 클라이언트 목록를 제거 합니다.
      _arrClientLister.Clear();
      // 목록 관리 리스트를 해제합니다.
      _arrClientLister = null;
    }


    private void ServerThreadStart()
    {
      // 클라이언트 소켓객체를 선언합니다.
      Socket clientSocket = null;
      // 소켓 리스트 객체를 선언합니다.
      TCPSocketListener socketListener = null;

      while (!_isStop)
      {
        try
        {
          // 서버에 접속된 클라이언트 소켓을 받습니다.
          clientSocket = _server.AcceptSocket();
          // 연결된 소켓값으로 클라이언트에 대응할 소켓 리스너를 생성합니다.
          socketListener = new TCPSocketListener(clientSocket, this);
          // 목록으로 락을 겁니다
          lock (_arrClientLister)
          {
            // 연결리스트에 새로운 리스너를 추가시킵니다.
            _arrClientLister.Add(socketListener);
          }

          // 클라이언트와 통신할 쓰레드를 실행 시킵니다.
          socketListener.StartSocketListener();
          // 연결된 로그를 남깁니다.
          LogWrite("[" + clientSocket.RemoteEndPoint.ToString() + "]" + "이 연결되었습니다.");

        }
        catch (SocketException se)
        {
          _isStop = true;
        }
      }
    }


    private void RemoveThreadStart()
    {
      // 종료하기 위해 멈춘것이 아니라면 삭제하는 작업을 계속 합니다.
      while (!_isStopRemove)
      {

        // 연결 목록에서 제거할 리스트를 담을 리스트 객체 선언
        ArrayList deleteList = new ArrayList();

        // 현재의 리스트가 변동이 없도록 락을 걸어 줍니다.
        lock (_arrClientLister)
        {
          foreach (TCPSocketListener socketListener in _arrClientLister)
          {
            // 연결되어 있지 않아 삭제 표시가 된 리스너를 찾아 내고 중지시킵니다.
            if (socketListener.IsMarkedForDeletion())
            {
              // 마킹 되었다면 삭제 리스트에 추가시킵니다.
              deleteList.Add(socketListener);
              // 소켓에서 실행하고 있던 리스너를 종료 시킵니다.
              socketListener.StopSocketListener();
              // 종료하는동안 잠시 쉽니다.
              Thread.Sleep(300);
            }

          }
          // 마킹된 리스트를 종료시킵니다.
          foreach (TCPSocketListener delListener in deleteList)
          {
            // 종료된 메세지를 로그에 남깁니다.
            LogWrite(delListener.NickName + " 님이 접속 종료되었습니다.");
            // 연결 목록에서 삭제 합니다 .
            _arrClientLister.Remove(delListener);
          }
        }
        Thread.Sleep(300);

        deleteList = null;
      }// end while
    }


    // 연결되어 있는 모든 클라이언트에게 메세지를 보냅니다.
    public void BroadcastMsg(string msg)
    {
      // 현재의 리스트값이 변동 없는 조건으로 메세지를 보내도록 합니다.
      lock (_arrClientLister)
      {
        // 각각의 클라이언트마다 체크하도록 합니다.
        foreach (TCPSocketListener client in _arrClientLister)
        {
          try
          {
            // 각각의 클라인언트의 메소드 값을 통해 메세지를 보냅니다.
            client.SendMessage(msg);
          }
          catch (Exception ex)
          {
            LogWrite("[error]" + ex.ToString());
          }
        }// End of foreach
      }// End of lock
    }// End of Method
  }// End of Class
}// End of Namespace

posted by 써니루루 2007. 3. 27. 12:41
선형 콘그루엔셜 방법(Hull. De bull 제안)

1. 하나의 초기 숫자를 설정(x0)
2. 3개의 상수 a, c, m을 수식에 대입해 난수를 발생시킨다.
수식 : x1 = (a * x0 + c) % m
3. 다음 조건을 만족해야 한다.
a < m, c < m, 0 < m


위 알고리즘을 C#으로 표현한 소스는 다음과 같다.

posted by 써니루루 2007. 3. 22. 18:26

reference : http://msdn2.microsoft.com/en-us/library/ms752059.aspx

XAML Overview

This topic describes the features of the Extensible Application Markup Language (XAML) language and demonstrates how you can use XAML to write Windows Presentation Foundation (WPF) applications. This topic specifically describes XAML as implemented by Windows Presentation Foundation (WPF). XAML itself is a larger language concept than Windows Presentation Foundation (WPF).

This topic contains the following sections.