소켓 정리

Language/C 2011. 11. 2. 18:45
1. Byte Order
 
네트워크 상에서의 데이터 저장 방식은(Network Byte Order)는 Big Endian 이다.
네트워크 관련 데이터를 채우거나 패킷을 생성할 때 Network Byte Order로 형성을 해 주어야 될 때가 있는데,
이때에는 다음과 같은 함수를 이용해 Host Byte Order의 수를 Network Byte Order로 바꾸어 줄 수가 있다.


htons()

host to network short

htonl()

host to network long

ntohs()

network to host short

ntohl()

network to host long



만약 이미 Network Byte Order의 수를 htonl 을 써서 Network Byte Order을 만들고자 한다면?
함수는 실행되지 않는다.


2. 구조체

2-1. addrinfo

struct addrinfo {
    int              ai_flags;     // AI_PASSIVE, AI_CANONNAME, etc.
    int              ai_family;    // AF_INET, AF_INET6, AF_UNSPEC
    int              ai_socktype;  // SOCK_STREAM, SOCK_DGRAM
    int              ai_protocol;  // use 0 for "any"
    size_t           ai_addrlen;   // size of ai_addr in bytes
    struct sockaddr *ai_addr;      // struct sockaddr_in or _in6
    char            *ai_canonname; // full canonical hostname

    struct addrinfo *ai_next;      // linked list, next node
};

이 구조체는 소켓을 만들 때 필요한 정보들을 얻기위한 구조체이다.
몇몇 요소들을 채운 뒤 getaddrinfio()함수를 실행하면 함수가 필요한 정보를 가진 linked list를 반환해준다
 
ai_flags : AI_PASSIVE를 넣을 시 localhost의 관련 정보들을 얻을 수 있다
ai_family : IPv4를 이용할시 AF_INET, IPv6을 이용할 시 AF_INET6,  정해져있지 않을 시 AF_UNSPEC을 넣는다
ai_socktype : 스트림 소켓이면 SOCK_STREAM, 데이터그램 소켓이면 SOCK_DGRAM을 넣는다
ai_protocol : 0을 넣을 시 모든 프로토콜 관련 정보를 얻을 수 있고, 특정 프로토콜을 지정해 줄 수도 있다
ai_addrlen : ip주소의 길이를 가지고있다
ai_addr : sockaddr 구조체 포인터로, ip, port number 정보를 가지고 있다.
ai_cononname :  hostname을 가르키는 포인터이다.
ai_next : 다음 노드를 가리키고 있다 


2-2. sockaddr
struct sockaddr {
    unsigned short    sa_family;    // address family, AF_xxx
    char              sa_data[14];  // 14 bytes of protocol address
}; 

sockaddr구조체는 IPv4용인 sockaddr_in, IPv6용인 sockaddr_in6으로 캐스팅될 수 있고, 그 반대도 가능하다.

IPv4
// (IPv4 only--see struct sockaddr_in6 for IPv6)

struct sockaddr_in {
    short int          sin_family;  // Address family, AF_INET
    unsigned short int sin_port;    // Port number
    struct in_addr     sin_addr;    // Internet address
    unsigned char      sin_zero[8]; // Same size as struct sockaddr
};
// (IPv4 only--see struct in6_addr for IPv6)

// Internet address (a structure for historical reasons)
struct in_addr {
    uint32_t s_addr; // that's a 32-bit int (4 bytes)
};

IPv6
// (IPv6 only--see struct sockaddr_in and struct in_addr for IPv4)

struct sockaddr_in6 {
    u_int16_t       sin6_family;   // address family, AF_INET6
    u_int16_t       sin6_port;     // port number, Network Byte Order
    u_int32_t       sin6_flowinfo; // IPv6 flow information
    struct in6_addr sin6_addr;     // IPv6 address
    u_int32_t       sin6_scope_id; // Scope ID
};

struct in6_addr {
    unsigned char   s6_addr[16];   // IPv6 address
};


2-3. ip주소의 입력과 출력

