본문 바로가기

Programming Language/C&C++

소켓 통신 #2 - AF_INET 사용하기 (1)

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

소켓 통신 #2 - AF_INET 사용하기 (1)

- by Tapito


sockaddr_in 구조체를 사용해 인터넷망에 연결해보겠습니다. 여기에서는 편의상 localhost(127.0.0.1):1435번 포트에 접속해 패킷을 교환합니다.

1단계. sockaddr_in 구조체 설정하기

접속할 네트워크, 접속 대상과 포트 번호를 설정합니다.

/* sock_ex01.c */
struct sockaddr_in sockaddr_internet;

/* 주소 체계로서 IPv4를 사용하는 인터넷망에 접속 */
sockaddr_internet.sin_family = AF_INET;
/* 접속할 대상은 127.0.0.1 */
serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
/* 포트 번호는 1435 */
serveraddr.sin_port = htons(1435);

 

inet_addr 함수는 문자열로 된 IP 주소를 4바이트 정수 주소로 변환합니다. 반대로 정수 주소를 문자열 주소로 변환하는 함수는 inet_ntoa입니다.

unsigned long inet_addr(const char * cp);
char * inet_ntoa(struct in_addr in);
/* 사용 예 */
char addr1[] = "127.0.0.1";
struct in_addr addr2 = 0;
char * addr3 = NULL; 
addr2.s_addr = inet_addr(addr1); // addr2  = 0x7F000001
addr3 = inet_ntoa(addr2); // addr3 = "127.0.0.1";

 

htons, htonl, htonll, htonf, htond와 ntohs, ntohl, ntohll, ntohf, ntohd는 바이트 변환 함수입니다. hton과 ntoh는 각각 Host to Network, Network to Host의 약자로 Host는 접속 단말기, Network는 네트워크를 통해 전달되는 경로를 뜻합니다. Intel CPU를 사용하는 개인용 컴퓨터는 Little Endian을 사용하고, 일부 CPU는 Big Endian 방식을 사용하지만, 네트워크는 항상 Big Endian으로 데이터가 전달되기 때문에 이를 변환할 필요가 있습니다. hton과 ntoh 뒤에 붙는 s, l, ll, f, d는 전송되는 데이터의 자료형을 뜻합니다.

  • htons / ntohs : unsigned short 형의 Endian을 변환합니다.
  • htonl / ntohl : unsigned long 형의 Endian을 변환합니다.
  • htonll / ntohll : unslgned long 형의 Endian을 변환합니다.
  • htonf / ntohf : float 형의 Endian을 변환합니다.
  • htond / ntohd : double 형의 Endian을 변환합니다.

2단계. socket 생성하기

socket() 함수를 사용하여 데이터 교환을 위한 소켓을 생성합니다.

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

af: 접속하는 방식을 지정합니다.

  1. AF_UNSPEC: 기타 네트워크에 접속합니다.

  2. AF_INET: IPv4 주소체계를 사용하는 인터넷 망에 접속합니다.

  3. AF_IPX: IPX/SPX 주소체계를 사용하는 네트워크에 접속합니다. Windows 운영체제의 경우 Windows Vista 이후로 지원하지 않는 모드입니다.

  4. AF_APPLETALK: AppleTalk 네트워크에 접속합니다. Windows 운영체제의 경우 Windows Vista 이후로 지원하지 않는 모드입니다.

  5. AF_NETBIOS: NetBIOS 네트워크에 접속합니다. Windows 운영체제의 경우 32비트 버전에서만 이 모드가 지원됩니다.

  6. AF_INET6: IPv6 주소체계를 사용하는 인터넷망에 접속합니다.

  7. AF_IRDA: 적외선 통신(IrDA)으로 상대편에게 접속합니다.

  8. AF_BTH: 블루투스로 상대편에게 접속합니다.

type: 데이터를 교환하는 방식을 지정합니다.

  1. SOCK_STREAM: 입/출력 작동의 스트림 방식으로 데이터를 교환합니다. AF_INET, AF_INET6에서 일반적으로 사용하는 방식입니다.

  2. SOCK_DGRAM: 데이터그램의 형태로 데이터를 교환합니다. AF_INET, AF_INET6에서는 UDP 프로토콜을 사용할 때 이 방식이 지정되며, NetBIOS도 이 방식으로 지정해야 합니다.

  3. SOCK_RAW: 저수준으로 데이터를 교환하고자 할 때 지정됩니다. IPv4 헤더를 수정하고 싶다면 IP_HDRINCL, IPv6 헤더를 수정하고 싶다면 IPV6_HDRINCL 옵션이 함께 지정되어야 합니다.

  4. SOCK_RDM: 멀티캐스트(한 서버에서 라우터에 연결된 모든 컴퓨터로 일괄적으로 데이터를 전송)를 사용하고 싶을 때 지정하는 방식입니다.

  5. SOCK_SEQPACKET: 모름

protocol: 사용할 프로토콜을 지정합니다. IPPROTO_ICMP, IPPROTO_IGMP, BTHPROTO_RFCOMM, IPPROTO_TCP, IPPROTO_UDP, IPPROTO_ICMPV6, IPPROTO_RM 등의 어려운 방식들이 열거되는데 AF_INET에서는 그냥 상수 0만 넣어도 됩니다.

 

그럼 IPv4를 사용하는 인터넷망에 접속하기 위한 소켓을 만들어 보겠습니다.

/* 사용 예 */
SOCKET sock;
sock = socket(AF_INET, SOCK_STREAM, 0);
/* 소켓 만들기에 실패 시 INVALID_SOCKET이 반환 */
if(sock == INVALID_SOCKET) printf("Invalid Socket");

3단계. 생성한 socket으로 상대편에게 접속

socket() 함수로 인터넷망에 접속하기 위한 소켓을 생성했고 sockaddr_in 구조체로 접속할 대상을 지정하였습니다. 상대방에게 접속하기 위한 함수는 connect입니다.

int connect(SOCKET s, const struct sockaddr * name, int namelen);

s: 접속에 필요한 소켓입니다.

name: 소켓으로 접속할 대상입니다.

namelen: name의 크기입니다. sizeof 연산자로 sockaddr_in 구조체의 크기를 입력하면 됩니다.

연결에 성공하면 0을 반환하지만, 실패 시 SOCKET_ERROR 상수를 반환합니다.

/* 사용 예 */
struct sockaddr_in sockaddr_internet;
SOCKET sock;
int connection;

/* -------- 1 단계 -------- */
/* 주소 체계로서 IPv4를 사용하는 인터넷망에 접속 */
sockaddr_internet.sin_family = AF_INET;
/* 접속할 대상은 127.0.0.1 */
serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
/* 포트 번호는 1435 */
serveraddr.sin_port = htons(1435);

/* -------- 2 단계 -------- */
/* 스트림 방식으로 인터넷 망에 접속하기 위한 소켓을 생성 */
sock = socket(AF_INET, SOCK_STREAM, 0);
/* 소켓 만들기에 실패 시 INVALID_SOCKET이 반환 */
if(sock == INVALID_SOCKET) printf("Invalid Socket");

/* -------- 3 단계 -------- */
connection = connect(sock, sockaddr_internet, sizeof(struct sockaddr_in));
if(connection == SOCKET_ERROR) printf("Socket Error");