본문 바로가기
프로그래밍

프로세스간 메시지 전송[1]

by minimax95 2020. 5. 5.

이전 포스팅에서 Window Message를 이용해서 프로세스 간에 COPYDATASTRUCT 구조체를 메시지로 전송하는 방법을 살펴보았다. (이전 포스팅 보기)

 

이번 포스팅에서는 IPC를 이용한 메시지 송수신 방법을 살펴보고자 한다.

먼저 간단하게 IPC통신이 무엇인지부터 살펴보자.

IPC는 Inter Process Communication의 약자로 커널 영역에서 제공하는 내부 프로세스간 통신을 이용해서

그 기능을 구현할 수 있다.

IPC의 종류에는 PIPE, Named PIPE, Message Queue, Shared Memory, Memory Map, socket 등이 있지만

여기서는 Shared Memory를 이용하여 기능을 살펴보고자 한다.

 

먼저 솔루션을 만들어 주고 하나의 DLL 라이브러리 프로젝트와 2개의 윈폼 프로젝트를 구성해 준다.

솔루션 이름은 IPCExample이고 IPCSharedDll은 공통으로 사용할 SharedMemory를 위한 DLL 프로젝트이다.

 

먼저 공통으로 사용할 SharedMemory DLL 부터 만들어보자.

Class1 클래스의 이름을 MySharedMemory로 수정하고 MarshalByRefObject 클래스를 상속받는다.

메모리로 사용할 컬렉션은 스레드에 안전한 ConcurrentDictionary를 사용하고

아래와 같이 생성자에서 객체를 생성해 준다.

이제 SharedMemory에 내용을 삽입하고 꺼낼 수 있도록 함수를 만들어주자.

SharedMemory에는 이름을 키로하고 Value를 값으로 하여 컬렉션을 구성하였다.

이제 DLL 프로젝트를 빌드하여 라이브러리를 생성해 주자.

생성한 Dll 파일은 Server와 Client에 각각 참조로 추가를 해 준다.

이제 Server와 Client에서 SharedMemory를 이용하여 메시지를 전송할 수 있다.

 

다음은 Server를 구현해보자.

서버 UI는 클라이언트로부터 전송받은 메시지를 출력하는 창으로만 구성하였다.

클라이언트에서 메시지를 전송하게 되면 서버의 listBox에 출력될 것이다.

 

서버 구현을 위해 IPC 통신을 위한 참조를 추가해 준다.

참조 관리자를 열어서 어셈블리의 System.Runtime.Remoting을 참조시킨다.

그리고 아래와 같이 using문을 추가해 준다.

다음은 서버에 공유메모리와 IPC 채널, 클라이언트에서 보낸 메시지를 수신할 스레드 객체를 선언한다.

MySharedMemory _mySharedMemory;     // 공유 메모리
IpcServerChannel _serverChannel;    // IPC 채널   
Thread IPCReadThread;               // 클라이언트에서 보낸 메시지를 확인하는 스레드

 

생성자에서 객체 생성 및 IPC 채널 사용을 위해 채널을 등록해 준다.

 

public ServerForm()
{
    InitializeComponent();
    _serverChannel = new IpcServerChannel("IPCServer");
    ChannelServices.RegisterChannel(_serverChannel, false);
    RemotingConfiguration.RegisterWellKnownServiceType(typeof(MySharedMemory), "ServerMemory",

                                                                                            WellKnownObjectMode.Singleton);
    _mySharedMemory = new MySharedMemory();

    InitServer();
}

void InitServer()
{
    _bIPCReadThreadRun = true;
    IPCReadThread = new Thread(IpcRead);
    IPCReadThread.Name = "IPCReadThread";
    IPCReadThread.Start();
}

 

스레드 처리기는 아래와 같이 작성한다.

void IpcRead()
{
    while(_bIPCReadThreadRun)
    {
        if(_mySharedMemory.GetSharedMemory().Count > 0)
        {
            var data = _mySharedMemory.GetSharedMemory();
            foreach(string strValue in data)
            {  
                listBox1.Invoke(new Action(() => listBox1.Items.Add(strValue)));
            }
            _mySharedMemory.MemoryClear();
        }
        Thread.Sleep(1000);
    }
}

1초마다 돌면서 클라이언트에서 공유메모리에 저장한 Text를 읽어 온다.

여기서 주의할 점은 UI를 업데이트하는 스레드가 작업자 스레드이므로 크로스 스레드 문제가 발생한다.

따라서 listBox 객체의 Invoke( )를 통해 주스레드에 UI 업데이트를 위임시킨다.

 

마지막으로 Form 종료시 처리해야할 사항을 FormClosing 이벤트 핸들러를 통해 아래와 같이 등록한다.

private void ServerForm_FormClosing(object sender, FormClosingEventArgs e)
{
    _bIPCReadThreadRun = false;
    if (IPCReadThread.ThreadState == ThreadState.Running)
    {
        if(!IPCReadThread.Join(10))
        {
            IPCReadThread.Abort();
            IPCReadThread = null;
        }
        else
        {
            IPCReadThread = null;
        }
    }            
}

 

서버를 빌드해 준다.

 

다음은 클라이언트 차례이다.

클라이언트 UI는 아래와 같이 구성한다.

심플하게 TextBox에 글을 입력하고 메시지 전송 버튼을 클릭하면 공유 메모리에 Text를 전송하여 저장하도록 한다.

이때 TextBox는 Clear된다.

클라이언트에서도 서버에서 처럼 System.Runtime.Remoting을 참조시키고 동일하게 using문을 추가해 준다.

그리고 아래와 같이 공유메모리와 IPC 채널을 선언한다.

MySharedMemory _mySharedMemory;     // 공유 메모리
IpcClientChannel _clientChannel;             // IPC 채널  

 

생성자에서 클라이언트 초기화시 공유메모리 객체를 생성하고 IPC 채널을 등록해 준다.

이때 서버에서 생성한 공유메모리 객체에 접근할 수 있도록 경로를 지정해준다.

void InitClient()
{
    _clientChannel = new IpcClientChannel();
    ChannelServices.RegisterChannel(_clientChannel, false);
    RemotingConfiguration.RegisterWellKnownClientType(typeof(MySharedMemory), "ipc://IPCServer/ServerMemory");
    _mySharedMemory = new MySharedMemory();
 }

 

다음은 텍스트상자에 입력한 텍스트를 전송할 메시지 전송 버튼 처리기를 구현한다.

private void button1_Click(object sender, EventArgs e)
{
    string strSendMsg = textBox1.Text;
    bool bSend = _mySharedMemory.SetSharedMemoryValue(strSendMsg);
    if(bSend) textBox1.Clear();

}

 

클라이언트를 빌드해 준다.

 

이제 서버와 클라이언트를 각각 실행해주고 클라이언트에서 메시지를 입력후 메시지 전송 버튼을 클릭해보자.

IPC SharedMemory 방식의 메시지 전송이 정상적으로 진행된다면 아래와 같은 실행 결과를 얻을 수 있다.

댓글