The Sockets Interface
Sockets interface는 Network Application을 build하기 위해 UNIX I/O 함수와 함께 사용되는 함수 집합이다.
윈도우와 매킨토시 시스템뿐 아니라 모든 UNIX계를 포함한 대부분의 시스템에 구현되었다.

Socket
- kernel 관점으로 봤을 때, socket은 client와 server의 통신을 위한 endpoint이다.
- application 관점으로 봤을 때, socket은 application이 network에서 read/write할 수 있게 하는 file descriptor다.
client와 server는 socket descriptor을 read/write 함으로써 통신할 수 있다.

Socket Address Structures
socket을 생성하게 되면, client와 server는 각각 clientfd, serverfd를 return받는다.
반환된 file descriptor를 통해 read/write를 할 수 있다.
file I/O와 socket I/o의 가장 큰 차이는 application이 socket descriptor를 어떻게 open하는지 여부이다.
Internet socket address는 sockaddr_in type의 16바이트 구조체로 저장된다.
/* Generic socket address structure (for connect, bind, and accept) */
struct sockaddr {
uint16_t sa_family; /* Protocol family */
char sa_data[14]; /* Address data */
};
Internet application은 sin_familly 필드의 AF_INET이다.
sin_port 필드는 16비트 포트 번호이며 sin_addr필드는 32비트 IP주소를 포함한다.
IP 주소와 포트 번호는 항상 네트워크 바이트 - Big Endian 순서로 저장된다.
/* IP socket address structure */
struct sockaddr_in {
uint16_t sin_family; /* Protocol family (always AF_INET) */
uint16_t sin_port; /* Port number in network byte order */
struct in_addr sin_addr; /* IP address in network byte order */
unsigned char sin_zero[8]; /* Pad to sizeof(struct sockaddr) */
};
connet, bind, acccept 함수에는 프로토콜 별 scoket 주소 구조체에 대한 포인터가 필요하다. 일반적인 void *포인터를 사용해서 type casting을 하여 generic한 socket 구조체를 사용할 수 있다. socket 함수를 정의하여 일반적인 socket 구조체로 type casting하고 application이 이 구조에 프로토콜별 구조체로 포인터를 casting하도록 하는 것이다.

client가 server에 접속해서 data commuication을 주고 받기 전의 애플리케이션을 구축한다고 가정한다.
1. start server
- 서버는 getaddrinfo를 통해서 IP 주소와 port number를 받아온다.
- IP 주소와 Port로 socket을 만든다.
- bind -> kernel한테 socket descirptor와 IP와 Port 를 연결하여 사용한다는 것을 알려준다.
- listen -> socket descriptor를 통해 connection이 오는 것을 보고
- accept -> 사용할 지 안 할지 결정한다.
2. Start Client
- client는 server가 작동하면 socket을 만든다.
- connection build -> client의 socket 주소와 server의 socket 주소의 pari를 만들기 위한 작업을 한다.
3. Excahnge Data
- server는 accept되면 데이터 요청이 올 때까지 기다린다.
- client가 socket에 write하게 되면 데이터가 전송된다.
- server는 buffer에 온 데이터를 read한다.
- data를 write하고 client로 전송한다.
- client는 전송한 뒤 block되어 있다. 처리된 data를 받게 되면 block이 해제되고 다시 write하며 위 과정을 반복한다.
4. Disconnect Client
close할 때까지 데이터를 주고받는다.
The socket Function
client와 server는 socket 함수를 사용하여 socket descriptor를 생성할 수 있다.
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
Returns: nonnegative descriptor if OK, −1 on error
socket이 connection의 end-point가 되도록 하려면, socket 함수를 아래와 같은 인자로 호출하면 된다.
clientfd = Socket(AF_INET, SOCK_STREAM, 0);
여기서 AF_INET은 32비트 IP 주소를 사용하고 있음을 나타내며 SOCK_STREAM은 socket이 connection의 end-point가 될 것임을 나타낸다. 이보다 더 나은 방법은 getaddrinfo 함수를 사용하여 이러한 매개 변수를 자동으로 생성하여 코드가 프로토콜에 의존하지 않도록 하는 것이다.
socket에 의해 반환된 clientfd descriptor는 open만 되고 아직 read/write를 사용할 수 없다.
socket을 open하는 방법은 client인지 server인지에 따라 달라진다.
The connect Function
client는 connet 함수를 호출하여 server와 연결을 설정할 수 있다.
#include <sys/socket.h>
int connect(int clientfd, const struct sockaddr *addr,
socklen_t addrlen);
Returns: 0 if OK, −1 on error
connet 함수는 server의 socket 주소인 addr와 Internet 연결을 설정하려고 시도한다. addrlen은 socketaddr_in의 크기다.
connet 함수는 connetion이 성공적으로 설정되거나 오류가 발생할 때까지 block한다.
연결에 성공하면 clientfd가 read/write할 준비가 되고, 연결은 socket pair로 특정지어진다.
(x:y, addr.sin_addr:addr.sin_port)
여기서 x는 client의 IP 주소이고, y는 client host에서 client process를 고유하게 식별하는 임시 port이다.
socket과 마찬가지로 getaddrinfo를 사용하는 것이 가장 좋다.
Host and Service Conversion
Linux는 binary socket address 구조체와 호스트 이름, 호스트 주소, 서비스 이름 및 포트 번호의 문자열 표현 간에 앞뒤로 변환하기 위한 getaddrinfo와 getnameinfo라는 함수를 제공한다.
socket interface와 함께 사용될 때, IP 프로토콜의 특정 버전으로부터 독립적인 네트워크 프로그램을 쓸 수 있게 한다.
The getaddrinfo Function
getaddrinfo는 hostnames, host addrresses, service names, port의 문자열 표현을 socket address 구조체로 변환한다.
장점
- Reentrant (can be safely used by threaded programs).
- Allows us to write portable protocol-independent code
- Works with both IPv4 and IPv6
단점
- 복잡하다.
- 대부분의 경우 적은 수의 패턴으로 충분하다.
int getaddrinfo(const char *host, /* Hostname or address */
const char *service, /* Port or service name */
const struct addrinfo *hints, /* Input parameters */
struct addrinfo **result); /* Output linked list */
void freeaddrinfo(struct addrinfo *result); /* Free linked list */
const char *gai_strerror(int errcode); /* Retrun error msg*/
Linked List Returned by getaddrinfo

