윈도우 소켓 오류 처리

bolee·2022년 4월 4일
1

윈속 함수는 오류 처리 방법에 따라 아래 세가지 유형으로 나눌 수 있다.

  • 오류를 처리할 필요가 없는 경우: 리턴 값이 없거나 호출 시 항상 성공하는 일부 소켓 함수
  • 리턴 값만으로 오류를 처리하는 경우: WSAStartup() 함수
  • 리턴 값으로 오류 발생을 확인하고, 구체적인 내용은 오류 코드로 확인하는 경우: 대부분의 소켓 함수

첫 번째, 두 번째 유형의 특별한 경우이므로 여기에서는 대부분의 소켓 함수에 적용되는 마지막 유형을 처리하는 방법을 살펴볼 것이다.
소켓 함수 호출 결과 오류가 발생했나면 WSAGetLastError()함수를 이용해 요류 코드를 얻을 수 있다.

int WSAGetLastError(void);

사용 예는 아래와 같다.

if (소켓 함수(...) == 실패)
{
	int errcode = WSAGetLastError();
    printf(errcode에 해당하는 오류 메세지);
}

WSAGetLastError() 함수의 리턴 값을 화면에 그대로 표시할 경우 사용자가 직접 요류 코드의 의미를 알아내야한다. 따라서 해당 요류 코드에 대응하는 오류 메세지를 출력하는 것이 바람직한다.
FormatMessage() 함수를 사용하면 오류 코드에 대응하는 오류 메세지를 얻을 수 있다. 이 함수의 인자가 취할 수 있는 다양한 값과 의미는 MSDN(비주얼 스튜디어 2008 이하 버전)이나 비주얼 스튜디오 설명서(비주얼 스튜디오 2010 이상 버전)을 참고하면 된다. 여기서는 가장 흔히 사용하는 인자 값 위주로 소개하겠다.

// 성공: 오류 메세지 길이, 실패: 0
DWORD FromatMessage(
	DWORD dwFlags,
    LPCVOID lpSource,
    DWORD dwMessageId,
    DWORD dwLanguageId,
    LPTSTR lpBuffer,
    DWORD nSize,
    va_list *Arguments
);
  • dwFlags: FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM 값을 사용한다. dwFlags를 이처럼 설정할 경우 lpSource에는 NULL 값, nSize에는 0, Arguments에는 NULL 값을 사용하면 된다.
    • FORMAT_MESSAGE_ALLOCATE_BUFFER: 오류 메세지를 저장할 공간을 FormateMessage() 함수가 알아서 할당한다는 의미
    • FORMAT_MESSAGE_FROM_SYSTEM: 운영체제로부터 오류 메세지를 가져온다는 의미
  • dwMessageId: 오류 코드를 나타내며, MSAGetLastError() 함수의 리턴값을 여기에 넣는다.
  • dwLanguageId: 오류 메세지를 표시할 언어를 나타내며, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)를 사용하면 사용자가 제어판에서 설정한 기본 언어로 오류 메세지를 얻을 수 있다.
  • lpBuffer: 오류 메세지의 시작 주소가 여기에 저장된다. 오류 메세지를 저장할 공간은 FomatMessage()가 알아서 할당하기 때문에 사용자는 주소 값을 저장할 변수를 여기에 놓어주면 된다. 오류 메세지 사용을 마치면 LocalFree() 함수를 사용해 시스템이 할당한 메모리를 반환해야하는 것을 주의해야한다.

위의 FormateMessage() 함수를 적용한 요류 처리 함수 err_quit(char *msg) 함수는 아래와 같다. 이 함수는 msg 인자로 전달된 문자열과 더물어 현재 발생한 오류 메세지를 화면에 메세지 상자로 표시하고, 응용 프로그램을 종료하는 역할을 한다.

void err_quit(char *msg)
{
	LPVOID lpMsgBuf;
    FormatMessage(
    	FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
        NULL, WSAGetLastError(),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf, 0, NULL);
    MessageBox(NULL, (LPCTSTR) lpMsgBuf, msg, MB_ICONERROR);
    LocalFree(lpMsgBuf);
    exit(1);
}

사용 예는 아래와 같다.

if (socket(...) == INVALID_SOCKET) err_quit("socket()");
if (bind(...) == SOCKET_ERROR) err_quit("bind()");

예를 들어, bind() 함수에서 오류가 발생한다면 다음과 같이 메세지 상자를 화면에 표시하고, <확인> 버튼을 누르면 응용 프로그램을 종료한다.

(2-1 그림)

다른 오류 처리 함수 err_display() 함수는 아래와 같다. 이 함수는 err_quit() 함수에서 마지막 한 행을 제고하고, 출력 함수로 MessageBox() 대신 printf()를 사용한다. 이 함수는 오류 메세지를 출력하지만 응용프로그램은 종료하지 않는다.
사소한 오류의 경우 응용 프로그램을 종료한느 것은 바람직하지 않기 때문에 이 경우 사용될 수 있다.

void err_display(char *msg)
{
	LPVOID lpMsgBuf;
    FormatMessage(
    	FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
        NULL, WSAGetLastError(),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf, 0, NULL);
    printf("[%s] %s\n", msg, (char *) lpMsgBuf);
    LocalFree(lpMsgBuf);
}

bind() 함수에서 오류가 발생하는 상황에서 err_quit() 대신 err_display()를 사용한다면 아래와 같은 오류 메세지를 출력한다.

(2-2 그림)

참고 자료
김성우 저, "TCP/IP 윈도우 소켓 프로그래밍", 한빛아카데미, 2018

0개의 댓글