흐르는 시간의 블로그...


출처 :http://blog.naver.com/PostView.nhn?blogId=cocos2006&logNo=50041037321


Indy는 델파이나 C++빌더에서 사용할 수 있는 소켓프로그래밍용 컴포넌트입니다.

Visual Studio를 주로 사용하는 개발자들은 WinSock2 라이브러리를 주로 사용할 것이고, 기본소켓의 확장이기에 사용할 때도 그에 준하여 사용하면 되지만, Indy소켓은 그 사용방법에 있어 기본 소켓과는 다른 방식을 취하고 있어서 사용하는 데 고려해야 할 것이 많은 듯 합니다.

Indy의 사용방법은 데모프로그램을 통해서 사용법을 익힐 수 있지만 사용자가 직접 소켓프로그래밍을 하면서 곧바로 부딪히는 문제는 바로 의외의 데드락입니다. 데모프로그램에 나오는 방법으로 프로그래밍을 했음에도 불구하고, 알 수 없는 데드락으로 프로그램이 정지하는 문제가 자주 발생하게 되는데 이것은 Indy의 특성상 데드락이 되는 방식임에도 불구하고, 데모프로그램에서 그대로 사용하고 있는 것이 이러한 문제를 해결할 수 없게 만드는 한 요인이 됩니다.

간단하게 Indy를 사용할때 주의해야 할 점을 든다면 다음과 같습니다.

1. Indy는 버전에 따라 구조가 조금씩 다르고, 호환성이 없다.

2. Indy는 멀티스레드방식으로 작동하고, 블럭킹소켓을 사용한다.(이 내용은 Indy소개하는 문서들을 보면 많이 나와 있습니다.)

위와 같이 Indy를 사용할 때의 주의점은 2개정도로 간단한 것 같지만 델파이와 사용할 때 문제를 발생시킬 수 있는 소지가 많은 것이 2.번입니다. "Indy는 멀티스레드방식으로 작동한다."는 것과 델파이에서 "VCL은 스레드에 안전하지 않다"는 것을 간과한다면 많은 부분에서 이상동작을 하는 것을 해결하지 못할 것입니다.

즉, Indy중에 TIdTCPServer컴포넌트를 사용하고, 그 컴포넌트 내에서 OnConnect, OnDisConnect, OnExecute등의 이벤트를 사용한다면. 각각의 이벤트 내에서 버튼이나, 메모장, 심지어 체크박스의 내용을 변경할 경우에는 데드락이 발생할 것을 염두에 두어야 하고, 또한 버튼을 클릭하는 이벤트나, 체크박스의 변경 이벤트내에서 TIdTCPServer의 이벤트, 특히 Disconnect와 같은, 를사용하면 데드락이 발생할 수 있습니다. 이유는 위에서 말한 "Indy는멀티스레드방식을 사용한다"는 것과 "델파이의 VCL은 스레드에 안전하지 않다"는 것이 맞아 떨어져 동작에충돌이 발생할 가능성이 크기 때문입니다. TIdTCPServer의OnExecute같은 이벤트는 클라이언트와 연결이 이루어지면 지속적으로 이 이벤트가 발생하게되고, 클라이언트와의 연결이 끊어지기 전까지는 계속 발생합니다.따라서 폼에 버튼하나를 올려놓고, 버튼을 누르면 클라이언트와의 연결을 모두 종료하는 처리를 만들었다며, 이곳에서는 어떠한VCL컴포넌트들에 대한 처리를 직접해서는 않됩니다. 메모장컴포넌트에 클라이언트종료처리를표시해 주는 문자출력조차도 데드락을 발생시킬 수 있습니다.

이유는 OnConnect, OnDisConnect, OnExecute등등의 이벤트들은 모두 하나의 이벤트를 처리하는 루틴들이지만 Indy의 내부에서는모두 스레드로 관리하는 핸들에서 이 이벤트를 발생시키기 때문에, 이 이벤트들은 스레드내부에서 처리된다고 생각해야 하기 때문이다.

따라서VCL을 처리하기 위해서는 다른 방법으로 처리를 해야합니다. (Indy문서에서는OnDisConnect이벤트에서 Synchronize함수를 사용하지 말 것을 권고하고 있지만 아마도 Indy의 모든 이벤트에서 Synchronize를 사용하는 것은안전하지 않을 것으로 생각이 된다. 델파이에서 직접 만든 스레드라면 VCL의 처리를 위해 Synchronize를 사용하라고 되어 있지만, Indy에서는 이것 조차도 데드락을 발생시킬 수 있기 때문에 사용하지 말라고 되어 있다.)

해결방법

각각의 이벤트(OnConnect, OnDisConnect, OnExecute등등)에서 델파이의 기본 VCL들의 내용을 직접변경하지 말고, 윈도우메시지를 사용하는 것입니다.이 방법은 멀티미디어 타이머를 사용할 때 예제로 나오는 방법이기도 한데, 멀티미디어 타이머의 처리 프로시저내에서는 다른 컴포넌트와 관련된 처리나 시간이 걸리는 처리를 하면 않된다고 나오는 데 이유는 멀티미디어 타이머의 해상도에 따라서 이러한 처리들이 문제를 발생시킬 수 있기 때문입니다. 따라서 멀티미디어 타이머의 처리프로시저내에서는 PostMessage함수를 사용하여 메시지만 날려주는 처리만으로 간단하게 끝내고, 메시지처리 함수에서 사용자가 필요한 처리를 하게 만듭니다. Indy에서도 이러한 방법을 사용하면 됩니다. 예를 들면 사용자정의 메시지를 하나 만들고, OnDisConnect이벤트에서는 PostMessage로 이 메시지만 부르고 처리를 끝내고, 사용자정의 메시지처리부분에서 필요한 처리를 하게 만들면 되는 것입니다.

위의 내용은 제가 직접 Indy를 사용한 프로그래밍을 하면서 여러가지 문제해결방법을 찾으면서 알아낸 것으로 데드락이 발생하는 것은 Indy만의 문제가 아닌것을 알고 쓰는 내용입니다. 데드락을 피하는 방법은 위의 방법외에도 있을 수 있습니다.

하지만 테스트 결과 위의 방법으로 충분히 데드락의 발생을 피할 수 있어 이렇게 내용을 씁니다.

Indy관련 문서에서 이런 내용이 있는 것을 본 것이 기억납니다. "Indy를 잘 사용하려면 당신이 알고 있는 다른 소켓의 기본지식을 모두 잊어라!" 즉, Indy는 우리가 프로그래밍을 하면서 알고 있었던 기본소켓의 특성을 가지지 않고, 완전히 다른 라이브러리를 익힌다고 생각을 해야 한다는 말인것 같습니다.