host와 service가 주어지면 getaddrinfo는 addrinfo의 linked list를 가리키는 결과로 반환한다.
각 list를 host와 service에 해당하는 socket address 구조체를 가리킨다.
client가 getaddrinfo를 호출한 후 socket 연결에 대한 호출이 성공하고 연결이 설정될 때까지 각 socket address를 차례로 순환하며 시도한다.
server는 socket과 bind에 대한 호출이 성공하고 descriptor가 유효한 socket address에 binding될 때까지 list의 각 socket address를 순환하며 시도한다.
memory leak를 방지하려면 응용 프로그램에서 freeaddrinfo를 호출하여 list를 해제해야 한다. getaddrinfo가 0이 아닌 에러 코드를 반환하는 경우 응용 프로그램은 gai_strror를 호출하여 코드를 메시지 문자열로 변환할 수 있다.
getaddrinfo에 대한 host 인수는 도메인 이름 또는 숫자 주소(dot을 포함한 10진수 IP 주소)일 수 있다.
service 인수는 서비스 이름(예: http) 또는 십진수 포트 번호일 수 있다. 호스트 이름을 주소로 변환하지 않을 경우 호스트를 NULL로 설정할 수 있다. 서비스도 마찬가지이다. 그러나 둘 중 하나 이상을 지정해야 한다.
addrinfo struct
hint 인수에 대한 option은 addrinfo가 반환되는 소켓 주소 list를 보다 세밀하게 제어할 수 있는 addrinfo 구조체다.
struct addrinfo {
int ai_flags; /* Hints argument flags */
int ai_family; /* First arg to socket function */
int ai_socktype; /* Second arg to socket function */
int ai_protocol; /* Third arg to socket function */
char *ai_canonname; /* Canonical hostname */
size_t ai_addrlen; /* Size of ai_addr struct */
struct sockaddr *ai_addr; /* Ptr to socket address structure */
struct addrinfo *ai_next; /* Ptr to next item in linked list */
};
- getaddrinfo에서 반환되는 각 addrinfo 구조체에는 socket 함수로 직접 전달할 수 있는 인수가 있다.
- 또한 직접 전달하여 함수를 연결하고 binding할 수 있는 socket address 구조체를 가리킨다.
The getnameinfo Function
getnameinfo 함수는 getaddrinfo의 반대로 socket address 구조체를 해당 호스트 및 서비스 이름 문자열로 변환한다.
#include <sys/socket.h>
#include <netdb.h>
int getnameinfo(const struct sockaddr *sa, socklen_t salen,
char *host, size_t hostlen,
char *service, size_t servlen, int flags);
Returns: 0 if OK, nonzero error code on error
sa는 salen 바이트 크기의 socket address 구조체, hostlen 바이트 크기의 버퍼에 대한 호스트, servlen 바이트 크기의 버퍼에 대한 서비스를 가리킨다. getnameinfo 함수는 socket 주소 구조체를 해당 호스트 및 서비스 이름 문자열로 변환하여 호스트 및 서비스 버퍼에 복사한다.
getnameinfo가 0이 아닌 오류 코드를 반환하는 경우 응용 프로그램은 gai_strerror를 호출하여 문자열로 변환할 수 있다.
호스트 이름을 원하지 않으면 호스트를 NULL로 설정하고 hostlen을 0으로 설정할 수 있다. 서비스 필드도 마찬가지다.
그러나 둘 중 하나를 설정해야 한다.
Conversion Example
#include "csapp.h"
int main(int argc, char ** argv) {
struct addrinfo * p, * listp, hints;
char buf[MAXLINE];
int rc, flags;
if (argc != 2) {
fprintf(stderr, "usage: %s <domain name>\n", argv[0]);
exit(0);
}
/* Get a list of addrinfo records */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;/* IPv4 only */
hints.ai_socktype = SOCK_STREAM;/* Connections only */
if ((rc = getaddrinfo(argv[1], NULL, &hints, & listp)) != 0) {
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(rc));
exit(1);
}
/* Walk the list and display each IP address */
flags = NI_NUMERICHOST;/* Display address string instead of domain name */
for (p = listp; p; p = p -> ai_next) {
Getnameinfo(p -> ai_addr, p -> ai_addrlen, buf, MAXLINE, NULL, 0, flags);
printf("%s\n", buf);
}
/* Clean up */
Freeaddrinfo(listp);
exit(0);
}
whaleshark> ./hostinfo localhost
127.0.0.1
whaleshark> ./hostinfo whaleshark.ics.cs.cmu.edu
128.2.210.175
whaleshark> ./hostinfo twitter.com
199.16.156.230
199.16.156.38
199.16.156.102
199.16.156.198
The Sockets Interface
Sockets interface는 Network Application을 build하기 위해 UNIX I/O 함수와 함께 사용되는 함수 집합이다.
윈도우와 매킨토시 시스템뿐 아니라 모든 UNIX계를 포함한 대부분의 시스템에 구현되었다.

