본문 바로가기
전공 수업/컴퓨터 통신(Computer Communication)

[4주 차] - Windows 소켓 시작하기

by TwoJun 2022. 9. 19.

    과목명 : 컴퓨터 통신(Computer communication)

수업일자 : 2022년 09월 19일 (월)

 

 

 

 

1. 오류 처리(Error Handling)

1-1 정의

- 네트워크 프로그램에서는 여러 원인 때문에 오류가 발생할 수 있으며 발생 확률도 비교적 높습니다. 이에 따라 함수 호출 시, 오류를 체크하여 사용자에게 오류 내용을 알려주는 것이 매우 중요합니다.

 

- 학습할 윈속 함수는 오류 처리 방법을 다음과 같이 세 가지로 나눌 수 있습니다.

 

 

(1) 오류를 처리할 필요가 없는 경우

- return 값이 존재하지 않거나 호출 시 항상 성공하는 일부 소켓 함수 

 

(2) return 값만으로도 오류를 처리하는 경우

- WSAStartup() 함수

 

(3) return 값으로 오류를 확인하고, 구체적인 오류 내용은 코드로 확인하는 경우

- 대부분의 소켓 함수

 

 

 

 

1-2  WSAGetLastError() 함수

- 소켓 함수 호출 결과 오류가 발생했다면 해당 함수를 사용하여 오류 코드를 얻을 수 있습니다.

- int WSAGetLastError(void);

 

- 해당 함수의 사용 예시는 아래 코드와 같습니다.

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

 

 

- 해당 함수의 return 값을 화면에 그대로 표시할 경우 사용자가 오류 코드의 의미를 직접 알아내야 하는 불편함이 생깁니다. 이에 따라 FormatMessage() 함수를 사용하면 오류코드에 대응하는 오류 메시지를 얻을 수 있습니다.

 

 

- err_display() 함수는 err_quit() 함수에서 마지막 한 행을 제거하고 출력함수로 MessageBox() 대신 printf()를 사용합니다.

- 이 함수는 오류 메세지를 출력하되, 응용 프로그램을 종료하지 않습니다.

 

 

 

 

 

 

2. 윈속의 초기화와 종료

아래 그림은 모든 윈속 응용 프로그램의 공통 구조를 나타냅니다. 2번에서는 윈속의 초기화와 종료 방법을 알아보겠습니다.

윈속의 초기화, 종료

 

 

2-1 윈속 초기화 함수 : WSAStartup() 

- 모든 윈속 프로그램은 소켓 함수를 호출하기 전에 반드시 윈속 초기화 함수인 WSAStartup()을 호출해야 합니다.

 

- WSAStartup() 함수는 사용할 윈속 버전을 요청함으로써 윈속 라이브러리(WS2_32.DLL)를 초기화합니다.

 

- 해당 함수는 오류 코드를 직접 Return할 수 있도록 설계되어 있으며 구조는 아래와 같습니다.

 

int WSAStartup(

    WORD wVersionRequested,

    LPWSADATA lpWSAData

);

 

 

(1) wVersionRequested

- 프로그램이 요구하는 최상위 윈속 버전입니다.

- 하위 8비트에 주 버전, 상위 8비트에 부 버전을 넣어서 전달합니다.

- Ex) 윈속 3.2 version, 0x0203 또는 MAKEWORD(3, 2)

 

(2) lpWSAData 

- WSADATA 구조체를 전달하면 이를 통해 윈도우 운영체제가 제공하는 윈속 구현에 관한 정보를 얻을 수 있습니다.

 

 

 

 

 

2-2 윈속 버전 지원

운영체제 윈속 버전
Windows 95 1.1(2.2)
Windows 98 / ME,
Windows NT / 2000 / XP / 2003 Server,
Windows Vista / 2008 Server / 7
2.2
Windows CE 1.1(2.2)

 

 

 

 

 

