Borland Socket은 소켓을
이용하여 작업을 처리하는 도중 문제가 발생하게 되면 항상 OnError 이벤트를 발생하게 된다. 특히 TserverSocket의 경우엔 수많은
TserverClientWinSocket의 모든 에러메세지를 TserverSocket의 OnClientError에서 수신받을수 있다.
OnClientError 이벤트가 발생할땐 넘어오는 파라메터중 다음의 3가지에 대해서 알아보도록 하자.
Socket:
TcustomWinSocket
ErrorEvent:
TerrorEvent
var ErrorCode:
Integer
Socket은 문제가 발생한 소켓의 포인터이다. ErrorEvent는 어떠한
작업을 진행하다가 에러가 발생했는지를 알려주게 된다. 만약 값이 eeSend 라면 Send를 하기 위해 작업을 진행하다가 에러가 발생했다는 것이
된다. 마지막으로 ErrorCode는 GetLastError를 이용하여 얻어낸 에러 코드이다. 에러코드는 코드 번호에 따라 오류 내용이 서술되어
있으며 자세한 내용은 Winsock 관련서적을 참고하거나 MSDN을 참고하면 자세한 정보를 얻을수 있다. 특히 Errorcode의 경우엔
Code값을 0으로 설정해 주지 않으면 오류번호를 가진 메시지 박스가 화면에 뜨게 되므로 원만한 서버의 운영을 위해선 반드시 ErrorCode를
0으로 설정해주어야 한다.
대부분의 사용자들은 에러이벤트를 다음과 같이 처리한다.
void __fastcall TfrmMain::ServerSocketClientError(TObject
*Sender,
TCustomWinSocket *Socket, TErrorEvent
ErrorEvent, int &ErrorCode)
{
String
Temp;
Temp.printf("수신 Socket Error -> ErrorCode : %d",
ErrorCode);
Memo->Lines->Add(Temp);
/****************************
ErrorCode에 해당하는
적절한 대응책을 구현
****************************/
ErrorCode =
0;
Socket->Close(); // Error가 발생한 Socket을
닫음
}
위의 내용은 만약
TserverClientWinSocket에서 문제가 발생하면 ErrorCode를 0으로 설정하고 Socket을 닫는것이다.
Socket->Close()를 진행하게 되면 OnClientDisconnect 이벤트가 발생하게 되며 최종적으로 처리를 진행하게 된다.
그렇다면 만약 OnClientDisconnect에서 Socket이 종료될 때 에러가 발생해서 종료한것인지 아니면 정상적인 처리에 의해서 종료한
것인지를 확인할려면 어떻게 해야 될까? 답은 간단하다. OnClientError이벤트에서 에러가 발생한 소켓이 어떤놈이라는걸 확인시켜주면 된다.
그리고 OnClientDisconnect에서 종료되는 소켓이 에러가 발생했던 소켓인지 아닌지를 확인하면
되는것이다.
그림으로 보도록 하자.
하지만 대단히 불편한 문제가 발생한다. 바로 Flag를 어디에 두어야
하는가이다. 동적으로 Client의 개수가 변경되기 때문에 유동적인 클라이언트의 수에 대응할수 있는 자료구조인 Tlist정도는 사용하여야 한다. 하지만 겨우 Flag를 설정하기 위해
사용하기엔 작업량이 많아지게 된다. 또 만약 각각의 소켓에 사용자 정보를 저장해야 될 경우가 있다면 어떻게 할것인가? 앞에서 언급한 Tlist를
이용하여 구현하게 되면 많은 작업을 해야 된다. TcustomWinSocket에서는 이러한 문제를 해결하기 위해 Data라는 Property를
제공한다. Data는 void * 를 대입받는다.
즉 필요한 메모리 공간을 new를 이용하여 동적으로 할당한후 내용을
입력하고 Socket->Data에 대입을 하면 해당소켓의 부가적인 정보를 편하게 관리할수 있게
된다.
class UserInfo
{
String
Name; // 사용자 이름저장
DWORD
Flag; // 0 이면 정상종료, 1 이면 에러 발생 후
종료
};
void __fastcall TfrmMain::ServerSocketClientConnect(TObject
*Sender,
TCustomWinSocket *Socket)
{
UserInfo*
pUserInfo = new UserInfo;
pUserInfo->Flag =
0;
Socket->Data =
pUserInfo;
}
void __fastcall TfrmMain::ServerSocketClientError(TObject
*Sender,
TCustomWinSocket *Socket, TErrorEvent
ErrorEvent, int &ErrorCode)
{
((UserInfo*)Socket->Data)->Flag = 1; // Error
설정
/****************************
ErrorCode에
해당하는 적절한 대응책을 구현
****************************/
ErrorCode =
0;
Socket->Close(); // Error가 발생한 Socket을
닫음
}
void __fastcall
TfrmMain::ServerSocketClientDisconnect(TObject
*Sender,
TCustomWinSocket *Socket);
{
if
(((UserInfo*)Socket->Data)->Flag ==
0)
Memo->Lines->Add(((UserInfo*)Socket->Data)->Name + "정상적인
종료");
else
Memo->Lines->Add(((UserInfo*)Socket->Data)->Name + "에러발생
종료");
}
이상으로 그동안 연재했던 강좌를 끝내도록
하겠습니다(강좌도 끝났으니 이젠 말 높여야 겠죠 ? ^^;;). 정말 많이 아쉽기만 하네요. 그동안 게으름 피웠던
부분들과 내용도 처음 의도했던 것보다 부실해서 많은 후회가 되기도 합니다. 기회가 되면 IOCP에 관련된 내용도 강좌를 해보고 싶지만 강좌
연재가 생각보다 어려운 작업임을 이번기회를 통해 알게 되었습니다.
그동안 내용이 매끄럽지 못하고 많이 부족한 글을 읽어준 독자들께도 정말 감사합니다. 중간에 강의가 너무 오랫동안 올라가지 못한 점도 정말 죄송할 따름이구요..(그동안 개인적인 사정이 너무 많어서욤..
^^;;)
앞으로도 볼랜드 포럼에서 계속 활동할 예정입니다. 회사가 볼랜드
제품을 사용하지 않다 보니 빌더6나 델파이6는 아직 제대로 접해보지 못했습니다. 넘 안타까운 현실입니다. 그래서 Q&A에 올라오는 질문도
인디소켓에 관련된 내용은 제가 못할 것 같구요.. winsock에 관련된 내용은 답변을 달도록 열심히
노력하겠습니다.