Socket
- kernel 관점으로 봤을 때, socket은 client와 server의 통신을 위한 endpoint이다.
- application 관점으로 봤을 때, socket은 application이 network에서 read/write할 수 있게 하는 file descriptor다.
client와 server는 socket descriptor을 read/write 함으로써 통신할 수 있다.

Socket Address Structures
socket을 생성하게 되면, client와 server는 각각 clientfd, serverfd를 return받는다.
반환된 file descriptor를 통해 read/write를 할 수 있다.
file I/O와 socket I/o의 가장 큰 차이는 application이 socket descriptor를 어떻게 open하는지 여부이다.
Internet socket address는 sockaddr_in type의 16바이트 구조체로 저장된다.
/* Generic socket address structure (for connect, bind, and accept) */
struct sockaddr {
uint16_t sa_family; /* Protocol family */
char sa_data[14]; /* Address data */
};
Internet application은 sin_familly 필드의 AF_INET이다.
sin_port 필드는 16비트 포트 번호이며 sin_addr필드는 32비트 IP주소를 포함한다.
IP 주소와 포트 번호는 항상 네트워크 바이트 - Big Endian 순서로 저장된다.
/* IP socket address structure */
struct sockaddr_in {
uint16_t sin_family; /* Protocol family (always AF_INET) */
uint16_t sin_port; /* Port number in network byte order */
struct in_addr sin_addr; /* IP address in network byte order */
unsigned char sin_zero[8]; /* Pad to sizeof(struct sockaddr) */
};
connet, bind, acccept 함수에는 프로토콜 별 scoket 주소 구조체에 대한 포인터가 필요하다. 일반적인 void *포인터를 사용해서 type casting을 하여 generic한 socket 구조체를 사용할 수 있다. socket 함수를 정의하여 일반적인 socket 구조체로 type casting하고 application이 이 구조에 프로토콜별 구조체로 포인터를 casting하도록 하는 것이다.

client가 server에 접속해서 data commuication을 주고 받기 전의 애플리케이션을 구축한다고 가정한다.
1. start server
- 서버는 getaddrinfo를 통해서 IP 주소와 port number를 받아온다.
- IP 주소와 Port로 socket을 만든다.
- bind -> kernel한테 socket descirptor와 IP와 Port 를 연결하여 사용한다는 것을 알려준다.
- listen -> socket descriptor를 통해 connection이 오는 것을 보고
- accept -> 사용할 지 안 할지 결정한다.
2. Start Client
- client는 server가 작동하면 socket을 만든다.
- connection build -> client의 socket 주소와 server의 socket 주소의 pari를 만들기 위한 작업을 한다.
3. Excahnge Data
- server는 accept되면 데이터 요청이 올 때까지 기다린다.
- client가 socket에 write하게 되면 데이터가 전송된다.
- server는 buffer에 온 데이터를 read한다.
- data를 write하고 client로 전송한다.
- client는 전송한 뒤 block되어 있다. 처리된 data를 받게 되면 block이 해제되고 다시 write하며 위 과정을 반복한다.
4. Disconnect Client
close할 때까지 데이터를 주고받는다.
The socket Function
client와 server는 socket 함수를 사용하여 socket descriptor를 생성할 수 있다.
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
Returns: nonnegative descriptor if OK, −1 on error
socket이 connection의 end-point가 되도록 하려면, socket 함수를 아래와 같은 인자로 호출하면 된다.
clientfd = Socket(AF_INET, SOCK_STREAM, 0);
여기서 AF_INET은 32비트 IP 주소를 사용하고 있음을 나타내며 SOCK_STREAM은 socket이 connection의 end-point가 될 것임을 나타낸다. 이보다 더 나은 방법은 getaddrinfo 함수를 사용하여 이러한 매개 변수를 자동으로 생성하여 코드가 프로토콜에 의존하지 않도록 하는 것이다.
socket에 의해 반환된 clientfd descriptor는 open만 되고 아직 read/write를 사용할 수 없다.
socket을 open하는 방법은 client인지 server인지에 따라 달라진다.
The connect Function
client는 connet 함수를 호출하여 server와 연결을 설정할 수 있다.
#include <sys/socket.h>
int connect(int clientfd, const struct sockaddr *addr,
socklen_t addrlen);
Returns: 0 if OK, −1 on error
connet 함수는 server의 socket 주소인 addr와 Internet 연결을 설정하려고 시도한다. addrlen은 socketaddr_in의 크기다.
connet 함수는 connetion이 성공적으로 설정되거나 오류가 발생할 때까지 block한다.
연결에 성공하면 clientfd가 read/write할 준비가 되고, 연결은 socket pair로 특정지어진다.
(x:y, addr.sin_addr:addr.sin_port)
여기서 x는 client의 IP 주소이고, y는 client host에서 client process를 고유하게 식별하는 임시 port이다.
socket과 마찬가지로 getaddrinfo를 사용하는 것이 가장 좋다.
Host and Service Conversion
Linux는 binary socket address 구조체와 호스트 이름, 호스트 주소, 서비스 이름 및 포트 번호의 문자열 표현 간에 앞뒤로 변환하기 위한 getaddrinfo와 getnameinfo라는 함수를 제공한다.
socket interface와 함께 사용될 때, IP 프로토콜의 특정 버전으로부터 독립적인 네트워크 프로그램을 쓸 수 있게 한다.
The getaddrinfo Function
getaddrinfo는 hostnames, host addrresses, service names, port의 문자열 표현을 socket address 구조체로 변환한다.
장점
- Reentrant (can be safely used by threaded programs).
- Allows us to write portable protocol-independent code
- Works with both IPv4 and IPv6
단점
- 복잡하다.
- 대부분의 경우 적은 수의 패턴으로 충분하다.
int getaddrinfo(const char *host, /* Hostname or address */
const char *service, /* Port or service name */
const struct addrinfo *hints, /* Input parameters */
struct addrinfo **result); /* Output linked list */
void freeaddrinfo(struct addrinfo *result); /* Free linked list */
const char *gai_strerror(int errcode); /* Retrun error msg*/
Linked List Returned by getaddrinfo

