네트워크 및 보안/윈도우즈 소켓 통신 프로그램

2.5 자주 사용하는 윈속 함수[TCP/IP 소켓 통신 프로그래밍 with 윈도우즈]

언제나휴일 2016. 4. 7. 12:23
반응형

2.5 자주 사용하는 윈속 함수

 윈속을 이용하여 TCP/IP 통신 프로그램을 작성할 때 사용하는 기본적인 함수와 자료형을 살펴봅시다. 여기에서 소개하는 함수는 다음 장부터 실제 사용하는 방법을 예제 코드와 함께 설명할게요. 여기에서는 먼저 윈속 라이브러리의 기본적인 함수와 자료 형식을 간략하게 살펴봅시다.

 

2.5.1 socket, closesocket

 윈속 라이브러리에서는 소켓 생성 함수와 소켓을 닫는 함수를 제공합니다.

 

소켓을 생성하는 함수

SOCKET socket(int af,int type,int protocol);

실패 시: -1(SOCKET_ERROR) 반환

af: 네트워크 주소 체계

    #define AF_INET          2               //IPv4

    #define AF_INET6         23             //IPv6

type: 소켓 타입

    #define SOCK_STREAM  1              //스트림 , TCP 프롤토콜의 전송 방식

    #define SOCK_DGRAM  2              //데이터 그램, UDP 프로토콜의 전송 방식

    #define SOCK_RAW      3              //RAW 소켓, 가공하지 않은 소켓

protocol: 프로토콜

    #define IPPROTO_TCP    6             //TCP 프로토콜

    #define IPPROTO_UDP   17            //UDP 프로토콜

    #define IPPROTO_RAW   255          //RAW

소켓을 닫는 함수

int closesocket(SOCKET sock);

실패 시: -1(SOCKET_ERROR) 반환

 

 소켓을 생성하는 함수는 socket입니다. 이 함수의 입력 인자로는 네트워크 주소 체계와 소켓 타입 및 프로토콜이 있습니다.

 

 IPv4를 사용할 때 네트워크 주소 체계는 AF_INET(PF_INET과 같음) 상수를 사용합니다. 그리고 소켓 타입은

TCP 프로토콜로 통신할 때 SOCK_STREAM, UDP 프로토콜로 통신할 때 SOCK_DGRAM을 사용하고 가공하지 않은 방식으로 직접 패킷을 수신하거나 패킷을 만들어 통신할 때 SOCK_RAW를 사용합니다.

 

 이 외에도 윈속은 다양한 네트워크 주소 체계에서 다양한 방식의 소켓 타입과 프로토콜로 통신 프로그램을 제작할 수 있습니다. 하지만 이 책에서는 TCP/IP 통신 프로그램을 제작하는 방법만 다루고 있습니다.

 

2.5.2 bind

 윈속 라이브러리에서는 소켓과 로컬 네트워크 인터페이스와 결합하는 bind 함수를 제공합니다.

 

소켓과 로컬 네트워크 인터페이스를 결합하는 함수

int bind(SOCKET sock,const struct sockaddr *addr,int addrlen);

실패 시: -1(SOCKET_ERROR) 반환

소켓 주소 구조체

typedef struct sockaddr {

    u_short sa_family;                    //소켓 주소 체계

    CHAR sa_data[14];                   //상위 14바이트 주소

} SOCKADDR, *PSOCKADDR, FAR *LPSOCKADDR;

IPv4 소켓 주소 구조체

typedef struct sockaddr_in {

    short   sin_family;                    //소켓 주소 체계, IPv4 AF_INET(PF_INET과 같음)

    USHORT sin_port;                    //포트 번호

    IN_ADDR sin_addr;                   //IPv4 주소

    CHAR sin_zero[8];                    //항상 0

} SOCKADDR_IN, *PSOCKADDR_IN;

 

 소켓 통신 프로그램 중에 서버는 로컬 네트워크 인터페이스와 결합하는 과정은 필수입니다. 서버에서는 약속한 주소(서버 네트워크 주소와 포트)로 결합하면 클라이언트 측에서는 연결을 요청할 수 있습니다.

 

 윈속을 사용하여 IPv4 소켓 주소를 표현할 때는 struct sockaddr_in 형식 변수에 소켓 주소 체계를 AF_INET으로 지정하고 자신의 IPv4 주소와 사용할 포트 번호를 지정합니다. 그리고 IPv4에서는 사용하지 않는 나머지 부분은 항상 0으로 설정합니다.

 

struct sockaddr_in servaddr={0};

servaddr.sin_family = AF_INET;

servaddr.sin_addr = GetDefaultMyIP();

servaddr.sin_port = htons(10200);

int re = 0;

re = bind(sock,(struct sockaddr *)&servaddr,sizeof(servaddr));

if(re == -1)

{

    printf("bind 실패\n");

    return 0;

}

printf("bind 성공\n");

 

2.5.3 listen

 TCP 프로토콜을 이용하여 서버를 구축할 때는 동시 연결을 시도하는 백 로그 큐의 크기를 설정합니다. 이는 서버에서 수용할 수 있는 최대 클라이언트 수를 의미하는 것이 아닙니다. 동시에 연결을 시도하는 최대 클라이언트 수를 의미합니다.

 

TCP 프로토콜을 사용하는 소켓의 동시 연결 백 로그 큐의 크기 설정하는 함수

int listen(sock,int backlog);

실패 시: -1(SOCKET_ERROR) 반환

 

