[C] Functions

박세윤·2022년 6월 3일
0
post-thumbnail

이 글은 학교 네트워크 프로그래밍 과목의 기말 시험을 공부하기 위한 글



📌 Functions

✍ Functions

  • 인터넷의 전신, 알파넷의 알파(ARPA)는 TCP/IP 소프트웨어를 유닉스 운영 체제로 이식하도록 위임하였다.

  • 이 프로젝트의 중요한 부분은 TCP/IP 기반 네트워크와 이를 사용하는 네트워크 애플리케이션 간의 인터페이스를 만드는 것

  • 인터페이스가 기존의 유닉스 시스템 호출과 알고리즘을 사용해야 하며, 절대적으로 필요할 때만 새로운 기능을 추가해야 한다고 결정

  • 이 프로젝트의 결과물인 인터페이스는 socket interface가 된다. (가끔 Berkeley socket interface라고도 불림)

  • 이 시스템은 Berkeley UNIX or BSD UNIX가 된다.

  • Berkeley Socket Interface는 사실상의 표준으로 간주되며 널리 지원되고 받아들여진다.

  • Berkely Socket Interface는 리눅스를 사용했다.


✍ What Is A Socket?

  • 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이 존재한다.



✍ Ex. #1 : Standard Input, Output Error

  • dev : device 디렉토리 (특수 디렉토리)

  • cat < /dev/stdin (stdin : 콘솔 입력 - 기본적으로 키보드로 입력)

  • ls > /dev/stdout (stdout : 콘솔 출력)


  • who 명령 - tty terminal number을 찾음

  • who

  • cat < /dev/pts/0

  • ls > /dev/pts/0



✍ Ex. #2 : File Descriptor

#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);
}



✍ Ex. #3 : File Descriptor

#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);
}



✍ File Descriptor

Descriptive NameShort NameFile NumberDescription
Standard Instdin0Input from the keyboard
Standard Outstdout1Output to the console
Standard Errorstderr2Error output to the console



✍ File Descriptor

NameCommandExample
Redirect to a file> filenamels > filename
Read from a file< filenameecho < filename
Pipe`program1program2`



✍ What Is A Socket?

  • 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 둘다 같은 방식으로 생성된다.



✍ Using Sockets

  • socket() - connect() - write () - read() - close()
    (close -> write 가능)

  • socket() - bind() - listen() - accept() - read() - write()- close()
    (write -> read 가능, close -> accept 가능)

  • client 측에서, 응용프로그램은 socket() 함수로 socket을 생성하고,
    connect() 함수로 서버에 연결하고, write() 함수로 서버에 요청을 전송하기 위해 서버와 상호작용하고, read() 함수를 통해 서버로부터 온 응답을 읽는다.

  • Client에서 이 모든 과정이 끝나면, close() 함수를 호출한다.


  • server 측에서, 응용프로그램은 socket() 함수로 socket을 생성하고, bind() 함수로 사용할 포트와 로컬 주소를 지정하고, listen()함수로 connection queue의 길이를 세팅한다.

  • connection queue는 서버가 현재 요청을 처리하는 동안 응용 프로그램이 보류해야 하는 요청 수이다.


  • 일단 connection queue가 설정되면 서버는 accept()를 호출하고 클라이언트에서 다음 연결 요청이 도착할 때까지 기다립니다.

  • 요청이 도착하면 서버는 read()와 write()를 사용하여 요청 및 응답을 작성합니다.

  • 완료되면 서버는 close()를 호출하여 클라이언트에 대한 연결을 종료하고 accept() 함수로 돌아가 클라이언트의 다음 연결 요청을 기다립니다.



✍ Socket Constants (소켓 상수)

  • 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 constantsUDP를 포함한 모든 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을 사용하긴 한다.


  • 사실상 PF와 AF가 동일하다.

Address FamilyDescription
AF_UNIX, AF_LOCALCommunications local to same host
AF_INETIPv4 Internet protocols
AF_INET6IPv6 Internet protocols
AF_IPXIPX-Novell protocols
AF_NETLINKKernel user interface
AF_X25X.25 protocols
AF_AX25Amateur radio AX.25 protocols
AF_ATMPVCATM Private Virtual Circuites (PVCs)
AF_APPLETALKAppleTalk protocols
AF_PACKETLow-level packet communications

TypeDescription
SOCK_STREAMCommunications are connection-based, sequenced, reliable, and two-way
SOCK_DGRAMConnectionless, unreliable message-type communications using a fixed length
SOCK_SEQPACKETMessage-type communications with fixed-length packets, but sequenced and more reliable
SOCK_RAWAccess to raw network protocols
SOCK_RDMConnectionless but reliable communications, without using a particular packet order
SOCK_PACKETObsolete and should not be used
  • SOCK_STREAM : TCP/IP일 때, 마치 Circuit-Switched Network인 것 처럼 reliable 하게 함

  • SOCK_DGRAM : UDP일 때 - reliable 하진 않지만, 그때그때 메시지나 스트림 같은 것들 깨져도 상관없는 것들일 때 사용