2-3 윈속 종료 함수 :  WSACleanup() 

- 프로그램을 종료할 때는 윈속 종료 함수인 WSACleanup() 함수를 호출해야 합니다.

- 해당 함수는 윈속 사용을 중지함을 운영체제에게 알리고 사용됐던 시스템 자원(Resource)를 반환하게 됩니다. 

- 함수 호출에 실패할 경우 WSAGetLastError() 함수를 호출하여 구체적인 오류 코드를 얻을 수 있습니다.

- 형식은 아래와 같습니다.

 

int WSACleanup(void);

 

 

 

 

2-4 윈속을 초기화하고 종료하는 간단한 프로그램 작성

// Chapter02.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <winsock2.h>
#pragma comment(lib, "ws2_32")

int _tmain(int argc, _TCHAR* argv[])
{
	// 윈속 초기화
	WSADATA wsa;
	if(WSAStartup(MAKEWORD(2,2), &wsa) != 0) return 1;
	MessageBox(NULL, "윈속 초기화 성공", "알림", MB_OK);

	//윈속 종료
	WSACleanup();
	return 0;
}

 

 

 

해당 프로그램을 실행하면 윈속 초기화가 성공했음을 아래와 같이 알 수 있습니다.

윈속이 초기화되어 출력된 메시지 박스

 

 

 

 

 

 

3. 소켓 생성과 닫기

위의 과정들을 통해 윈속이 초기화되었고, 이제는 소켓을 생성할 준비가 되었습니다.

아래 그림은 소켓을 생성하고 닫는 방법에 대한 그림이며 해당 방법들을 알아보도록 하겠습니다.

소켓의 생성과 닫기

 

 

 

3-1 소켓 생성하기

- 소켓을 사용하여 통신하기 위한 기본 조건은 통신하는 양단이 같은 프로토콜을 사용해야 합니다.

- 이에 따라 TCP로 통신할 것인지, UDP로 통신할 것인지 정의해야 합니다.

 

- socket() 함수는 사용자가 요청한 프로토콜을 이용해 통신할 수 있도록 내부적으로 자원을 할당하고, 이에 접근할 수 있는 핸들값(SOCKET 타입, 32비트 정수)을 리턴하며 이를 소켓 디스크립터(Socket descriptor)라고 합니다.

 

- 소켓 디스크립터는 각종 소켓 함수를 호출할 때, 인자로 전달해 사용하며 형식은 아래와 같습니다.

SOCKET socket(
    int af,
    int type,
    int protocol
}

(1) af : 주소 체계를 지정하는 인자입니다.

(2) type : 소켓 타입을 지정하는 인자입니다.

(3) protocol : 어떤 프로토콜을 사용할 것인지에 대한 인자입니다.

 

해당 인자들의 의미를 하나씩 알아보겠습니다.

 

 

 

 

 

3-2 주소 체계

- 네트워크 통신을 하기 위해 상대를 지정할 수 있는 주소가 필요하고 이러한 주소 체계는 프로토콜의 따라 달라지므로 주소 체계 지정은 사용한 프로토콜을 지정하기 위한 첫 번째 단계입니다.

 

- winsock2.h 또는 ws2def.h 헤더 파일을 보면 아래 코드와 같이 AF로 시작하는 상수를 찾을 수 있으며 사용할 프로토콜에 대응하는 값을 선택해 socket() 함수의 첫 번째 인자로 전달합니다.

#define AF_INET     2    // Internetwork : UDP, TCP etc...

#define AF_INET6   23    // Internetwork version 6

#define AF_IRDA    26    // IrDA

#define AF_BTH     32    // Bluetooth RFCOMM / L2CAP protocols

 

 

 

 

3-3 소켓 타입

- 통신을 위해 사용할 프로토콜의 특성을 나타내는 값이며 자주 사용하는 소켓 타입은 아래의 표와 같습니다.