inet_pton()를 이용하여 구조체에 IP주소를 넣을 수 있으며
inet_ntop()를 이용하여 구조체에 있는 IP주소를 문자열로 얻을 수 있다.


struct sockaddr_in sa; // IPv4
struct sockaddr_in6 sa6; // IPv6

inet_pton(AF_INET, "192.0.2.1", &(sa.sin_addr)); // IPv4
inet_pton(AF_INET6, "2001:db8:63b3:1::3490", &(sa6.sin6_addr)); // IPv6

// IPv4:

char ip4[INET_ADDRSTRLEN];  // space to hold the IPv4 string
struct sockaddr_in sa;      // pretend this is loaded with something

inet_ntop(AF_INET, &(sa.sin_addr), ip4, INET_ADDRSTRLEN);

printf("The IPv4 address is: %s\n", ip4);


// IPv6:

char ip6[INET6_ADDRSTRLEN]; // space to hold the IPv6 string
struct sockaddr_in6 sa6;    // pretend this is loaded with something

inet_ntop(AF_INET6, &(sa6.sin6_addr), ip6, INET6_ADDRSTRLEN);

printf("The address is: %s\n", ip6);



3. 함수

3-1. 연결 - 클라이언트, 서버 공통


3-1-1.getaddrinfo()

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int getaddrinfo(const char *node,     // e.g. "www.example.com" or IP
                const char *service,  // e.g. "http" or port number
                const struct addrinfo *hints,
                struct addrinfo **res);

addrinfo를 채워주는 함수이다
*hints에 몇몇 요소들을 기입한 addrinfo 구조체를 넣어주면
**res를 통해 linked list가 반환된다
함수 성공시 0이 반환된다.

예제1) 나의 localhost의 3490포트 정보 얻기
int status;
struct addrinfo hints;
struct addrinfo *servinfo;  // will point to the results

memset(&hints, 0, sizeof hints); // make sure the struct is empty
hints.ai_family = AF_UNSPEC;     // don't care IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
hints.ai_flags = AI_PASSIVE;     // fill in my IP for me

if ((status = getaddrinfo(NULL, "3490", &hints, &servinfo)) != 0) {
    fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
    exit(1);
}

// servinfo now points to a linked list of 1 or more struct addrinfos

// ... do everything until you don't need servinfo anymore ....

freeaddrinfo(servinfo); // free the linked-list

예제2)www.example.com의 3490포트 정보 얻기
int status;
struct addrinfo hints;
struct addrinfo *servinfo;  // will point to the results

memset(&hints, 0, sizeof hints); // make sure the struct is empty
hints.ai_family = AF_UNSPEC;     // don't care IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets

// get ready to connect
status = getaddrinfo("www.example.net", "3490", &hints, &servinfo);

// servinfo now points to a linked list of 1 or more struct addrinfos

// etc.


3-1-2. socket()

#include <sys/types.h>
#include <sys/socket.h>

int socket(int domain, int type, int protocol); 

소켓 파일 기술자를 생성한다
domain에는 PF_INET 또는 PF_INET6 이 들어가고, type에는 SOCK_STREAM 또는 SOCK_DGRAM, protocol에는 addrinfo 구조체의 protocol과 같다.
또 PF_INET과 PF_INET6은 각각 AF_INET과 AF_INET6으로 대신할 수 있다
함수 성공시 소켓 파일 기술자를 반환하고, 에러시 -1을 반환한다.
int s;
struct addrinfo hints, *res;

// do the lookup
// [pretend we already filled out the "hints" struct]
getaddrinfo("www.example.com", "http", &hints, &res);

// [again, you should do error-checking on getaddrinfo(), and walk
// the "res" linked list looking for valid entries instead of just
// assuming the first one is good (like many of these examples do.)
// See the section on client/server for real examples.]

s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);


3-2. 연결 - 서버측

3-2-1. bind()

#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, struct sockaddr *my_addr, int addrlen);

프로세스가 몇번 포트를 이용해 통신을 할 것인지 정해주는 함수이다
sockfd에는 소켓 파일 기술자, my_addr에는 bind할 곳의 주소 정보를 가지고 있는 sockaddr구조체, addrlen에는 주소의 길이를 넣어준다.
에러시 -1을 반환한다.