ArgumentExplanation
FamilyProtocol or address family (AF_INET for TCP/IP, AF_UNIX for internal)
TypeType of service (SOCK_STREAM for TCP; SOCK_DGRAM for UDP)
ProtocolThe protocol number to use, typically a zero(0) to use the default for a given family and type

  • socket() : 다른 호스트에 대한 연결을 열고 TCP와 통신하기 위한 목적

-- mySocket = socket(PF_INET, SOCK_STREAM, 0);
: 소켓 디스크립터가 하나 생성되어 리턴된다.



✍ Address Structure

  • 가장 중요한 구조체 : sockaddr_in

  • sockaddr_in 구조체는 IP주소와 protocol 포트 번호를 포함한다.



✍ Byte Order Function

  • 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



✍ Core Functions - socket()

  • socket()

  • socket() 함수 없이는 네트워크와 교류할 수 없다.

  • 네트워크 끝점을 만드는 데 사용되며 나중에 다른 함수에 의해 사용되는 socket descriptor를 반환합니다.

  • int socket(int domain, int type, int protocol);
    ex) socket(AF_INET, SOCK_STREAM, 0)



✍ Ex. #4 : Socket Descriptor

// 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]



✍ Ex. #5 : Socket Example

#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");
}



✍ Core Functions - bind()

  • bind() : 소켓이 생성되면 지정된 주소에 소켓을 바인딩하는 데 사용됩니다.

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

  • bind 함수는 socket descriptor를 필요로 한다. (첫 번째 파라미터)

  • sockaddr 구조체 포인터도 필요로 한다. (두 번째 파라미터)

  • 세 번째 파라미터는 sockaddr 구조체의 길이이다.


  • 아래 예시에서, 상수 INADDR_ANY를 사용한다.
    -- 위 상수를 사용하여 모든 로컬 호스트의 주소에 바인딩하려는 신호를 보냄.
// 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);
}



✍ struct sockaddr vs. struct sockaddr_in (in : internet)

  • address family가 다르더라도 상호 호환성을 보장하기 위해 인터넷 구조체를 만든 것이다.

  • 세팅은 sockaddr_in으로 하고, 실제로 넘겨줄 때는 위의 sockaddr로 타입캐스팅 하여 넘겨준다.



✍ Core Functions - listen()

  • listen() 함수는 소켓에 연결을 허용할 준비가 되었음을 알려주고 연결이 거부되기 전에 대기할 수 있는 최대 연결 수를 지정합니다.

-- 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);
}



✍ Core Functions - accept()

  • 소켓을 만들어 주소 및 포트에 바인딩하고 소켓에 연결 요청을 받을 준비가 되었다고 알렸습니다.

  • 그렇다면, 실제로 이러한 연결 요청을 수락하고 처리해야 하며, 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는 변하지 않고, 여전히 많은 연결 요청을 수신할 수 있다.



✍ Core Functions - write()

  • ssize_t write(intfd, const void *buf, size_t count);

// handle the new connection request
// write out our message to the client

write(simpleChildSocket, APRESSMESSAGE, strlen(APRESSMESSAGE));



✍ Core Functions - close()

  • int close(int fd);

  • 소켓을 닫는 함수

  • write 완료 시 바로 child socket을 닫아야 한다.

  • 이 소켓을 닫더라도 accept() 함수는 차단 함수이며 루프에 있습니다. (다시 돌아와 무한 루프 이어갈 수 있음)

  • child socket이 닫히자마자, 프로그램은 다시 반복하여 다음 연결을 기다린다.



✍ Ex. #6 : Simple Server with Warnings

#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;
}



✍ More Socket Functions - connect()

  • connect() : bind()와 비슷

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

  • connect() 함수와의 차이점 : 두번째 파라미터가 client의 주소가 아니라, server의 주소이다.

  • 성공하면 0 리턴, 실패하면 -1 리턴



✍ More Socket Functions - read()

  • write() 함수의 반대이다.

  • read() 함수는 성공적인 연결 요청 이후 서버에 의해 전송된 것을 accept 한다.

  • ssize_t read(int d, void *buf, size_t nbytes);

  • socket descriptor을 사용하면서, read() 함수는 nbytes의 데이터를 accept 하고 그것을 buffer에 저장한다.

  • 만약 read 호출에 성공하면, 읽은 실제 바이트 수가 반환됨.

  • 만약 communication의 끝에 도달하면, 0이 리턴되고 그렇지 않으면 -1이 반환됨.



✍ Ex. #7 : Simple Client with Bugs

#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;
    }
}



✍ Identification Functions - gethostbyaddr() and gethostbyname()

  • 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
}



✍ Ex. #8 : gethostbyname()

// 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



✍ Ex. #8 : sockaddr_in, in_addr

  • struct sockaddr_in, struct in_addr

: Structures for handling internet addresses



✍ Identification Functions - gethostname() and sethostname()

  • 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 리턴



✍ #Ex. #9 : gethostname()

// 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



✍ Identification Functions - getservbyname() and getservbyport()

  • 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 리턴



✍ Ex. #10 : getservbyname()

  • servent structure는 <netdb.h> 헤더파일에 정의되어 있다.
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)



✍ Ex. #11 : getservbyname() #2

// 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



✍ Identification Functions - getsockopt() and setsockopt()

  • 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);


profile
개발 공부!

0개의 댓글