Chapter 02. 윈도우 소켓 시작하기
학습 목표 윈속 함수의 오류 처리 방법을 익힌다. 윈속 초기화와 종료 방법을 익힌다. 소켓을 생성하고 닫는 방법을 익힌다.
오류 유형 ① 오류를 처리할 필요가 없는 경우 ② 리턴값만으로 오류를 처리하는 경우 윈속 함수 오류 처리 (1/2) 오류 유형 ① 오류를 처리할 필요가 없는 경우 리턴값이 없거나 호출시 항상 성공하는 일부 소켓 함수 ② 리턴값만으로 오류를 처리하는 경우 WSAStartup() 함수 ③ 리턴값으로 오류 발생을 확인하고, 구체적인 내용은 오류 코드를 이용하여 확인하는 경우 대부분의 소켓 함수
오류 코드 얻기 사용 예 윈속 함수 오류 처리 (2/2) int WSAGetLastError (void) ; if (소켓함수(...) == 오류) { int errcode = WSAGetLastError(); printf(errcode에 해당하는 오류 메시지); }
FormatMessage() 함수 오류 코드를 문자열로 바꾸기 (1/4) DWORD FormatMessage ( DWORD dwFlags, // ① 옵션 LPCVOID lpSource, // NULL DWORD dwMessageId, // ② 오류 코드 DWORD dwLanguageId, // ③ 언어 LPTSTR lpBuffer, // ④ 오류 문자열 시작 주소 DWORD nSize, // 0 va_list* Arguments // NULL ) ; 성공: 오류 메시지의 길이, 실패: 0
err_quit() 함수 정의 오류 코드를 문자열로 바꾸기 (2/4) #include <winsock2.h> #include <stdlib.h> 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); }
err_quit() 함수 사용 예 오류 출력 화면 오류 코드를 문자열로 바꾸기 (3/4) if (socket(...) == SOCKET_ERROR) err_quit("socket()"); if (bind(...) == SOCKET_ERROR) err_quit("bind()"); err_quit() 함수에 전달한 문자열 오류 코드에 해당하는 문자열
err_display() 함수 정의 오류 코드를 문자열로 바꾸기 (4/4) #include <winsock2.h> #include <stdlib.h> 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", msg, (LPCTSTR)lpMsgBuf); LocalFree(lpMsgBuf); }
윈속 초기화 함수 wVersionRequested lpWSAData 윈속 초기화와 종료 (1/3) 프로그램이 요구하는 최상위 윈속 버전. 하위 8비트에 주(major) 버전을, 상위 8비트에 부(minor) 버전을 넣어서 전달함 lpWSAData WSADATA 타입 변수의 주소. 시스템에서 제공하는 윈속 구현에 대한 세부 사항을 얻을 수 있음 int WSAStartup ( WORD wVersionRequested, LPWSADATA lpWSAData ) ; 성공: 0, 실패: 오류 코드
윈속 종료 함수 윈속 초기화와 종료 (2/3) int WSACleanup (void) ; 성공: 0, 실패: SOCKET_ERROR
예제 코드 윈속 초기화와 종료 (3/3) #include <winsock2.h> int main(int argc, char* argv[]) { // 윈속 초기화 WSADATA wsa; if(WSAStartup(MAKEWORD(2,2), &wsa) != 0) return -1; MessageBox(NULL, "윈속 초기화 성공", "성공", MB_OK); // 윈속 종료 WSACleanup(); return 0; }
소켓 생성과 닫기 (1/6) 소켓 생성 함수 사용자가 요청한 프로토콜을 이용하여 통신을 할 수 있도록 내부적으로 리소스를 할당하고, 이를 접근할 수 있는 일종의 핸들값(SOCKET 타입, 32비트 정수)인 소켓 디스크립터(socket descriptor)를 리턴 SOCKET socket ( int af, // 주소 체계 int type, // 소켓 타입 int protocol // 프로토콜 ) ; 성공: 새로운 소켓, 실패: INVALID_SOCKET
소켓 생성과 닫기 (2/6) 주소 체계 #define AF_UNIX 1 /* local to host (pipes, portals) */ #define AF_INET 2 /* internetwork: UDP, TCP, etc. */ #define AF_IMPLINK 3 /* arpanet imp addresses */ #define AF_PUP 4 /* pup protocols: e.g. BSP */ #define AF_CHAOS 5 /* mit CHAOS protocols */ #define AF_NS 6 /* XEROX NS protocols */ #define AF_IPX AF_NS /* IPX protocols: IPX, SPX, etc. */ #define AF_ISO 7 /* ISO protocols */ #define AF_OSI AF_ISO /* OSI is ISO */ ...
소켓 타입 사용할 프로토콜의 특성 소켓 생성과 닫기 (3/6) 예) TCP와 UDP 프로토콜 설정(1) 소켓 타입 특성 SOCK_STREAM 신뢰성 있는 데이터 전송 기능 제공 연결형 프로토콜 SOCK_DGRAM 비신뢰적인 데이터 전송 기능 제공 비연결형 프로토콜 사용할 프로토콜 주소 체계 소켓 타입 TCP AF_INET SOCK_STREAM UDP SOCK_DGRAM
프로토콜 주소 체계와 소켓 타입이 같더라도 이에 해당하는 프로토콜이 두 개 이상 존재할 경우 프로토콜을 명시적으로 지정 소켓 생성과 닫기 (4/6) 프로토콜 주소 체계와 소켓 타입이 같더라도 이에 해당하는 프로토콜이 두 개 이상 존재할 경우 프로토콜을 명시적으로 지정 예) TCP와 UDP 프로토콜 설정(2) 예) TCP와 UDP 프로토콜 설정(3) 사용할 프로토콜 주소 체계 소켓 타입 프로토콜 TCP AF_INET SOCK_STREAM IPPROTO_TCP UDP SOCK_DGRAM IPPROTO_UDP 사용할 프로토콜 주소 체계 소켓 타입 프로토콜 TCP AF_INET SOCK_STREAM UDP SOCK_DGRAM
소켓 종료 함수 소켓을 닫고 관련 리소스를 반환 소켓 생성과 닫기 (5/6) int closesocket ( SOCKET s ) ; 성공: 0, 실패: SOCKET_ERROR
예제 코드 소켓 생성과 닫기 (6/6) int main(int argc, char* argv[]) { // 윈속 초기화 ... // 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); // 윈속 종료 }