2.5.4 connect

 클라이언트 측에서 서버와 연결을 요청할 때 사용하는 함수입니다. TCP 프로토콜을 이용하는 클라이언트는 반드시 연결을 성공해야 서버와 통신할 수 있습니다. 반면 UDP 프로토콜을 이용하는 클라이언트는 명시적으로 연결하지 않아도 서버와 통신할 수 있습니다.

 

연결을 요청하는 함수

int connect(SOCKET sock, const struct sockaddr otheraddr,int addrlen);

실패 시: -1(SOCKET_ERROR) 반환

 

 연결을 요청할 때도 bind 함수처럼 두 번째 인자로 소켓 주소를 전달해야 합니다. bind 함수에 사용한 소켓 주소는 자신의 호스트 주소로 설정하는데 연결을 요청하는 connect 함수의 두 번째 인자에는 연결하고자 하는 상대 호스트 주소로 설정합니다.

 

2.5.5 accept

 TCP 프로토콜을 이용하는 서버에서는 클라이언트의 연결을 수락해야 합니다. 이 때 사용하는 함수가 accept함수입니다.

 

클라이언트 연결을 수락하는 함수

SOCKET accept(SOCKET sock,struct sockaddr *clientaddr,int *addrlen);

실패 시: -1(SOCKET_ERROR) 반환

 

 accept 함수를 호출할 때는 연결을 시도한 클라이언트의 주소를 기억할 소켓 주소 형식 변수의 메모리 주소를 전달하면 accept 함수 내부에서 클라이언트 주소를 설정합니다. 그리고 accept 함수에서는 클라이언트와 통신에 사용할 소켓을 반환합니다.

 

2.5.6 send, recv

 연결 상태의 서버와 클라이언트는 상대에게 메시지를 보내거나 수신할 때 send recv 함수를 사용합니다.

 

메시지를 전송하는 함수

int send(SOCKET sock, const char * buf, int len,int flags);

실패 시: -1(SOCKET_ERROR) 반환, 성공 시: 송신한 바이트 수

메시지를 수신하는 함수

int recv(SOCKET sock, char * buf, int len,int flags);

실패 시: -1(SOCKET_ERROR) 반환, 성공 시: 수신한 바이트 수, 0을 받았으면 상대 소켓이 닫힌 것임

flags: 세부적인 전송 및 수신 방법

    MSG_OOB : 메시지 대역을 사용하지 않고 별도의 대역 사용

    MSG_DONTROUTE : 라우터 테이블을 참조하지 않음

 

 send 함수에서는 두 번째 인자로 송신할 데이터가 있는 메모리 주소, 세 번째 인자로 송신할 바이트 수를 전달합니다. 그리고 반환 값으로 실제 송신한 바이트 수를 반환합니다. recv 함수도 send 함수와 입력 인자가 비슷합니다. 단지 수신할 버퍼의 주소라는 것과 수신 요청할 바이트 수라는 점이 다를 뿐입니다.

 

 send recv 함수의 네 번째 인자로 특별한 방식의 송수신 플레그를 지정할 수 있는데 특별한 경우에 사용합니다.

 

2.5.7 sendto, recvfrom

 UDP 통신처럼 연결하지 않은 상태에서 메시지를 전송하거나 수신할 때 sendto 함수와 recvfrom 함수를 사용합니다.

 

비연결 상태에서 메시지를 전송하는 함수

int sendto(SOCKET sock, const char * buf, int len, int flags,const struct sockaddr * to, int addrlen);

실패 시: -1(SOCKET_ERROR) 반환, 성공 시: 송신한 바이트 수

비연결 상태에서 메시지를 수신하는 함수

int recvfrom(SOCKET sock, char * buf, int len, int flags, struct sockaddr * from,int *addrlen);

실패 시: -1(SOCKET_ERROR) 반환, 성공 시: 수신한 바이트 수, 0을 받았으면 상대 소켓이 닫힌 것임

 

 비연결 상태에서 전송하는 sendto 함수는 send 함수에 사용한 입력 인자 외에 상대 소켓 주소와 주소 길이를 추가로 전달해야 합니다.

 

 그리고 비연결 상태에서 수신하는 recvfrom 함수도 상대 소켓 주소와 주소 길이를 위한 인자를 전달해야 합니다.

 

2.6 앞으로 사용할 파일

 이 책에서 다루는 예제에서 공통으로 사용할 부분을 소개할게요. 참고로 이 책에서는 콘솔 응용프로그램 형태와 윈도우즈 응용 프로그램 형태를 모두 사용할 것입니다.

 

#pragma once

#include <WinSock2.h>

#include <Windows.h>

#include <process.h>

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <malloc.h>

#pragma comment(lib,"ws2_32")

IN_ADDR GetDefaultMyIP();

[소스 2.1] 공통으로 사용할 파일 common.h

 

#include "common.h"

IN_ADDR GetDefaultMyIP()

{

    char localhostname[MAX_PATH];

    IN_ADDR addr={0,};

    if(gethostname(localhostname, MAX_PATH) == SOCKET_ERROR){ return addr; }

    HOSTENT *ptr = gethostbyname(localhostname);

    while(ptr && ptr->h_name)

    {

        if(ptr->h_addrtype == PF_INET)

        {

            memcpy(&addr, ptr->h_addr_list[0], ptr->h_length);

            break;

        }

        ptr++;

    }

    return addr;

}

[소스 2.2] 공통으로 사용할 파일 common.cpp

반응형