struct addrinfo hints, *res;
int sockfd;

// first, load up address structs with getaddrinfo():

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;     // fill in my IP for me

getaddrinfo(NULL, "3490", &hints, &res);

// make a socket:

sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

// bind it to the port we passed in to getaddrinfo():

bind(sockfd, res->ai_addr, res->ai_addrlen)


3-2-2. listen()


int listen(int sockfd, int backlog); 

접속을 받아들일 준비를 하는 함수이다. backlog는 몇개의 연결까지 대기상태에 둘 것인지를 정해준다.
에러시 -1을 반환한다


3-2-3. accept()

#include <sys/types.h>
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 

대기중인 접속 하나를 받아드려 데이터를 주고받기 위한 함수이다.
성공시 새로운 소켓 파일 기술자를 반환하며, 이를 통해 데이터를 주고받을 수 있다
addr은 연결된 클라이언트의 주소가 들어갈 sockaddr_storage 구조체 포인터 변수를 넣어주고, 
addrlen은 sockaddr_storage의 크기를 넣어준다


#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define MYPORT "3490"  // the port users will be connecting to
#define BACKLOG 10     // how many pending connections queue will hold

int main(void)
{
    struct sockaddr_storage their_addr;
    socklen_t addr_size;
    struct addrinfo hints, *res;
    int sockfd, new_fd;

    // !! don't forget your error checking for these calls !!

    // first, load up address structs with getaddrinfo():

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;     // fill in my IP for me

    getaddrinfo(NULL, MYPORT, &hints, &res);

    // make a socket, bind it, and listen on it:

    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    bind(sockfd, res->ai_addr, res->ai_addrlen);
    listen(sockfd, BACKLOG);

    // now accept an incoming connection:

    addr_size = sizeof their_addr;
    new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_size);

    // ready to communicate on socket descriptor new_fd!
    .
    .



3-3. 연결 - 클라이언트측


3-3-1. connect()

#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen); 

서버에 접속을 하기위한 함수이다. serv_addr에는 접속할 곳의 주소가 들어가며, addrlen에는 주소의 길이를 넣어준다.
에러시 -1을 반환한다.


예제)www.example.com 의 3490포트에 접속
struct addrinfo hints, *res;
int sockfd;

// first, load up address structs with getaddrinfo():

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;

getaddrinfo("www.example.com", "3490", &hints, &res);

// make a socket:

sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

// connect!

connect(sockfd, res->ai_addr, res->ai_addrlen);




3-4. 데이터 송수신


3-4-1. 스트림 소켓 : send() recv()

int send(int sockfd, const void *msg, int len, int flags); 
int recv(int sockfd, void *buf, int len, int flags);

accept()된 소켓 혹은 connect된 소켓을 이용해 데이터를 주고받기 위한 함수이다

send - msg에는 보낼 테이터, len에는 보낼 길이를 넣어준다
recv - buf에는 받을 데이터를 저장할 공간, len에는 받을 길이를 넣어준다
함수 종료시 실제 송,수신한 데이터의 길이를 반환하고, 에러시 -1을 반환한다



3-4-2. 데이터그램 소켓 : sendto() recvfrom()

int sendto(int sockfd, const void *msg, int len, unsigned int flags,
           const struct sockaddr *to, socklen_t tolen);
int recvfrom(int sockfd, void *buf, int len, unsigned int flags,
             struct sockaddr *from, int *fromlen); 

send,recv함수와 비슷하지만 송신지or 수신지의 주소를 넣어주고, 구조체 변수의 크기를 넣어주어야 한다.

'Language > C' 카테고리의 다른 글

현재 날짜와 시간 출력  (0) 2011.11.04
gettimeofday  (0) 2011.11.04
getopt()와 optarg, optopt  (0) 2011.11.02
시리얼 통신 - 자료 수신을 위한 poll  (0) 2011.09.16
IPC - 파이프(PIPE)의 개념  (0) 2011.09.16
: