이 글은 학교 네트워크 프로그래밍 과목의 기말 시험을 공부하기 위한 글
인터넷의 전신, 알파넷의 알파(ARPA)는 TCP/IP 소프트웨어를 유닉스 운영 체제로 이식하도록 위임하였다.
이 프로젝트의 중요한 부분은 TCP/IP 기반 네트워크와 이를 사용하는 네트워크 애플리케이션 간의 인터페이스를 만드는 것
인터페이스가 기존의 유닉스 시스템 호출과 알고리즘을 사용해야 하며, 절대적으로 필요할 때만 새로운 기능을 추가해야 한다고 결정
이 프로젝트의 결과물인 인터페이스는 socket interface가 된다. (가끔 Berkeley socket interface라고도 불림)
이 시스템은 Berkeley UNIX or BSD UNIX가 된다.
Berkeley Socket Interface는 사실상의 표준으로 간주되며 널리 지원되고 받아들여진다.
Berkely Socket Interface는 리눅스를 사용했다.
Socket : 네트워크 통신을 위한 추상화
리눅스의 기본 network I/O Function을 보면, 네트워크가 input, output 하기 위해서 다섯가지 기본 함수를 활용한다
: open, close, read, write, control(ioctl)
open : input 또는 output을 위한 준비
close : 이전 계산을 멈추고 리소스 리턴
read : 데이터를 얻고 응용 프로그램 메모리에 배치
write : 응용 프로그램 메모리에서 데이터를 저장하고 컨트롤을 전송
control (ioctl) : 버퍼 크기 및 연결 동작과 같은 옵션 설정
non-networked application에서, I/O 함수들은 파일에서 수행된다.
파일이 열리면, 데이터는 파일에 읽히거나 쓰여지고, 파일이 닫힌다.
이러한 함수는 file descriptor로 알려진 것을 사용하여 작동한다.
file descriptor는 응용 프로그램이 후속 작업에 사용할 수 있는 함수에 의해 반환되는 작은 정수이다.
각 프로세스마다 file descriptor table이 존재한다.
dev : device 디렉토리 (특수 디렉토리)
cat < /dev/stdin (stdin : 콘솔 입력 - 기본적으로 키보드로 입력)
ls > /dev/stdout (stdout : 콘솔 출력)
who 명령 - tty terminal number을 찾음
who
cat < /dev/pts/0
ls > /dev/pts/0
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
void main(void) {
char buf[] = "Chae-Bong Sohn\n";
int fd = open("/dev/pts/0", O_RDWR);
int ret = write(fd, &buf, strlen(buf));
close(fd);
}
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#define BUF_SIZE 256
void main(void) {
char buf[BUF_SIZE];
int fd = open("/dev/pts/0", O_RDWR);
int ret = read(fd, &buf. BUF_SIZE);
printf("%s\n", buf); // 화면에 결과를 출력
close(fd);
}
Descriptive Name | Short Name | File Number | Description |
---|---|---|---|
Standard In | stdin | 0 | Input from the keyboard |
Standard Out | stdout | 1 | Output to the console |
Standard Error | stderr | 2 | Error output to the console |
Name | Command | Example |
---|---|---|
Redirect to a file | > filename | ls > filename |
Read from a file | < filename | echo < filename |
Pipe | `program1 | program2` |
Berkeley Socket Interface에서 file descriptor 개념을 확장하였다. (file descriptor -> socket descriptor)
active socket은 socket descriptor라고 알려진 정수에 의해 식별됨
socet descriptor는 file descriptor와 같은 방식으로 할당함.
응용 프로그램에는 동일한 값을 가진 socket descriptor 존재 X
network에서 communicate하기 위해서, network로의 연결(= Socket)은 반드시 열려있어야 한다.
일단 Socket이 열리면, data는 socket에 쓰여지거나, socket으로부터 읽혀진다.
communication이 끝나면, 네트워크로의 연결(소켓)은 닫히고, 소켓에 사용된 리소스는 해제된다.
Socket은 두가지 방식으로 사용될 수 있다.
일단 생성되면, Socket은 다가오는 연결을 기다리거나, 원격의 호스트의 다른 socket으로의 연결을 열 수 있다.
Active Socket : 서버 연결을 시작하기 위해 클라이언트 프로그램에서 사용하는 소켓
Passive Socket : 서버 역할을 하고 들어오는 연결을 기다리는 소켓
active socket, passive socket 둘다 같은 방식으로 생성된다.
client 측에서, 응용프로그램은 socket() 함수로 socket을 생성하고,
connect() 함수로 서버에 연결하고, write() 함수로 서버에 요청을 전송하기 위해 서버와 상호작용하고, read() 함수를 통해 서버로부터 온 응답을 읽는다.
Client에서 이 모든 과정이 끝나면, close() 함수를 호출한다.
server 측에서, 응용프로그램은 socket() 함수로 socket을 생성하고, bind() 함수로 사용할 포트와 로컬 주소를 지정하고, listen()함수로 connection queue의 길이를 세팅한다.
connection queue는 서버가 현재 요청을 처리하는 동안 응용 프로그램이 보류해야 하는 요청 수이다.
일단 connection queue가 설정되면 서버는 accept()를 호출하고 클라이언트에서 다음 연결 요청이 도착할 때까지 기다립니다.
요청이 도착하면 서버는 read()와 write()를 사용하여 요청 및 응답을 작성합니다.
완료되면 서버는 close()를 호출하여 클라이언트에 대한 연결을 종료하고 accept() 함수로 돌아가 클라이언트의 다음 연결 요청을 기다립니다.
Berkeley socket interface에서 사용하는 주요 두가지 상수
: protocol type constant / address family constants
포함시켜야 하는 두 개의 헤더 파일은 type.h와 socket.h로, /usr/include 디렉토리에 있다.
: #include <sys/types.h>
: #include <sys/socket.h>
protocol families로 알려진 address family constants는 UDP를 포함한 모든 TCP/IP을 위한 Internet address family에 속해있다.
이 address family는 AF_INET으로 알려져있다. (AF : Address Family : 통신에 필요한 Internet Family 주소)
address constant에 대한 또다른 옵션은 AF_UNIX이다. 이는 로컬 내부에서 프로세스간 통신 (IPC)을 할 때 사용된다.
protocol family constants (PF)도 아마 네트워크 프로그래밍을 하다보면 마주칠 것이다.
protocol family constants와 address family constants는 사실 같다.
최근 Berkely socket interface에서는 AF_INET을 사용하긴 한다.
Address Family | Description |
---|---|
AF_UNIX, AF_LOCAL | Communications local to same host |
AF_INET | IPv4 Internet protocols |
AF_INET6 | IPv6 Internet protocols |
AF_IPX | IPX-Novell protocols |
AF_NETLINK | Kernel user interface |
AF_X25 | X.25 protocols |
AF_AX25 | Amateur radio AX.25 protocols |
AF_ATMPVC | ATM Private Virtual Circuites (PVCs) |
AF_APPLETALK | AppleTalk protocols |
AF_PACKET | Low-level packet communications |
Type | Description |
---|---|
SOCK_STREAM | Communications are connection-based, sequenced, reliable, and two-way |
SOCK_DGRAM | Connectionless, unreliable message-type communications using a fixed length |
SOCK_SEQPACKET | Message-type communications with fixed-length packets, but sequenced and more reliable |
SOCK_RAW | Access to raw network protocols |
SOCK_RDM | Connectionless but reliable communications, without using a particular packet order |
SOCK_PACKET | Obsolete and should not be used |
SOCK_STREAM : TCP/IP일 때, 마치 Circuit-Switched Network인 것 처럼 reliable 하게 함
SOCK_DGRAM : UDP일 때 - reliable 하진 않지만, 그때그때 메시지나 스트림 같은 것들 깨져도 상관없는 것들일 때 사용
Argument | Explanation |
---|---|
Family | Protocol or address family (AF_INET for TCP/IP, AF_UNIX for internal) |
Type | Type of service (SOCK_STREAM for TCP; SOCK_DGRAM for UDP) |
Protocol | The protocol number to use, typically a zero(0) to use the default for a given family and type |
-- mySocket = socket(PF_INET, SOCK_STREAM, 0);
: 소켓 디스크립터가 하나 생성되어 리턴된다.
가장 중요한 구조체 : sockaddr_in
sockaddr_in 구조체는 IP주소와 protocol 포트 번호를 포함한다.
Byte Order Function : 이걸 호출해서 Byte Order을 미리 맞추는 세팅을 한다.
TCP/IP 프로토콜은 프로토콜 헤더에서 발견되는 이진 정수를 나타내는 방법을 지정한다.
이진 정수는 MSB(Most Significant Byte)를 먼저 지정해야 한다.
: 이것을 network byte order 라고 한다. (Big Endian)
TCP/IP 프로토콜은 Big Endian이지만 숫자를 나타내는 다양한 방법의 세부 사항을 이해하는 것은 중요하지 않다.
이러한 함수는 네트워크 바이트 순서에서 로컬 호스트의 기본 바이트 순서로 짧은 정수와 긴 정수를 자동으로 변환하고 다시 변환합니다.
이러한 함수들은 16bit 또는 32bit 정수들로 계산한다.
htons(), ntohs(), htonl(), ntohl()
htons() : Host Byteorder / To / Network / byteorder Short
socket()
socket() 함수 없이는 네트워크와 교류할 수 없다.
네트워크 끝점을 만드는 데 사용되며 나중에 다른 함수에 의해 사용되는 socket descriptor를 반환합니다.
int socket(int domain, int type, int protocol);
ex) socket(AF_INET, SOCK_STREAM, 0)
// bongbong@ssl:~/Works/np$ vi sckt.c
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
void main(void) {
int socket_fd;
socket_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
printf("Socket Descriptor : %d\n", socket_fd);
getchar();
close(socket_fd);
}
// bongbong@ssl:~/Works/np$ make sckt
// cc sckt.c -o sckt
// bongbong@ssl:~/Works/np$ ./sckt
// Socket Descriptor : 3
// bongbong@ssl:~/Works/np$ ps -e | grep sckt
// 10149 pts/8 00:00:00 sckt
// bongbong@ssl:~/Works/np$ ls -al /proc.10149/fd
// total 0
// dr-x------ 2 bongbong bongbong 0 3월 19 21:59
// dr-xr-xr-x 9 bongbong bongbong 0 3월 19 21:59
// lrwx------ 1 bongbong bongbong 64 3월 19 22:01 0 -> /dev/pts/8
// lrwx------ 1 bongbong bongbong 64 3월 19 22:01 1 -> /dev/pts/8
// lrwx------ 1 bongbong bongbong 64 3월 19 21:59 2 -> /dev/pts/8
// lrwx------ 1 bongbong bongbong 64 3월 19 22:01 3 -> socket:[2744138]
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
const char APRESSMESSAGE[] = "APRESS - For Professionals, by Professionals!\n";
int main(int argc, char *argv[]) {
int simpleSocket = 0;
int simplePort = 0;
/* make sure we have a port number */
if(2 != argc) {
fprintf(stderr, "Usage: %s <port>\n", argv[0]);
exit(1);
}
/* create a streaming socket */
simpleSocket = socket(AF_INET, SOCK_STREA, IPPROTO_TCP);
if(simpleSocket == -1) {
fprintf(stderr, "Could not create a socket!\n");
exit(1);
}
else
fprintf(stderr, "Socket created!\n");
}
-- int bind(int sockfd, struct sockaddr *my_addr
, socklen_t addrlen);
bind 함수는 socket descriptor를 필요로 한다. (첫 번째 파라미터)
sockaddr 구조체 포인터도 필요로 한다. (두 번째 파라미터)
세 번째 파라미터는 sockaddr 구조체의 길이이다.
// retrieve the port number for listening
simplePort = atoi(argv[1]);
// set up the address structure
// use INADDR_ANY to bind to all local addresses
// note use of htonl() and htons()
// bzero : 0으로 리셋
bzero(&simpleServer, sizeof(simpleServer));
simpleServer.sin_family = AF_INET;
simpleServer.sin_addr.s_addr = htonl(INADDR_ANY);
simpleServer.sin.port = htons(simplePort);
// bind to the address and port with our socket
returnStatus = bind(simpleSocket, (struct sockaddr *)&simpleServer, sizeof(simpleServer));
if(returnStatus == 0)
fprintf(stderr, "Bind Completed!\n");
else {
fprintf(stderr, "Could not bind to address!\n");
close(simpleSocket);
exit(1);
}
address family가 다르더라도 상호 호환성을 보장하기 위해 인터넷 구조체를 만든 것이다.
세팅은 sockaddr_in으로 하고, 실제로 넘겨줄 때는 위의 sockaddr로 타입캐스팅 하여 넘겨준다.
-- int listen(int s, int backlog);
backlog는 connection queue를 결정하는 값
일반적으로 backlog 값은 5이다. (필요한만큼 늘어날 수 있다. default : 5)
ex> backlog 5일때 5명을 줄 세우는느낌 아직 다 처리못했는데 한명 더온다 그럼 6되니까 새로오는 애 connection refused 시켜버림
// tell the socket we are ready to accept connections
returnStatus = listen(simpleSocket, 5);
if(returnStatus == -1) {
fprintf(stderr, "Cannot listen on socket!\n");
close(simpleSocket);
exit(1);
}
소켓을 만들어 주소 및 포트에 바인딩하고 소켓에 연결 요청을 받을 준비가 되었다고 알렸습니다.
그렇다면, 실제로 이러한 연결 요청을 수락하고 처리해야 하며, accept() 함수 사용하여 이를 수행합니다.
-- int accept(int s, struct sockaddr *addr
, socklen_t *addrlen
);
accept() 함수는 socket descriptor, 주소 구조체의 포인터, 그리고 주소 구조체의 길이를 파라미터로 필요로 한다.
이 함수에 대해 특별히 기억해야 할것이 있다. 무한루프에서 작동한다는 것이다. (accept 대기 -> 다른 거 하고 끝나면 -> accept()대기 -> -> ...)
네트워크 서버가 수신을 중지하려면 수동으로 종료해야 합니다. 그렇지 않으면 응용 프로그램이 연결을 계속 수신하고 수락해야 합니다. (무한루프기 때문)
while(1) {
// set up variables to handle client connections
struct sockaddr_in clientName = { 0 };
int simpleClient = 0;
int clientNameLength = sizeof(clientName);
// block on accept function call
simpleChildSocket = accept(simpleSocket, (struct sockaddr *)&clientName, &clientNameLength);
if(simpleClient == -1) {
fprintf(stderr, "Cannot accept connections!\n");
close(simpleSocket);
exit(1);
}
}
accept() 함수는 지금까지 배운 함수들과는 조금 다른데, accept()함수는 blocking function 이라는 것이다.
blocking function은 계속 대기한다. i/o bound 형태로서, 시스템 리소스를 사용하지 않는 상태라는 것이다.
그 말은, 응용프로그램은 client로부터 연결응답을 받을 때 까지 accept() 함수가 계속 기다린다.
이 동작은 구성할 수 있지만 일반적으로 기본 차단 동작이 원하는 동작입니다.
또 이전 함수들과 다른 것은, accept() 함수를 통과하는 구조체들은 Server Related가 아니라, Client Related 라는 점이다.
accept() 함수를 거친 두 번째, 세 번째 파라미터들은 서버가 아니라, 클라이언트에 관한 정보를 저장하는 장소이다.
시작되면, client name의 값은 이름의 길이와 마찬가지로 0으로 세팅된다.
accept()에서 호출이 반환되면, 두 구조체들은 올바른 정보가 입력되어야 한다.
성공하면, accept() 함수는 새 연결을 위한 새로운 socket descriptor을 리턴한다.
위의 코드 예시에서 새로운 socket descriptor은 simpleChildSocket이다.
원래 있던 socket descriptor는 변하지 않고, 여전히 많은 연결 요청을 수신할 수 있다.
// handle the new connection request
// write out our message to the client
write(simpleChildSocket, APRESSMESSAGE, strlen(APRESSMESSAGE));
int close(int fd);
소켓을 닫는 함수
write 완료 시 바로 child socket을 닫아야 한다.
이 소켓을 닫더라도 accept() 함수는 차단 함수이며 루프에 있습니다. (다시 돌아와 무한 루프 이어갈 수 있음)
child socket이 닫히자마자, 프로그램은 다시 반복하여 다음 연결을 기다린다.
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
const char APRESSMESSAGE[] = "APRESS - For Professionals, By Professionals!\n";
int main(int argc, char *argv[]) {
int simpleSocket = 0;
int simplePort = 0;
int returnStatus = 0;
struct sockaddr_in simpleServer;
if(2 != argc) {
fprintf(stderr, "Usage: %s <port>\n", argv[0]);
exit(1);
}
simpleSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(simpleSocket == -1) {
fprintf(stderr, "Could not create a socket!\n");
exit(1);
}
else
fprintf(stderr, "Socket created!\n");
// retrieve the port number for listening
simplePort = atoi(argv[1]);
// set up the address structure
// use INADDR_ANY to bind to all local addresses
bzero(&simpleServer, sizeof(simpleServer));
simpleServer.sin_family = AF_INET;
simpleServer.sin_addr.s_addr = htonl(INADDR_ANY);
simpleServer.sin_port = htons(simplePort);
// bind to the address and port with our socket
returnStatus = bind(simpleSocket, (struct sockaddr *)&simpleServer, sizeof(simpleServer));
if(returnStatus == 0)
fprintf(stderr, "Bind completed!\n");
else {
fprintf(stderr, "Could not bind to address!\n");
close(simpleSocket);
exit(1);
}
// let's listen on the socket for connections
returnStatus = listen(simpleSocket, 5);
if(returnStatus == -1) {
fprintf(stderr, "Cannot listen on socket!\n");
close(simpleSocket);
exit(1);
}
while(1) {
struct sockaddr_in clientName = { 0 };
int simpleChildSocket = 0;
int clientNameLength = sizeof(clientName);
// wait here
simpleChildSocket = accept(simpleSocket, struct sockaddr *)&clientName, &clientNameLength);
if(simpleChildSocket == -1) {
fprintf(stderr, "Cannot accept connections!\n");
close(simpleSocket);
exit(1);
}
// handle the new connection request
// write out our message to the client
write(simpleChildSocket, APRESSMESSAGE, strlen(APRESSMESSAGE));
close(simpleChildSocket);
}
close(simpleSocket);
return 0;
}
connect() : bind()와 비슷
int connect(int sockfd, const struct sockaddr *serv_addr
, socklen_t addrlen
);
connect() 함수와의 차이점 : 두번째 파라미터가 client의 주소가 아니라, server의 주소이다.
성공하면 0 리턴, 실패하면 -1 리턴
write() 함수의 반대이다.
read() 함수는 성공적인 연결 요청 이후 서버에 의해 전송된 것을 accept 한다.
ssize_t read(int d, void *buf
, size_t nbytes);
socket descriptor을 사용하면서, read() 함수는 nbytes의 데이터를 accept 하고 그것을 buffer에 저장한다.
만약 read 호출에 성공하면, 읽은 실제 바이트 수가 반환됨.
만약 communication의 끝에 도달하면, 0이 리턴되고 그렇지 않으면 -1이 반환됨.
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int main(int argc, char *argv[]) {
int simpleSocket = 0;
int simplePort = 0;
int returnStatus = 0;
char buffer[256] = "";
struct sockaddr_in simpleServer;
if(3 != argc) {
fprintf(stderr, "Usage: %s <server> <port>\n", argv[0]);
exit(1);
}
else
fprintf(stderr, "Socket created!\n");
// retrieve the port number for connecting
simplePort = atoi(argv[3]);
// set up the address structure
// use the IP address argument for the server addres
bzero(&simpleServer, sizeof(simpleServer));
simpleServer.sin_family = AF_INET;
inet_addr(argv[2], &simpleServer.sin_addr.s_addr);
simpleServer.sin_port = htons(simplePort);
// connect to the address and port with our socket
returnStatus = connect(simpleSocket, struct sockaddr *)&simpleServer, sizeof(simpleServer));
if(returnStatus == 0)
fprintf(stderr, "Connect successful!\n");
else {
fprintf(stderr, "Could not connect to address!\n");
close(simpleSocket);
exit(1);
// get the message from the server
returnStatus = read(simpleSocket, buffer, sizeof(buffer));
if(returnStats > 0)
printf("%d: %s, returnStatus, buffer");
else
fprintf(stderr, "Return Status = %d \n", returnStatus);
close(simpleSocket);
return 0;
}
}
gethostbyaddr(), gethostbyname() 두 함수는 비슷한 작업을 수행한다.
gethostbyaddr() 함수는 address가 주어질 때 host name을 리턴한다.
gethostbyname() 함수는 그 반대인데, host name이 주어지면 address를 리턴한다.
struct hostent *gethostbyaddr(const char *addr, int len, int type)
struct hostent *gethostbyname(const char *name)
struct hostent {
char *h_name; // official name of host
char **h_aliases; // NULL-terminated array of alternate
// names
int h_addrtype; // host address type, typically AF_INET
int h_ength; // length of address
char **h_addr_list; // NULL-terminated list of addresses
// returned from name server in network
// byte order
}
// bongbong@ssl:~/NetworkProgramming$ cat gethostbyname.c
#include <stdio.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
void main(void) {
const char *dns = "www.kw.ac.kr";
struct hostent *host;
struct in_addr addr;
host = gethostbyname(dns);
printf("official Name : %s\n, host->h_name");
printf("aliases Name : %s\n", host->h.aliases[0]);
addr.s_addr = *(u_long *)host->h_addr_list[0];
printf("ip Address : %s\n", inet_ntoa(addr));
}
// bongbong@ssl:~/NetworkProgramming$ ./gethostbyname
// official Name : klas.kw.ac.kr
// aliases Name : www.kw.ac.kr
// ip Address : 223.194.1.180
: Structures for handling internet addresses
gethostname() : 함수 호출이 시작되는 현재 로컬 호스트의 이름을 리턴
sethostname() : 호스트 이름 설정
int gethostname(char *name
, int namelen);
int sethostname(const char *name
, int namelen);
superuser만 sethostname() 함수 호출 가능하며, 일반적으로 서버의 부팅 시퀀스 중에 발생합니다.
gethostname() 함수는 누구든지 사용할 수 있으며, 외부 name server나 /etc/hosts 파일과 같은 다른 lookup table로 호출하지 않고도 서버 이름을 알고 싶을 때 유용합니다.
성공하면 0 리턴, 실패하면 -1 리턴
// bongbong@ssl:~/NetworkProgramming$ cat gethostname.c
#include <stdio.h>
#include <unistd.h>
void main(void) {
char hostname[256];
int ret;
ret = gethostname(hostname, 256);
printf("Host Name : %s\n", hostname);
}
// bongbong@ssl:~/NetworkProgramming$ ./gethostname
// Host Name : ssl.kw.ac.kr
getservbyname()은 servent(server entry) 구조체 포인터를 리턴
getservbyport()는 companion function이다.
struct servent *getservbyname(const char *name, const char *proto);
struct servent *getservbyport(int port, const char *proto);
어떤 것을 사용할지는 servent 구조체의 어떤 구성원을 채울지 결정
port number은 반드시 network byte order을 따른다.
함수는 성공하면 0 리턴, 실패하면 -1 리턴
struct servent {
char *s_name; // official service name
char **s_aliases; // alias list
int s_port; // port number
char *s_proto; // protocol to use
}
// bongbong@ssl:~/NetworkProgramming$ cat getservbyname.c
#include <stdio.h>
#include <netdb.h>
#include <arpa/inet.h>
void main(void) {
const char *service = "http";
const char *proto = "tcp";
short port = 80;
struct servent *serv;
serv = getservbyname(service, proto);
printf("official Service Name : %s\n", serv->s_name);
printf("port Number : %d\n", serv->s_port);
}
// bongbong@ssl:~/NetworkProgramming$ ./getservbyname
// official Service Name : http
// port Number : 20480
// Segmentation fault (core dumped)
// bongbong@ssl:~/NetworkProgramming$ cat getservbyname2.c
#include <stdio.h>
#include <netdb.h>
#include <arpa/inet.h>
void main(void) {
const char *service = "http";
const char *proto = "tcp";
short port = 80;
struct servent *serv;
serv = getservbyname(service, proto);
printf("official Service Name : %s\n", serv->s_name);
printf("port Number : %d\n", ntohs(serv->s_port));
}
// bongbong@ssl:~/NetworkProgramming$ ./getservbyname2
// official Service Name : http
// port Number : 80
// official Service Name : http
// port Number : 80
getsockopt(), setsockopt()은 socket의 옵션을 다룬다.
int getsockopt(int s, int level, int optname, void *optval
, socklen_t *optlen
);
int setsockopt(int s, int level, int optname, const void *optval
, socklen_t optlen);