host와 service가 주어지면 getaddrinfo는 addrinfo의 linked list를 가리키는 결과로 반환한다.
각 list를 host와 service에 해당하는 socket address 구조체를 가리킨다.
client가 getaddrinfo를 호출한 후 socket 연결에 대한 호출이 성공하고 연결이 설정될 때까지 각 socket address를 차례로 순환하며 시도한다.
server는 socket과 bind에 대한 호출이 성공하고 descriptor가 유효한 socket address에 binding될 때까지 list의 각 socket address를 순환하며 시도한다.
memory leak를 방지하려면 응용 프로그램에서 freeaddrinfo를 호출하여 list를 해제해야 한다. getaddrinfo가 0이 아닌 에러 코드를 반환하는 경우 응용 프로그램은 gai_strror를 호출하여 코드를 메시지 문자열로 변환할 수 있다.
getaddrinfo에 대한 host 인수는 도메인 이름 또는 숫자 주소(dot을 포함한 10진수 IP 주소)일 수 있다.
service 인수는 서비스 이름(예: http) 또는 십진수 포트 번호일 수 있다. 호스트 이름을 주소로 변환하지 않을 경우 호스트를 NULL로 설정할 수 있다. 서비스도 마찬가지이다. 그러나 둘 중 하나 이상을 지정해야 한다.
addrinfo struct
hint 인수에 대한 option은 addrinfo가 반환되는 소켓 주소 list를 보다 세밀하게 제어할 수 있는 addrinfo 구조체다.
struct addrinfo {
int ai_flags; /* Hints argument flags */
int ai_family; /* First arg to socket function */
int ai_socktype; /* Second arg to socket function */
int ai_protocol; /* Third arg to socket function */
char *ai_canonname; /* Canonical hostname */
size_t ai_addrlen; /* Size of ai_addr struct */
struct sockaddr *ai_addr; /* Ptr to socket address structure */
struct addrinfo *ai_next; /* Ptr to next item in linked list */
};
- getaddrinfo에서 반환되는 각 addrinfo 구조체에는 socket 함수로 직접 전달할 수 있는 인수가 있다.
- 또한 직접 전달하여 함수를 연결하고 binding할 수 있는 socket address 구조체를 가리킨다.
The getnameinfo Function
getnameinfo 함수는 getaddrinfo의 반대로 socket address 구조체를 해당 호스트 및 서비스 이름 문자열로 변환한다.
#include <sys/socket.h>
#include <netdb.h>
int getnameinfo(const struct sockaddr *sa, socklen_t salen,
char *host, size_t hostlen,
char *service, size_t servlen, int flags);
Returns: 0 if OK, nonzero error code on error
sa는 salen 바이트 크기의 socket address 구조체, hostlen 바이트 크기의 버퍼에 대한 호스트, servlen 바이트 크기의 버퍼에 대한 서비스를 가리킨다. getnameinfo 함수는 socket 주소 구조체를 해당 호스트 및 서비스 이름 문자열로 변환하여 호스트 및 서비스 버퍼에 복사한다.
getnameinfo가 0이 아닌 오류 코드를 반환하는 경우 응용 프로그램은 gai_strerror를 호출하여 문자열로 변환할 수 있다.
호스트 이름을 원하지 않으면 호스트를 NULL로 설정하고 hostlen을 0으로 설정할 수 있다. 서비스 필드도 마찬가지다.
그러나 둘 중 하나를 설정해야 한다.
Conversion Example
#include "csapp.h"
int main(int argc, char ** argv) {
struct addrinfo * p, * listp, hints;
char buf[MAXLINE];
int rc, flags;
if (argc != 2) {
fprintf(stderr, "usage: %s <domain name>\n", argv[0]);
exit(0);
}
/* Get a list of addrinfo records */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;/* IPv4 only */
hints.ai_socktype = SOCK_STREAM;/* Connections only */
if ((rc = getaddrinfo(argv[1], NULL, &hints, & listp)) != 0) {
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(rc));
exit(1);
}
/* Walk the list and display each IP address */
flags = NI_NUMERICHOST;/* Display address string instead of domain name */
for (p = listp; p; p = p -> ai_next) {
Getnameinfo(p -> ai_addr, p -> ai_addrlen, buf, MAXLINE, NULL, 0, flags);
printf("%s\n", buf);
}
/* Clean up */
Freeaddrinfo(listp);
exit(0);
}
whaleshark> ./hostinfo localhost
127.0.0.1
whaleshark> ./hostinfo whaleshark.ics.cs.cmu.edu
128.2.210.175
whaleshark> ./hostinfo twitter.com
199.16.156.230
199.16.156.38
199.16.156.102
199.16.156.198