소켓 타입 특성
SOCK_STREAM 신뢰성 있는 데이터 전송 기능 제공, 연결형 프로토콜 (TCP)
SOCK_DGRAM 신뢰성 없는 데이터 전송 기능 제공, 비연결형 프로토콜 (UDP)

 

 

- TCP, UDP 프로토콜을 사용해 통신하려면, 아래의 주소 체계와 소켓 타입으로 설정해야 합니다.

- 주소 체계가 동일해도 소켓 타입을 다르게 정할 수 있으며 결과적으로 사용할 프로토콜의 종류가 달라지게 됩니다.

사용할 프로토콜 주소 체계 소켓 타입
TCP AF_INET 또는 AF_INET6 SOCK_STREAM
UDP AF_INET 또는 AF_INET6 SOCK_DGRAM

 

 

 

 

 

3-4 프로토콜 타입

- 주소 체계와 소켓 타입이 동일해도 이에 해당하는 프로토콜이 두 개 이상 존재할 수 있는 상황에선 명시적으로 프로토콜의 타입을 지정해 줘야 합니다.

 

- Socket() 함수의 세 번째 인자에 해당하며 TCP, UDP 프로토콜을 사용하려면 주소 체계와 소켓 타입, 그리고 프로토콜 타입을 아래와 같이 지정할 수 있습니다.

 

사용할 프로토콜 주소 체계 소켓 타입 프로토콜 타입
TCP AF_INET 또는 AF_INET6 SOCK_STREAM IPPROTO_TCP
UDP AF_INET 또는 AF_INET6 SOCK_DGRAM IPPROTO_UDP

 

 

- TCP, UDP 프로토콜은 소켓 타입을 지정함으로써 프로토콜이 결정되므로 세 번째 인자는 0을 주게 됩니다.

사용할 프로토콜 주소 체계 소켓 타입 프로토콜 타입
TCP AF_INET 또는 AF_INET6 SOCK_STREAM 0
UDP AF_INET 또는 AF_INET6 SOCK_DGRAM 0

 

 

 

 

 

 

3-5 소켓 닫기

- 소켓을 사용한 통신을 마치면 시스템에게 관련 자원을 반환해야 합니다.

- closesocket() 함수를 사용하면 소켓을 종료하고 자원을 반환합니다.

int closesocket(
    SOCKET s
)

 

 

 

 

 

3-6 소켓을 생성하고 닫는 프로그램 작성

// SocketOpenClose.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <winsock2.h>
#pragma comment(lib, "ws2_32")

	// 소켓 함수 오류 출력 후 종료 
	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, (LPTSTR)&lpMsgBuf, msg, MB_ICONERROR);
		LocalFree(lpMsgBuf);
		exit(1);
	}

int _tmain(int argc, _TCHAR* argv[])
{
	// 윈속 초기화
	WSADATA wsa;
	if(WSAStartup(MAKEWORD(2,2), &wsa) != 0) return 1;
	MessageBox(NULL, "윈속 초기화 성공", "알림", MB_OK);

	// socket()
	SOCKET tcp_sock = socket(AF_INET, SOCK_STREAM, 0);
	if(tcp_sock == INVALID_SOCKET) err_quit("socket()");
	MessageBox(NULL, "TCP 소켓 생성 성공", "알림", MB_OK);

	// closesocket()
	closesocket(tcp_sock);


    //윈속 종료
	WSACleanup();
	return 0;
}

 

 

 

해당 프로그램을 컴파일, 링크 후 실행되는 결과는 아래와 같이 소켓이 정상적으로 생성됨을 알 수 있습니다.

소켓 생성이 정상적으로 완료되었다는 메시지 박스 출력

 

 

 

 

 

- 학부에서 수강했던 전공 수업 내용을 정리하는 포스팅입니다.

- 내용 중에서 오타 또는 잘못된 내용이 있을 시 지적해 주시기 바랍니다.

댓글