System programming note 2

짱J·2022년 6월 5일
0

시스템프로그래밍

목록 보기
3/3
post-thumbnail

ICE3013 시스템 프로그래밍 기말고사 대비 내용 정리 🤓

개요

: 각 주차에 배운 내용 간단히 요약

9주차 + 10주차 : exec, shell

  • execve()를 사용하여 리눅스 명령어 직접 구현
  • execve()로 입력하는 리눅스 명령어 직접 구현

11주차 : network

  • server와 client 소통

12주차 : network2

  • FTP 서버

13주차 : select

  • server와 client 간 채팅 프로그램 구현

14주차 : signal

  • signal()를 사용하여 프로세스에 시그널(동작) 전달

exec + shell

ex2를 실행할 때 ex1이 실행되도록 하기

ex1.c

#include <stdio.h>
void main() {
	printf("I am ex1\n");
}

ex2.c

#include <stdio.h>
void main() {
	execve("./ex1", 0, 0);
	printf("I am ex2\n");
}
  • execve()가 실행되어 메모리 상에서 ex2.c의 코드가 ex1.c로 대체
  • execve() 뒤에 printf("I am ex2\n"); 는 실행되지 않음

execve()로 /bin/ls 수행

#include <stdio.h>
void main() {
	char *k[10];
    k[0] = "/bin/ls";
    k[1] = 0;
    execve(k[0], k, 0);
}
  • k[1], 즉 argument list의 마지막은 0으로 끝나야 한다.

execve()로 /bin/cat f1 수행

#include <stdio.h>
void main() {
	char *k[10];
    k[0] = "/bin/cat";
    k[1] = "f1";
    k[2] = 0;
    execve(k[0], k, 0);
}
  • k[0] - 명령어
  • k[i] - 인자 또는 옵션
  • k 배열의 마지막은 0으로 해줄 것 !!

execve()로 /bin/ls -l 수행

#include <stdio.h>
void main() {
	char *k[10];
    k[0] = "/bin/ls";
    k[1] = "-l";
    k[2] = 0;
    execve(k[0], k, 0);
}

execve()로 /bin/cp f1 f2 수행

#include <stdio.h>
void main() {
	char *k[10];
    k[0] = "/bin/cp";
    k[1] = "f1";
    k[2] = "f2"
    k[3] = 0;
    execve(k[0], k, 0);
}

/bin/ls -l 끝나고 "job done" 출력하기

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

void main() {
	if(fork() == 0) { // 자식 프로세스
      char *k[10];
      k[0] = "/bin/ls";
      k[1] = "-l";
      k[2] = 0;
      execve(k[0], k, 0);
    }
    else { // 부모 프로세스
    	wait(0);
        printf("job done\n");
}
  • fork() 함수로 새로운 프로세스를 생성하여 명령어 실행
  • wait() 함수는 자식 프로세스가 종료되는 것을 기다린 뒤 부모 프로세스가 수행됨

execve()로 입력하는 리눅스 명령어 실행하기

#include <stdio.h>
#include <string.h>

void main() {
	char buf[256];
    char *token;
    char *k[10];
    
    // 입력값을 받아 buf 배열에 저장
    fgets(buf, 255, stdin);
    buf[strlen(buf)-1] = 0;
    
    // 공백을 기준으로 문자열 split 후 k 배열에 저장
    token = strtok(buf, " ");
    int i = 0;
    for(;;) {
    	k[i] = (char*)malloc(strlen(token+1));
        strcpy(k[i], token);
        token = strtok(NULL, " ");
        i++;
        if(token==NULL) break;
    }
    k[i] = 0;
    execve(k[0], k, 0); // 명령어 실행
}
  • 이전에는 k 배열의 인자를 직접 선언해주었는데, 이번 예제에서는 k 배열에 사용자의 입력을 받기 때문에 /bin/cat, /bin/ls 등 자신이 원하는 명령어를 사용 가능
  • strtok() 함수로 입력된 문자열에서 공백을 구분자로 하여 각 문자열을 k 배열에 저장
  • k 배열의 마지막 원소는 0 !!!!

execve()로 입력하는 리눅스 명령어 실행하기 + 5번 반복

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

void main() {
	int j;
    for(j=0; j<5; j++) {
    	if (fork()==0) { // 자식 프로세스
            char buf[256];
            char *token;
            char *k[10];

            // 입력값을 받아 buf 배열에 저장
            fgets(buf, 255, stdin);
            buf[strlen(buf)-1] = 0;

            // 공백을 기준으로 문자열 split 후 k 배열에 저장
            token = strtok(buf, " ");
            int i = 0;
            for(;;) {
                k[i] = (char*)malloc(strlen(token+1));
                strcpy(k[i], token);
                token = strtok(NULL, " ");
                i++;
                if(token==NULL) break;
            }
            k[i] = 0;
            execve(k[0], k, 0); // 명령어 실행
        }
        else {
        	wait(0);
        }
    }
}
  • 단순히 execve() 함수를 호출하면 명령어를 수행한 뒤 프로그램이 종료되기 때문에 fork() 함수를 이용하여 자식 프로세스에서 명령어를 실행

execve()로 입력하는 리눅스 명령어 실행하기 + 5번 반복 + prompt에 디렉토리 출력

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

void main() {
	int j;
    for(j=0; j<5; j++) {
    	if (fork()==0) { // 자식 프로세스
        	// 추가된 부분
        	// ---------------------------
        	char *cwd;
            char wd[BUFSIZ];
            cwd = getcwd(NULL, BUFSIZ);
            printf("[%s]$ ", cwd);
            // ---------------------------
            
            char buf[256];
            char *token;
            char *k[10];

            // 입력값을 받아 buf 배열에 저장
            fgets(buf, 255, stdin);
            buf[strlen(buf)-1] = 0;

            // 공백을 기준으로 문자열 split 후 k 배열에 저장
            token = strtok(buf, " ");
            int i = 0;
            for(;;) {
                k[i] = (char*)malloc(strlen(token+1));
                strcpy(k[i], token);
                token = strtok(NULL, " ");
                i++;
                if(token==NULL) break;
            }
            k[i] = 0;
            execve(k[0], k, 0); // 명령어 실행
        }
        else {
        	wait(0);
        }
    }
}
  • getcwd() 함수를 사용하여 현재 디렉토리를 가져온다

exit 입력하면 Shell 나가도록

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

void main() {
	int i, x;
    
    for(;;) {
    	printf("Child process to execute\n");
        char buf[100];
        printf("$ ");
        scanf("%s", buf);
        
        if(strcmp(buf, "exit")==0) break;
        char* argv[] = { buf, 0 };
        
        x = fork();
        if(x==0) {
        	if(execve(buf, argv, 0) < 0) perror("Fail to execute\n");
            execve(buf, argv, 0);
        }
        else wait(0);
    }
}
  • fork()를 호출하기 전 strcmp()로 "exit"을 입력했는지 확인
  • scanf로 입력값을 하나만 받기 때문에, 추가적인 인자나 옵션 사용 못함 !

Shell에서 인자를 handle할 수 있도록

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

void main() {
	int i, x;
    
    for(;;) {
        char buf[100];
        char *token;
        printf("$ ");
        fgets(buf, 255, stdin);
        buf[strlen(buf)-1] = 0;
        
        if(strcmp(buf, "exit")==0) break;
        
        x = fork();
        if(x==0) {
        	printf("Child process to execute\n");
            
            char* argv[10];
            token = strtok(buf, " ");
            
            int i=0;
            for(;;) {
            	argv[i] = (char*)malloc(strlen(token+1));
                strcpy(argv[i], token);
                token = strtok(NULL, " ");
                i++;
                if(token==NULL) break;
            }
            argv[i] = 0
        	if(execve(buf, argv, 0) < 0) {
            	perror("Fail to execute\n");
                exit(EXIT_FAILURE);
            }
            execve(buf, argv, 0);
        }
        else wait(0);
    }
}
  • scanf() 대신 fgets() 사용
  • fork()==0일 때, strtok()로 입력을 공백 기준으로 구분하여 argv 배열에 저장

'ex1 &'를 handle할 수 있도록 하기

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

void main() {
	int i, x;
    
    for(;;) {
        char buf[100];
        printf("$ ");
        fgets(buf, 255, stdin);
        buf[strlen(buf)-1] = '\0';
        
        if(strcmp(buf, "exit")==0) break;
        
        char* argv[100];
        int argc = 0;
        argv[argc] = strtok(buf, " ");
        while (argv[argc]!=NULL) {
        	argc++;
            argv[argc] = strtok(NULL, " ");
        }

        if(fork()==0) {
        	printf("Child process to execute\n");
        	if(execve(buf, argv, 0) < 0) {
            	perror("Fail to execute\n");
                exit(EXIT_FAILURE);
            }
        }
        else {
        	if(argv[argc-1][0] != '&') wait(0);
        	else sleep(1);
       }
    }
}
  • 명령어의 마지막 인자가 '&'이면 sleep()이 실행되어 잠시 기다린다 → 다음 명령어 입력 줄 등장
  • 그렇지 않으면 wait()이 실행되어 자식 프로세스가 종료되는 것을 기다린다.

명령어 앞에 '/bin/' 입력 안해도 되도록 하기

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

void main() {
    for(;;) {
        char buf[100];
        printf("$ ");
        fgets(buf, 255, stdin);
        buf[strlen(buf)-1] = '\0';
        
        if(strcmp(buf, "exit")==0) break;
        
        char* argv[100];
        int argc = 0;
        argv[argc] = strtok(buf, " ");
        while (argv[argc]!=NULL) {
        	argc++;
            argv[argc] = strtok(NULL, " ");
        }

        if(fork()==0) {
        	printf("Child process to execute\n");
            char pathname[100];
            sprintf(pathname, "%s%s", "/bin", argv[0]);
            
        	if(execve(pathname, argv, 0) < 0) {
            	perror("Fail to execute\n");
                exit(EXIT_FAILURE);
            }
        }
        else {
        	if(argv[argc-1][0] != '&') wait(0);
        	else sleep(1);
       }
    }
}
  • sprintf를 사용하여 pathname에 '/bin'과 입력하는 문자열이 합쳐진 문자열을 저장
  • execve()의 인자로 pathname을 사용하여 함수 실행 시에는 전체 경로명이 포함된 명령어가 전달되도록 함

환경 변수 출력

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

void main() {
	char* s;
    s = getenv("PATH");
    
    char* arr[100];
    int i=0;
    arr[i] = strtok(s, ":");
    while(arr[i]!=NULL) {
    	i++;
        arr[i] = strtok(NULL, ":");
    }
    
    int j;
    for(j=0; j<i; j++) {
    	printf("%s\n", arr[j]);
    }
}
  • : 를 기준으로 문자열을 잘라 출력하여 더 보기 편하도록 하였다.

명령어만 적어도 되도록

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

void main() {
    for(;;) {
        char buf[100];
        printf("$ ");
        fgets(buf, 255, stdin);
        buf[strlen(buf)-1] = '\0';
        
        if(strcmp(buf, "exit")==0) break;
        
        char* argv[100];
        int argc = 0;
        argv[argc] = strtok(buf, " ");
        while (argv[argc]!=NULL) {
        	argc++;
            argv[argc] = strtok(NULL, " ");
        }

        if(fork()==0) {
        	printf("Child process to execute\n");
            char *s = strtok(getenv("PATH"), ":");
            while (s!=NULL) {
            	char pathname[100];
            	sprintf(pathname, "%s%s", s, argv[0]);
                
                if(execve(pathname, argv, 0) < 0) {
                	s = strtok(NULL, ":");
                }
                else exit(EXIT_SUCCESS);
            }
           	perror("Fail to execute\n");
            exit(EXIT_FAILURE);
        }
        else {
        	if(argv[argc-1][0] != '&') wait(0);
        	else sleep(1);
       }
    }
}
  • 만약 해당 환경변수로 execve()가 실행되지 않으면 strtok()를 이용하여 다음 환경 변수로 시도하고, 이를 성공할 때까지 반복

cat f1 > f3 구현

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

void main() {
    for(;;) {
        char buf[100];
        printf("$ ");
        fgets(buf, 255, stdin);
        buf[strlen(buf)-1] = '\0';
        
        if(strcmp(buf, "exit")==0) break;
        
        char* argv[100];
        int argc = 0;
        argv[argc] = strtok(buf, " ");
        while (argv[argc]!=NULL) {
        	argc++;
            argv[argc] = strtok(NULL, " ");
        }

        if(fork()==0) {
        	printf("Child process to execute\n");
            if (argv[argc-2][0] == '>') {
            	int fd = open(argv[argc-1], O_WRONLY|O_CREAT|O_TRUNC, 00777);
                dup2(fd, STDOUT_FILENO);
                close(fd);
            }
            
            char *s = strtok(getenv("PATH"), ":");
            while (s!=NULL) {
            	char pathname[100];
            	sprintf(pathname, "%s%s", s, argv[0]);
                
                if(execve(pathname, argv, 0) < 0) {
                	s = strtok(NULL, ":");
                }
                else exit(EXIT_SUCCESS);
            }
           	perror("Fail to execute\n");
            exit(EXIT_FAILURE);
        }
        else {
        	if(argv[argc-1][0] != '&') wait(0);
        	else sleep(1);
       }
    }
}
  • 앞의 코드에 >을 이용한 redirection 기능을 추가
    • 두 번재 인자가 >일 때 dup2() 함수를 이용하여 기존 file descriptor로 가는 호출을 새로운 file descriptor로 가도록 함

network

client → server로 단어 보내기

serv.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>

#define SERV_TCP_PORT 13579
#define SERV_ADDR "165.246.38.152"

int main(void) {
    int s1, s2, x, y;
    struct sockaddr_in serv_addr, cli_addr;
    char buf[50];
    socklen_t xx;

    printf("Hi, I am the server\n");

    bzero((char*) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = PF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(SERV_ADDR);
    serv_addr.sin_port = htons(SERV_TCP_PORT);

    //open a tcp socket
    if ((s1 = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
        printf("socket creation error\n");
        exit(1);
    }
    printf("socket opened successfully. socket num is %d\n", s1);

    // bind ip
    x = bind(s1, (struct sockaddr*) &serv_addr, sizeof(serv_addr));
    if (x < 0) {
        printf("binding failed\n");
        exit(1);
    }
    printf("binding passed\n");
    listen(s1, 5);
    xx = sizeof(cli_addr);
    s2 = accept(s1, (struct sockaddr*) &cli_addr, &xx);
    printf("we passed accept. new socket num is %d\n", s2);
    
    // >>>>>>>>>>>>>>>>> 여기서부터 보면 됨 !! <<<<<<<<<<<<<<<<<<<<

    // read msg from client
    printf("now reading from client\n");
    y = read(s2, buf, 50);
    buf[y] = 0;
    printf("we got %s from cli\n", buf);
    // send msg to the client
    printf("enter a string to send to client\n");
    scanf("%s", buf);
    write(s2, buf, strlen(buf));
    close(s2); // disconnect the connection
    close(s1); // close the original socket

    return 0;
}

cli.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>

#define SERV_TCP_PORT 13579
#define SERV_ADDR "165.246.38.152"

int main(void) {
    int x, y;
    struct sockaddr_in serv_addr;
    char buf[50];
    printf("Hi, I am the client\n");

    bzero((char*) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = PF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(SERV_ADDR);
    serv_addr.sin_port = htons(SERV_TCP_PORT);

    //open a tcp socket
    if ((x = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
        printf("socket creation error\n");
        exit(1);
    }
    printf("socket opened successfully. socket num is %d\n", x);
    
    // >>>>>>>>>>>>>>>>> 여기서부터 보면 됨 !! <<<<<<<<<<<<<<<<<<<<

    //connect to the server
    if (connect(x, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0) {
        printf("can't connect to the server\n");
        exit(1);
    }
    // send msg to the server
    printf("now i am connected to the server. enter a string to send\n");
    scanf("%s", buf);
    write(x, buf, strlen(buf));
    // read from server
    printf("now reading from server\n");
    y = read(x, buf, 50);
    buf[y] = 0;
    printf("from server: %s\n", buf);
    close(x); // disconect the communication

    return 0;
}

client → server로 문장 보내기

serv.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFFER_SIZE 100
#define LISTEN_BACKLOG 5
#define SERV_ADDR "165.246.38.152"
#define SERV_PORT 13579

int main(void) {
    printf("Hi, I am the server.\n");

    struct sockaddr_in serv_addr, cli_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    if (inet_aton(SERV_ADDR, &serv_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid Address\n");
        exit(EXIT_FAILURE);
    }

    int sfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("Fail to create");
        exit(EXIT_FAILURE);
    }
    printf("Server socket(sfd=%d) is created.\n", sfd);

    if (bind(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        perror("Fail to bind");
        exit(EXIT_FAILURE);
    }

    if (listen(sfd, LISTEN_BACKLOG) == -1) {
        perror("Fail to listen");
        exit(EXIT_FAILURE);
    }

    socklen_t cli_addr_len = sizeof(cli_addr);
    int cfd = accept(sfd, (struct sockaddr*) &cli_addr, &cli_addr_len);
    if (cfd == -1) {
        perror("Fail to accept");
        exit(EXIT_FAILURE);
    }
    printf("Client socket(cfd=%d) is accepted.\n", cfd);

    char buf[BUFFER_SIZE];
    int chunk = read(cfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("receive: %s\n", buf);

	// scanf() 대신 fgets() 사용
    fgets(buf, BUFFER_SIZE - 1, stdin);
    buf[strlen(buf) - 1] = '\0';
    write(cfd, buf, strlen(buf));

    close(cfd);
    close(sfd);
    return 0;
}

cli.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFFER_SIZE 100
#define SERV_ADDR "165.246.38.152"
#define SERV_PORT 13579

int main(void) {
    printf("Hi, I am the client.\n");

    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    if (inet_aton(SERV_ADDR, &serv_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid Address\n");
        exit(EXIT_FAILURE);
    }

    int sfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("Fail to create");
        exit(EXIT_FAILURE);
    }
    printf("Socket(sfd=%d) is created.\n", sfd);

    if (connect(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        perror("Fail to connect");
        exit(EXIT_FAILURE);
    }

	// scanf() 대신 fgets() 사용
    char buf[BUFFER_SIZE];
    fgets(buf, BUFFER_SIZE - 1, stdin);
    buf[strlen(buf) - 1] = '\0';
    write(sfd, buf, strlen(buf));

    int chunk = read(sfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("receive: %s\n", buf);

    close(sfd);
    return 0;
}
  • scanf() 대신 fgets()를 사용하여 공백이 포함된 문장도 입력 받을 수 있도록 하였다.

client → server → client → server ...

serv.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFFER_SIZE 100
#define LISTEN_BACKLOG 5
#define SERV_ADDR "165.246.38.152"
#define SERV_PORT 13579

int main(void) {
    printf("Hi, I am the server.\n");

    struct sockaddr_in serv_addr, cli_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    if (inet_aton(SERV_ADDR, &serv_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid Address\n");
        exit(EXIT_FAILURE);
    }

    int sfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("Fail to create");
        exit(EXIT_FAILURE);
    }
    printf("Server socket(sfd=%d) is created.\n", sfd);

    if (bind(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        perror("Fail to bind");
        exit(EXIT_FAILURE);
    }

    if (listen(sfd, LISTEN_BACKLOG) == -1) {
        perror("Fail to listen");
        exit(EXIT_FAILURE);
    }

    socklen_t cli_addr_len = sizeof(cli_addr);
    int cfd = accept(sfd, (struct sockaddr*) &cli_addr, &cli_addr_len);
    if (cfd == -1) {
        perror("Fail to accept");
        exit(EXIT_FAILURE);
    }
    printf("Client socket(cfd=%d) is accepted.\n", cfd);

    char buf[BUFFER_SIZE];
    while (1) {
        int chunk = read(cfd, buf, BUFFER_SIZE - 1);
        buf[chunk] = '\0';
        if (strcmp(buf, "bye") == 0) {
            break;
        }
        printf("receive: %s\n", buf);

        fgets(buf, BUFFER_SIZE - 1, stdin);
        buf[strlen(buf) - 1] = '\0';
        write(cfd, buf, strlen(buf));
        if (strcmp(buf, "bye") == 0) {
            break;
        }
    }

    close(cfd);
    close(sfd);
    return 0;
}

cli.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFFER_SIZE 100
#define SERV_ADDR "165.246.38.152"
#define SERV_PORT 13579

int main(void) {
    printf("Hi, I am the client.\n");

    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    if (inet_aton(SERV_ADDR, &serv_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid Address\n");
        exit(EXIT_FAILURE);
    }

    int sfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("Fail to create");
        exit(EXIT_FAILURE);
    }
    printf("Socket(sfd=%d) is created.\n", sfd);

    if (connect(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        perror("Fail to connect");
        exit(EXIT_FAILURE);
    }

    char buf[BUFFER_SIZE];
    while (1) {
        fgets(buf, BUFFER_SIZE - 1, stdin);
        buf[strlen(buf) - 1] = '\0';
        write(sfd, buf, strlen(buf));
        if (strcmp(buf, "bye") == 0) {
            break;
        }

        int chunk = read(sfd, buf, BUFFER_SIZE - 1);
        buf[chunk] = '\0';
        if (strcmp(buf, "bye") == 0) {
            break;
        }
        printf("receive: %s\n", buf);
    }

    close(sfd);
    return 0;
}
  • 메시지를 읽고, 보내는 코드를 무한루프 while(1)으로 감싸고, strcmp()를 이용하여 "bye"가 입력되면 무한 루프에서 빠져 나오도록 코드를 수정했다.

다른 학생에게 메시지 보내기

  • 포트 번호를 165.246.38.136으로, 포트 번호를 19924로 수정하여 client에서 보낸 메시지를 에커로 출력해주는 프로그램을 만든다.

Inha web server에서 web page 읽어오기

cli.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFFER_SIZE 100
#define SERV_ADDR "165.246.13.108" // www.inha.ac.kr
#define SERV_PORT 80

int main(void) {
    printf("Hi, I am the client.\n");

    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    if (inet_aton(SERV_ADDR, &serv_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid Address\n");
        exit(EXIT_FAILURE);
    }

    int sfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("Fail to create");
        exit(EXIT_FAILURE);
    }
    printf("Socket(sfd=%d) is created.\n", sfd);

    if (connect(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        perror("Fail to connect");
        exit(EXIT_FAILURE);
    }

    char* request = "GET / HTTP/1.1\r\nHOST: www.inha.ac.kr\r\n\r\n";
    write(sfd, request, strlen(request));

    char buf[BUFFER_SIZE];
    int chunk;
    while ((chunk = recv(sfd, buf, BUFFER_SIZE - 1, 0)) > 0) {
        buf[chunk] = '\0';
        printf("%s", buf);
    }

    close(sfd);
    return 0;
}
  • cli.c만 수정 !!

client, server 순서 상관 없이 대화

serv.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#define BUFFER_SIZE 100
#define LISTEN_BACKLOG 5
#define SERV_ADDR "165.246.38.152"
#define SERV_PORT 13579

int main(void) {
    printf("Hi, I am the server.\n");

    struct sockaddr_in serv_addr, cli_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    if (inet_aton(SERV_ADDR, &serv_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid Address\n");
        exit(EXIT_FAILURE);
    }

    int sfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("Fail to create");
        exit(EXIT_FAILURE);
    }
    printf("Server socket(sfd=%d) is created.\n", sfd);

    if (bind(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        perror("Fail to bind");
        exit(EXIT_FAILURE);
    }

    if (listen(sfd, LISTEN_BACKLOG) == -1) {
        perror("Fail to listen");
        exit(EXIT_FAILURE);
    }

    socklen_t cli_addr_len = sizeof(cli_addr);
    int cfd = accept(sfd, (struct sockaddr*) &cli_addr, &cli_addr_len);
    if (cfd == -1) {
        perror("Fail to accept");
        exit(EXIT_FAILURE);
    }
    printf("Client socket(cfd=%d) is accepted.\n", cfd);

    char buf[BUFFER_SIZE];
    pid_t child_pid = fork();
    if (child_pid == 0) { // child
    	// send message
        while (1) {
            fgets(buf, BUFFER_SIZE - 1, stdin);
            buf[strlen(buf) - 1] = '\0';
            write(cfd, buf, strlen(buf));
            if (strcmp(buf, "bye") == 0) {
                kill(getppid(), SIGKILL);
                break;
            }
        }
    } else { // parent
    	// receive message
        while (1) {
            int chunk = read(cfd, buf, BUFFER_SIZE - 1);
            buf[chunk] = '\0';
            if (strcmp(buf, "bye") == 0) {
                kill(child_pid, SIGKILL);
                break;
            }
            printf("receive: %s\n", buf);
        }
    }

    close(cfd);
    close(sfd);
    return 0;
}

cli.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#define BUFFER_SIZE 100
#define SERV_ADDR "165.246.38.152"
#define SERV_PORT 13579

int main(void) {
    printf("Hi, I am the client.\n");

    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    if (inet_aton(SERV_ADDR, &serv_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid Address\n");
        exit(EXIT_FAILURE);
    }

    int sfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("Fail to create");
        exit(EXIT_FAILURE);
    }
    printf("Socket(sfd=%d) is created.\n", sfd);

    if (connect(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        perror("Fail to connect");
        exit(EXIT_FAILURE);
    }

    char buf[BUFFER_SIZE];
    pid_t child_pid = fork();
    
    // send message
    if (child_pid == 0) { // child
        while (1) {
            fgets(buf, BUFFER_SIZE - 1, stdin);
            buf[strlen(buf) - 1] = '\0';
            write(sfd, buf, strlen(buf));
            if (strcmp(buf, "bye") == 0) {
                kill(getppid(), SIGKILL);
                break;
            }
        }
    } else { // parent
    	// receive message
        while (1) {
            int chunk = read(sfd, buf, BUFFER_SIZE - 1);
            buf[chunk] = '\0';
            if (strcmp(buf, "bye") == 0) {
                kill(child_pid, SIGKILL);
                break;
            }
            printf("receive: %s\n", buf);
        }
    }

    close(sfd);
    return 0;
}
  • fork() 함수를 이용하여 텍스트를 보내는 프로세스와 텍스트를 받는 프로세스를 나누었다.
  • strcmp()로 "bye"가 입력되면 kill()을 이용하여 프로세스 종료

network 2

Simple FTP - 파일 이름 입력하면 내용 출력

serv.c

#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFFER_SIZE 100
#define LISTEN_BACKLOG 5
#define SERV_ADDR "165.246.38.152"
#define SERV_PORT 13579

int main(void) {
    printf("Hi, I am the FTP server.\n");

    struct sockaddr_in serv_addr, cli_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    if (inet_aton(SERV_ADDR, &serv_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid Address\n");
        exit(EXIT_FAILURE);
    }

    int sfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("Fail to create");
        exit(EXIT_FAILURE);
    }
    printf("Server socket(sfd=%d) is created.\n", sfd);

    if (bind(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        perror("Fail to bind");
        exit(EXIT_FAILURE);
    }

    if (listen(sfd, LISTEN_BACKLOG) == -1) {
        perror("Fail to listen");
        exit(EXIT_FAILURE);
    }

    socklen_t cli_addr_len = sizeof(cli_addr);
    int cfd = accept(sfd, (struct sockaddr*) &cli_addr, &cli_addr_len);
    if (cfd == -1) {
        perror("Fail to accept");
        exit(EXIT_FAILURE);
    }
    printf("Client socket(cfd=%d) is accepted.\n", cfd);

    char buf[BUFFER_SIZE];
    int chunk = read(cfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("client => server: %s\n", buf);

    if (strcmp(buf, "hello") == 0) {
        char* init_response = "What file do you want?";
        write(cfd, init_response, strlen(init_response));
        printf("server => client: %s\n", init_response);
        buf[0] = -1;
        write(cfd, buf, 1);

        while (1) {
        	// read file name from client
            chunk = read(cfd, buf, BUFFER_SIZE - 1);
            buf[chunk] = '\0';
            printf("client => server: %s\n", buf);

            if (strcmp(buf, "bye") == 0) {
                break;
            }

            int fd = open(buf, O_RDONLY);
            if (fd == -1) {
                char* response = "Fail to open the file.";
                write(cfd, response, strlen(response));
                printf("server => client: %s\n", response);
                buf[0] = -1;
                write(cfd, buf, 1);
                continue;
            }

            printf("server => client: ");
            fflush(stdout);
            while ((chunk = read(fd, buf, BUFFER_SIZE))) {
                write(cfd, buf, chunk);
                write(STDOUT_FILENO, buf, chunk);
            }
            buf[0] = -1;
            write(cfd, buf, 1);
            printf("\n");

            close(fd);
        }
    }

    close(cfd);
    close(sfd);
    return 0;
}

cli.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#define BUFFER_SIZE 100
#define SERV_ADDR "165.246.38.152"
#define SERV_PORT 13579

int main(void) {
    printf("Hi, I am the FTP client.\n");

    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    if (inet_aton(SERV_ADDR, &serv_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid Address\n");
        exit(EXIT_FAILURE);
    }

    int sfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("Fail to create");
        exit(EXIT_FAILURE);
    }
    printf("Socket(sfd=%d) is created.\n", sfd);

    if (connect(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        perror("Fail to connect");
        exit(EXIT_FAILURE);
    }

    char* init_request = "hello";
    write(sfd, init_request, strlen(init_request));
    printf("client => server: hello\n");

    while (1) {
        char buf[BUFFER_SIZE];
        int chunk;

        printf("server => client: ");
        fflush(stdout);
        while (1) {
            chunk = read(sfd, buf, BUFFER_SIZE);
            if (buf[chunk - 1] == -1) {
                write(STDOUT_FILENO, buf, chunk - 1);
                break;
            } else {
                write(STDOUT_FILENO, buf, chunk);
            }
        }
        printf("\n");

        printf("client => server: ");
        fgets(buf, BUFFER_SIZE - 1, stdin);
        buf[strlen(buf) - 1] = '\0';
        write(sfd, buf, strlen(buf));

        if (strcmp(buf, "bye") == 0) {
            break;
        }
    }

    close(sfd);
    return 0;
}
  • read()와 write() 함수 이용
  • open()으로 파일 열고 읽음

Simple FTP - 여러 client handle

cli.c는 그대로

serv.c

#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFFER_SIZE 100
#define LISTEN_BACKLOG 5
#define SERV_ADDR "165.246.38.152"
#define SERV_PORT 13579

int main(void) {
    printf("Hi, I am the FTP server.\n");

    struct sockaddr_in serv_addr, cli_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    if (inet_aton(SERV_ADDR, &serv_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid Address\n");
        exit(EXIT_FAILURE);
    }

    int sfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("Fail to create");
        exit(EXIT_FAILURE);
    }
    printf("Server socket(sfd=%d) is created.\n", sfd);

    if (bind(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        perror("Fail to bind");
        exit(EXIT_FAILURE);
    }

    if (listen(sfd, LISTEN_BACKLOG) == -1) {
        perror("Fail to listen");
        exit(EXIT_FAILURE);
    }

    socklen_t cli_addr_len = sizeof(cli_addr);

    while (1) {
        int cfd = accept(sfd, (struct sockaddr*) &cli_addr, &cli_addr_len);
        if (cfd == -1) {
            perror("Fail to accept");
            exit(EXIT_FAILURE);
        }
        printf("Client socket(cfd=%d) is accepted.\n", cfd);

        if (fork() == 0) {
            char buf[BUFFER_SIZE];
            int chunk = read(cfd, buf, BUFFER_SIZE - 1);
            buf[chunk] = '\0';
            printf("client => server: %s\n", buf);

            if (strcmp(buf, "hello") == 0) {
                char* init_response = "What file do you want?";
                write(cfd, init_response, strlen(init_response));
                printf("server => client: %s\n", init_response);
                buf[0] = -1;
                write(cfd, buf, 1);

                while (1) {
                    chunk = read(cfd, buf, BUFFER_SIZE - 1);
                    buf[chunk] = '\0';
                    printf("client => server: %s\n", buf);

                    if (strcmp(buf, "bye") == 0) {
                        break;
                    }
                    
                    int fd = open(buf, O_RDONLY);
                    if (fd == -1) {
                        char* response = "Fail to open the file.";
                        write(cfd, response, strlen(response));
                        printf("server => client: %s\n", response);
                        buf[0] = -1;
                        write(cfd, buf, 1);
                        continue;
                    }

                    printf("server => client: ");
                    fflush(stdout);
                    while ((chunk = read(fd, buf, BUFFER_SIZE))) {
                        write(cfd, buf, chunk);
                        write(STDOUT_FILENO, buf, chunk);
                    }
                    buf[0] = -1;
                    write(cfd, buf, 1);
                    printf("\n");

                    close(fd);
                }
            }

            close(cfd);
            close(sfd);
            exit(EXIT_SUCCESS);
        }
        close(cfd);
    }

    close(sfd);
    return 0;
}
  • fork()로 새로운 연결이 생길 때마다 자식 프로세스를 만들어 소켓의 연결을 만들어줌

lab server - PC 간 Simple FTP 구현 & lab server → PC로 파일 다운로드은 생략 !


select

ping-pong-pang-pung protocol

serv.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFFER_SIZE 100
#define LISTEN_BACKLOG 5
#define MAX_FD_COUNT 50
#define SERV_ADDR "165.246.38.152"
#define SERV_PORT 13579

void handle_protocol(int fd, fd_set* set, int recv_count);

int main(void) {
    printf("Hi, I am the server.\n");

    struct sockaddr_in serv_addr, cli_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    if (inet_aton(SERV_ADDR, &serv_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid Address\n");
        exit(EXIT_FAILURE);
    }

    int sfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("Fail to create");
        exit(EXIT_FAILURE);
    }
    printf("Server socket(sfd=%d) is created.\n", sfd);

    if (bind(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        perror("Fail to bind");
        exit(EXIT_FAILURE);
    }

    if (listen(sfd, LISTEN_BACKLOG) == -1) {
        perror("Fail to listen");
        exit(EXIT_FAILURE);
    }

    socklen_t cli_addr_len = sizeof(cli_addr);

    fd_set read_fds, temp_fds;
    FD_ZERO(&read_fds);
    FD_SET(sfd, &read_fds);

    int recv_counts[MAX_FD_COUNT] = { 0 };

    while (1) {
        temp_fds = read_fds;
        select(MAX_FD_COUNT, &temp_fds, NULL, NULL, NULL);
        for (int fd = 0; fd < MAX_FD_COUNT; fd += 1) {
            if (FD_ISSET(fd, &temp_fds)) {
                if (fd == sfd) {
                    int cfd = accept(sfd, (struct sockaddr*) &cli_addr, &cli_addr_len);
                    if (cfd == -1) {
                        perror("Fail to accept");
                        exit(EXIT_FAILURE);
                    }
                    printf("Client socket(cfd=%d) is accepted.\n", cfd);
                    FD_SET(cfd, &read_fds);
                } else {
                    recv_counts[fd] += 1;
                    handle_protocol(fd, &read_fds, recv_counts[fd]);
                }
            }
        }
    }
    return 0;
}

void handle_protocol(int fd, fd_set* set, int recv_count) {
    char buf[BUFFER_SIZE];
    int chunk = read(fd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("client(cfd=%d) => server: %s\n", fd, buf);

    if (strcmp(buf, "ping") == 0 && recv_count == 1) {
        write(fd, "pong", 4);
        printf("server => client(cfd=%d): pong\n", fd);
    } else if (strcmp(buf, "pang") == 0 && recv_count == 2) {
        write(fd, "pung", 4);
        printf("server => client(cfd=%d): pung\n", fd);
    } else if (strcmp(buf, "ping") == 0 && recv_count == 3) {
        write(fd, "Process completed.", 18);
        printf("server => client(cfd=%d): Process completed.\n", fd);
        printf("Client socket(cfd=%d) is closed.\n", fd);
        close(fd);
        FD_CLR(fd, set);
    } else {
        write(fd, "Invalid input to protocol.", 26);
        fprintf(stderr, "Invalid input to protocol.\n");
        printf("Client socket(cfd=%d) is closed.\n", fd);
        close(fd);
        FD_CLR(fd, set);
    }
}

cli.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFFER_SIZE 100
#define SERV_ADDR "165.246.38.152"
#define SERV_PORT 13579

int main(void) {
    printf("Hi, I am the client.\n");

    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    if (inet_aton(SERV_ADDR, &serv_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid Address\n");
        exit(EXIT_FAILURE);
    }

    int sfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("Fail to create");
        exit(EXIT_FAILURE);
    }
    printf("Socket(sfd=%d) is created.\n", sfd);

    if (connect(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        perror("Fail to connect");
        exit(EXIT_FAILURE);
    }

    char buf[BUFFER_SIZE];
    printf("client => server: (Enter ping) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(sfd, buf, strlen(buf) - 1);

    int chunk = read(sfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("server => client: %s\n", buf);

    if (strcmp(buf, "pong") != 0) {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(sfd);
        exit(EXIT_FAILURE);
    }

    printf("client => server: (Enter pang) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(sfd, buf, strlen(buf) - 1);

    chunk = read(sfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("server => client: %s\n", buf);

    if (strcmp(buf, "pung") != 0) {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(sfd);
        exit(EXIT_FAILURE);
    }

    printf("client => server: (Enter ping) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(sfd, buf, strlen(buf) - 1);

    chunk = read(sfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("server => client: %s\n", buf);

    close(sfd);
    return 0;
}

client의 name, age 저장

serv.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFFER_SIZE 100
#define LISTEN_BACKLOG 5
#define MAX_FD_COUNT 50
#define MAX_NAME_LENGTH 20
#define MAX_AGE_LENGTH 5
#define MAX_CLIENT_COUNT 50
#define SERV_ADDR "165.246.38.152"
#define SERV_PORT 13579
#define STATE_UNDEFINED 0
#define STATE_INIT 1
#define STATE_PROTOCOL 2
#define STATE_ASK_NAME 3
#define STATE_ASK_AGE 4
#define STATE_CHAT 5

typedef struct client {
    char name[MAX_NAME_LENGTH];
    char age[MAX_AGE_LENGTH];
} CLIENT;

void handle_protocol(int fd, fd_set* set, int* states, CLIENT* clients);

int main(void) {
    printf("Hi, I am the server.\n");

    struct sockaddr_in serv_addr, cli_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    if (inet_aton(SERV_ADDR, &serv_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid Address\n");
        exit(EXIT_FAILURE);
    }

    int sfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("Fail to create");
        exit(EXIT_FAILURE);
    }
    printf("Server socket(sfd=%d) is created.\n", sfd);

    if (bind(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        perror("Fail to bind");
        exit(EXIT_FAILURE);
    }

    if (listen(sfd, LISTEN_BACKLOG) == -1) {
        perror("Fail to listen");
        exit(EXIT_FAILURE);
    }

    socklen_t cli_addr_len = sizeof(cli_addr);

    fd_set read_fds, temp_fds;
    FD_ZERO(&read_fds);
    FD_SET(sfd, &read_fds);

    int states[MAX_FD_COUNT] = { STATE_UNDEFINED };
    CLIENT clients[MAX_CLIENT_COUNT];

    while (1) {
        temp_fds = read_fds;
        select(MAX_FD_COUNT, &temp_fds, NULL, NULL, NULL);
        for (int fd = 0; fd < MAX_FD_COUNT; fd += 1) {
            if (FD_ISSET(fd, &temp_fds)) {
                if (fd == sfd) {
                    int cfd = accept(sfd, (struct sockaddr*) &cli_addr, &cli_addr_len);
                    if (cfd == -1) {
                        perror("Fail to accept");
                        exit(EXIT_FAILURE);
                    }
                    states[cfd] = STATE_INIT;
                    printf("Client socket(cfd=%d) is accepted.\n", cfd);
                    FD_SET(cfd, &read_fds);
                } else {
                    handle_protocol(fd, &read_fds, states, clients);
                }
            }
        }
    }
    return 0;
}

void handle_protocol(int fd, fd_set* set, int* states, CLIENT* clients) {
    char buf[BUFFER_SIZE];
    int chunk = read(fd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("client(cfd=%d) => server: %s\n", fd, buf);

    int state = states[fd];
    if (state == STATE_INIT && strcmp(buf, "ping") == 0) {
        write(fd, "pong", 4);
        printf("server => client(cfd=%d): pong\n", fd);
        states[fd] += 1;
    } else if (state == STATE_PROTOCOL && strcmp(buf, "pang") == 0) {
        write(fd, "pung. Enter your name.", 22);
        printf("server => client(cfd=%d): pung. Enter your name.\n", fd);
        states[fd] += 1;
    } else if (state == STATE_ASK_NAME && strlen(buf) > 0) {
        strcpy(clients[fd].name, buf);
        write(fd, "Enter your age.", 15);
        printf("server => client(cfd=%d): Enter your age.\n", fd);
        states[fd] += 1;
    } else if (state == STATE_ASK_AGE && strlen(buf) > 0) {
        strcpy(clients[fd].age, buf);
        write(fd, "Process completed. Start chatting.", 34);
        printf("server => client(cfd=%d): Process completed. Start chatting.\n", fd);
        states[fd] += 1;
    } else if (state == STATE_CHAT && strlen(buf) > 0) {
        for (int other_fd = 0; other_fd < MAX_FD_COUNT; other_fd += 1) {
            if (fd != other_fd && states[other_fd] == STATE_CHAT) {
                write(other_fd, buf, strlen(buf));
                printf("server => client(cfd=%d): %s\n", other_fd, buf);
            }
        }
    } else {
        write(fd, "Invalid input to protocol.", 26);
        fprintf(stderr, "Invalid input to protocol.\n");
        printf("Client socket(cfd=%d) is closed.\n", fd);
        close(fd);
        FD_CLR(fd, set);
    }
}

cli.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFFER_SIZE 100
#define SERV_ADDR "165.246.38.152"
#define SERV_PORT 13579

int main(void) {
    printf("Hi, I am the client.\n");

    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    if (inet_aton(SERV_ADDR, &serv_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid Address\n");
        exit(EXIT_FAILURE);
    }

    int sfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("Fail to create");
        exit(EXIT_FAILURE);
    }
    printf("Socket(sfd=%d) is created.\n", sfd);

    if (connect(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        perror("Fail to connect");
        exit(EXIT_FAILURE);
    }

    char buf[BUFFER_SIZE];
    printf("client => server: (Enter ping) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(sfd, buf, strlen(buf) - 1);

    int chunk = read(sfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("server => client: %s\n", buf);

    if (strcmp(buf, "pong") != 0) {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(sfd);
        exit(EXIT_FAILURE);
    }

    printf("client => server: (Enter pang) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(sfd, buf, strlen(buf) - 1);

    chunk = read(sfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("server => client: %s\n", buf);

    if (strcmp(buf, "pung. Enter your name.") != 0) {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(sfd);
        exit(EXIT_FAILURE);
    }

    printf("client => server: (Enter your name) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(sfd, buf, strlen(buf) - 1);

    chunk = read(sfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("server => client: %s\n", buf);

    if (strcmp(buf, "Enter your age.") != 0) {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(sfd);
        exit(EXIT_FAILURE);
    }

    printf("client => server: (Enter your age) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(sfd, buf, strlen(buf) - 1);

    chunk = read(sfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("server => client: %s\n", buf);

    if (strcmp(buf, "Process completed. Start chatting.") != 0) {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(sfd);
        exit(EXIT_FAILURE);
    }

    if (fork() == 0) {
        while (1) {
            chunk = read(sfd, buf, BUFFER_SIZE - 1);
            if (chunk > 0) {
                buf[chunk] = '\0';
                printf("server => client: %s\n", buf);
            }
        }
    } else {
        while (1) {
            printf("client => server: (Chat) ");
            fgets(buf, BUFFER_SIZE - 1, stdin);
            write(sfd, buf, strlen(buf) - 1);
        }
    }

    close(sfd);
    return 0;
}

client의 name, age, partner 저장

serv.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFFER_SIZE 100
#define LISTEN_BACKLOG 5
#define MAX_FD_COUNT 50
#define MAX_NAME_LENGTH 20
#define MAX_AGE_LENGTH 5
#define MAX_CLIENT_COUNT 50
#define SERV_ADDR "165.246.38.152"
#define SERV_PORT 13579
#define STATE_UNDEFINED 0
#define STATE_INIT 1
#define STATE_PROTOCOL 2
#define STATE_ASK_NAME 3
#define STATE_ASK_AGE 4
#define STATE_ASK_PARTNER 5
#define STATE_CHAT 6

typedef struct client {
    char name[MAX_NAME_LENGTH];
    char age[MAX_AGE_LENGTH];
    int partner;
} CLIENT;

void handle_protocol(int fd, fd_set* set, int* states, CLIENT* clients);

int main(void) {
    printf("Hi, I am the server.\n");

    struct sockaddr_in serv_addr, cli_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    if (inet_aton(SERV_ADDR, &serv_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid Address\n");
        exit(EXIT_FAILURE);
    }

    int sfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("Fail to create");
        exit(EXIT_FAILURE);
    }
    printf("Server socket(sfd=%d) is created.\n", sfd);

    if (bind(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        perror("Fail to bind");
        exit(EXIT_FAILURE);
    }

    if (listen(sfd, LISTEN_BACKLOG) == -1) {
        perror("Fail to listen");
        exit(EXIT_FAILURE);
    }

    socklen_t cli_addr_len = sizeof(cli_addr);

    fd_set read_fds, temp_fds;
    FD_ZERO(&read_fds);
    FD_SET(sfd, &read_fds);

    int states[MAX_FD_COUNT] = { STATE_UNDEFINED };
    CLIENT clients[MAX_CLIENT_COUNT];

    while (1) {
        temp_fds = read_fds;
        select(MAX_FD_COUNT, &temp_fds, NULL, NULL, NULL);
        for (int fd = 0; fd < MAX_FD_COUNT; fd += 1) {
            if (FD_ISSET(fd, &temp_fds)) {
                if (fd == sfd) {
                    int cfd = accept(sfd, (struct sockaddr*) &cli_addr, &cli_addr_len);
                    if (cfd == -1) {
                        perror("Fail to accept");
                        exit(EXIT_FAILURE);
                    }
                    states[cfd] = STATE_INIT;
                    printf("Client socket(cfd=%d) is accepted.\n", cfd);
                    FD_SET(cfd, &read_fds);
                } else {
                    handle_protocol(fd, &read_fds, states, clients);
                }
            }
        }
    }
    return 0;
}

void handle_protocol(int fd, fd_set* set, int* states, CLIENT* clients) {
    char buf[BUFFER_SIZE];
    int chunk = read(fd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("client(cfd=%d) => server: %s\n", fd, buf);

    int state = states[fd];
    if (state == STATE_INIT && strcmp(buf, "ping") == 0) {
        write(fd, "pong", 4);
        printf("server => client(cfd=%d): pong\n", fd);
        states[fd] += 1;
    } else if (state == STATE_PROTOCOL && strcmp(buf, "pang") == 0) {
        write(fd, "pung. Enter your name.", 22);
        printf("server => client(cfd=%d): pung. Enter your name.\n", fd);
        states[fd] += 1;
    } else if (state == STATE_ASK_NAME && strlen(buf) > 0) {
        strcpy(clients[fd].name, buf);
        write(fd, "Enter your age.", 15);
        printf("server => client(cfd=%d): Enter your age.\n", fd);
        states[fd] += 1;
    } else if (state == STATE_ASK_AGE && strlen(buf) > 0) {
        strcpy(clients[fd].age, buf);
        write(fd, "Enter your partner.", 19);
        printf("server => client(cfd=%d): Enter your partner.\n", fd);
        states[fd] += 1;
    } else if (state == STATE_ASK_PARTNER && strlen(buf) > 0) {
        for (int other_fd = 0; other_fd < MAX_FD_COUNT; other_fd += 1) {
            if (fd != other_fd && states[other_fd] > STATE_ASK_NAME && strcmp(buf, clients[other_fd].name) == 0) {
                clients[fd].partner = other_fd;
                write(fd, "Process completed. Start chatting.", 34);
                printf("server => client(cfd=%d): Process completed. Start chatting.\n", fd);
                states[fd] += 1;
                break;
            }
        }
    } else if (state == STATE_CHAT && strlen(buf) > 0) {
        write(clients[fd].partner, buf, strlen(buf));
        printf("server => client(cfd=%d): %s\n", clients[fd].partner, buf);
        sprintf(buf, " (%s to %s)", clients[fd].name, clients[clients[fd].partner].name);
        write(clients[fd].partner, buf, strlen(buf));
    } else {
        write(fd, "Invalid input to protocol.", 26);
        fprintf(stderr, "Invalid input to protocol.\n");
        printf("Client socket(cfd=%d) is closed.\n", fd);
        close(fd);
        FD_CLR(fd, set);
    }
}

cli.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFFER_SIZE 100
#define SERV_ADDR "165.246.38.152"
#define SERV_PORT 13579

int main(void) {
    printf("Hi, I am the client.\n");

    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    if (inet_aton(SERV_ADDR, &serv_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid Address\n");
        exit(EXIT_FAILURE);
    }

    int sfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("Fail to create");
        exit(EXIT_FAILURE);
    }
    printf("Socket(sfd=%d) is created.\n", sfd);

    if (connect(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        perror("Fail to connect");
        exit(EXIT_FAILURE);
    }

    char buf[BUFFER_SIZE];
    printf("client => server: (Enter ping) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(sfd, buf, strlen(buf) - 1);

    int chunk = read(sfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("server => client: %s\n", buf);

    if (strcmp(buf, "pong") != 0) {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(sfd);
        exit(EXIT_FAILURE);
    }

    printf("client => server: (Enter pang) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(sfd, buf, strlen(buf) - 1);

    chunk = read(sfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("server => client: %s\n", buf);

    if (strcmp(buf, "pung. Enter your name.") != 0) {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(sfd);
        exit(EXIT_FAILURE);
    }

    printf("client => server: (Enter your name) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(sfd, buf, strlen(buf) - 1);

    chunk = read(sfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("server => client: %s\n", buf);

    if (strcmp(buf, "Enter your age.") != 0) {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(sfd);
        exit(EXIT_FAILURE);
    }

    printf("client => server: (Enter your age) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(sfd, buf, strlen(buf) - 1);

    chunk = read(sfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("server => client: %s\n", buf);

    if (strcmp(buf, "Enter your partner.") != 0) {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(sfd);
        exit(EXIT_FAILURE);
    }

    printf("client => server: (Enter your partner) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(sfd, buf, strlen(buf) - 1);

    chunk = read(sfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("server => client: %s\n", buf);

    if (strcmp(buf, "Process completed. Start chatting.") != 0) {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(sfd);
        exit(EXIT_FAILURE);
    }

    if (fork() == 0) {
        while (1) {
            chunk = read(sfd, buf, BUFFER_SIZE - 1);
            if (chunk > 0) {
                buf[chunk] = '\0';
                printf("server => client: %s\n", buf);
            }
        }
    } else {
        while (1) {
            printf("client => server: (Chat) ");
            fgets(buf, BUFFER_SIZE - 1, stdin);
            write(sfd, buf, strlen(buf) - 1);
        }
    }

    close(sfd);
    return 0;
}

자신의 partner이랑만 대화

serv.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFFER_SIZE 100
#define LISTEN_BACKLOG 5
#define MAX_FD_COUNT 50
#define MAX_NAME_LENGTH 20
#define MAX_AGE_LENGTH 5
#define MAX_CLIENT_COUNT 50
#define SERV_ADDR "165.246.38.152"
#define SERV_PORT 13579
#define STATE_UNDEFINED 0
#define STATE_INIT 1
#define STATE_ASK_NAME 2
#define STATE_READY 3
#define STATE_ASK_PARTNER 4
#define STATE_CHAT 5

typedef struct client {
    char name[MAX_NAME_LENGTH];
    char age[MAX_AGE_LENGTH];
    int partner;
} CLIENT;

void handle_protocol(int fd, fd_set* set, int* states, CLIENT* clients);

int main(void) {
    printf("Hi, I am the server.\n");

    struct sockaddr_in serv_addr, cli_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    if (inet_aton(SERV_ADDR, &serv_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid Address\n");
        exit(EXIT_FAILURE);
    }

    int sfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("Fail to create");
        exit(EXIT_FAILURE);
    }
    printf("Server socket(sfd=%d) is created.\n", sfd);

    if (bind(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        perror("Fail to bind");
        exit(EXIT_FAILURE);
    }

    if (listen(sfd, LISTEN_BACKLOG) == -1) {
        perror("Fail to listen");
        exit(EXIT_FAILURE);
    }

    socklen_t cli_addr_len = sizeof(cli_addr);

    fd_set read_fds, temp_fds;
    FD_ZERO(&read_fds);
    FD_SET(sfd, &read_fds);

    int states[MAX_FD_COUNT] = { STATE_UNDEFINED };
    CLIENT clients[MAX_CLIENT_COUNT];

    while (1) {
        temp_fds = read_fds;
        select(MAX_FD_COUNT, &temp_fds, NULL, NULL, NULL);
        for (int fd = 0; fd < MAX_FD_COUNT; fd += 1) {
            if (FD_ISSET(fd, &temp_fds)) {
                if (fd == sfd) {
                    int cfd = accept(sfd, (struct sockaddr*) &cli_addr, &cli_addr_len);
                    if (cfd == -1) {
                        perror("Fail to accept");
                        exit(EXIT_FAILURE);
                    }
                    states[cfd] = STATE_INIT;
                    printf("Client socket(cfd=%d) is accepted.\n", cfd);
                    FD_SET(cfd, &read_fds);
                } else {
                    handle_protocol(fd, &read_fds, states, clients);
                }
            }
        }
    }
    return 0;
}

void handle_protocol(int fd, fd_set* set, int* states, CLIENT* clients) {
    char buf[BUFFER_SIZE];
    int chunk = read(fd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("client(cfd=%d) => server: %s\n", fd, buf);

    int state = states[fd];
    if (state == STATE_INIT && strcmp(buf, "hello") == 0) {
        write(fd, "name?", 5);
        printf("server => client(cfd=%d): name?\n", fd);
        states[fd] += 1;
    } else if (state == STATE_ASK_NAME && strlen(buf) > 0) {
        strcpy(clients[fd].name, buf);
        write(fd, "ready?", 6);
        printf("server => client(cfd=%d): ready?\n", fd);
        states[fd] += 1;
    } else if (state == STATE_READY && strcmp(buf, "yes") == 0) {
        strcpy(buf, "client list(");
        for (int other_fd = 0; other_fd < MAX_FD_COUNT; other_fd += 1) {
            if (fd != other_fd && states[other_fd] > STATE_ASK_NAME) {
                strcat(buf, clients[other_fd].name);
                strcat(buf, ", ");
            }
        }
        if (buf[strlen(buf) - 2] == ',') {
            buf[strlen(buf) - 2] = '\0';
        }
        strcat(buf, ")");
        write(fd, buf, strlen(buf));
        printf("server => client(cfd=%d): %s\n", fd, buf);
        states[fd] += 1;
    } else if (state == STATE_ASK_PARTNER && strlen(buf) > 0) {
        for (int other_fd = 0; other_fd < MAX_FD_COUNT; other_fd += 1) {
            if (fd != other_fd && states[other_fd] > STATE_ASK_NAME && strcmp(buf, clients[other_fd].name) == 0) {
                clients[fd].partner = other_fd;
                write(fd, "go", 2);
                printf("server => client(cfd=%d): go\n", fd);
                states[fd] += 1;
                break;
            }
        }
    } else if (state == STATE_CHAT && strlen(buf) > 0) {
        write(clients[fd].partner, buf, strlen(buf));
        printf("server => client(cfd=%d): %s\n", clients[fd].partner, buf);
    } else {
        write(fd, "Invalid input to protocol.", 26);
        fprintf(stderr, "Invalid input to protocol.\n");
        printf("Client socket(cfd=%d) is closed.\n", fd);
        close(fd);
        FD_CLR(fd, set);
    }
}

cli.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFFER_SIZE 100
#define SERV_ADDR "165.246.38.152"
#define SERV_PORT 13579

int main(void) {
    printf("Hi, I am the client.\n");

    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    if (inet_aton(SERV_ADDR, &serv_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid Address\n");
        exit(EXIT_FAILURE);
    }

    int sfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("Fail to create");
        exit(EXIT_FAILURE);
    }
    printf("Socket(sfd=%d) is created.\n", sfd);

    if (connect(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        perror("Fail to connect");
        exit(EXIT_FAILURE);
    }

    printf("client => server: hello\n");
    write(sfd, "hello", 5);

    char buf[BUFFER_SIZE];
    int chunk = read(sfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("server => client: %s\n", buf);

    if (strcmp(buf, "name?") != 0) {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(sfd);
        exit(EXIT_FAILURE);
    }

    printf("client => server: (Enter your name) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(sfd, buf, strlen(buf) - 1);

    chunk = read(sfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("server => client: %s\n", buf);

    if (strcmp(buf, "ready?") != 0) {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(sfd);
        exit(EXIT_FAILURE);
    }

    printf("client => server: (Enter yes) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(sfd, buf, strlen(buf) - 1);

    chunk = read(sfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("server => client: %s\n", buf);

    printf("client => server: (Enter your partner) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(sfd, buf, strlen(buf) - 1);

    chunk = read(sfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("server => client: %s\n", buf);

    if (strcmp(buf, "go") != 0) {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(sfd);
        exit(EXIT_FAILURE);
    }

    if (fork() == 0) {
        while (1) {
            chunk = read(sfd, buf, BUFFER_SIZE - 1);
            if (chunk > 0) {
                buf[chunk] = '\0';
                printf("server => client: %s\n", buf);
            }
        }
    } else {
        while (1) {
            printf("client => server: (Chat) ");
            fgets(buf, BUFFER_SIZE - 1, stdin);
            write(sfd, buf, strlen(buf) - 1);
        }
    }

    close(sfd);
    return 0;
}

signal

ex1.c와 ex2.c를 Ctrl+C로 종료해보기

ex1.c

#include <stdio.h>

void main() {
	for(;;);
}
  • Ctrl+C 로 종료됨

ex2.c

#include <stdio.h>
#include <signal.h>

void foo(int signum) {
	printf("I am ok\n");
}

void main() {
	signal(2, foo);
    for(;;);
}
  • Ctrl+C 로 종료되지 않음
  • SIGINT의 입력에 대한 동작을 덮어씌워 기본 동작(프로세스 종료)이 사라짐
  • kill -2로 죽지 않음
  • 비슷한 방법으로 signal(15, foo); 하면 kill -15로 죽지 않음
  • kill -9 ( = SIGKILL )은 새로운 동작을 정의할 수 없어 항상 프로세스를 종료시킴

터미널이 종료될 때 ex2가 죽는 것 방지

#include <stdio.h>
#include <signal.h>

void foo(int signum) {
	printf("I am ok\n");
}

void main() {
	signal(2, foo);
    signal(15, foo);
    signal(1, foo);
    for(;;);
}
  • 1(SIGHUP)에 대한 동작을 정의

고아 프로세스

#include <stdio.h>
#include <signal.h>

void foo(int signum) {
	printf("I am ok\n");
}

void main() {
	if(fork()==0) for(;;);
    else {
    	if(fork()==0) for(;;);
    }
    for(;;);
}
  • 부모 프로세스와 자식 프로세스 2개, 총 3개의 프로세스가 생성됨
  • 이 때, 부모 프로세스를 죽이면 자식 프로세스의 PPID는 1이 되고, 고아 프로세스가 됨
  • 프로그램도 종료됨

고아 프로세스 방지

  • SIGHUP은 자식 프로세스들에게도 전달되므로, SIGHUP에 대한 동작을 정의한다.

좀비 프로세스

  • 자식 프로세스를 종료시키면, 자식 프로세스는 종료되었지만 부모 프로세스에서 자식 프로세스의 종료 상태를 회수하지 않아 자식 프로세스가 좀비 프로세스가 된다.
  • 부모 프로세스를 죽이면 좀비 프로세스가 사라진다.

좀비 프로세스 방지

#include <stdio.h>
#include <signal.h>

void foo(int signum) {
	printf("I am ok\n");
}

void wait_parent() {
	wait();
}

void main() {
	signal(17, wait_parent);
	if(fork()==0) for(;;);
    else {
    	if(fork()==0) for(;;);
    }
    for(;;);
}
  • wait_parent()를 SIGCHILD(17)에게 전달해주어 wait() 함수를 통해 부모 프로세스가 자식 프로세스의 종료 상태를 회수할 수 있도록 한다.

select()를 사용하여 Simple FTP 구현

serv.c

#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFFER_SIZE 100
#define LISTEN_BACKLOG 5
#define MAX_FD_COUNT 50
#define MAX_FILENAME_LENGTH 100
#define MAX_CLIENT_COUNT 50
#define SERV_ADDR "165.246.38.152"
#define SERV_PORT 13579

#define STATE_UNDEFINED 0
#define STATE_INIT 1
#define STATE_ASK_FILENAME 2
#define STATE_READY 3

typedef struct client {
    char reqeusted_filename[MAX_FILENAME_LENGTH];
} CLIENT;

void handle_protocol(int fd, fd_set* set, int* states, CLIENT* clients);

int main(void) {
    printf("Hi, I am the FTP server.\n");

    struct sockaddr_in serv_addr, cli_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    if (inet_aton(SERV_ADDR, &serv_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid Address\n");
        exit(EXIT_FAILURE);
    }

    int sfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("Fail to create");
        exit(EXIT_FAILURE);
    }
    printf("Server socket(sfd=%d) is created.\n", sfd);

    if (bind(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        perror("Fail to bind");
        exit(EXIT_FAILURE);
    }

    if (listen(sfd, LISTEN_BACKLOG) == -1) {
        perror("Fail to listen");
        exit(EXIT_FAILURE);
    }

    socklen_t cli_addr_len = sizeof(cli_addr);

    fd_set read_fds, temp_fds;
    FD_ZERO(&read_fds);
    FD_SET(sfd, &read_fds);

    int states[MAX_FD_COUNT] = { STATE_UNDEFINED };
    CLIENT clients[MAX_CLIENT_COUNT];

    while (1) {
        temp_fds = read_fds;
        select(MAX_FD_COUNT, &temp_fds, NULL, NULL, NULL);
        for (int fd = 0; fd < MAX_FD_COUNT; fd += 1) {
            if (FD_ISSET(fd, &temp_fds)) {
                if (fd == sfd) {
                    int cfd = accept(sfd, (struct sockaddr*) &cli_addr, &cli_addr_len);
                    if (cfd == -1) {
                        perror("Fail to accept");
                        exit(EXIT_FAILURE);
                    }
                    states[cfd] = STATE_INIT;
                    printf("Client socket(cfd=%d) is accepted.\n", cfd);
                    FD_SET(cfd, &read_fds);
                } else {
                    handle_protocol(fd, &read_fds, states, clients);
                }
            }
        }
    }
    return 0;
}

void handle_protocol(int fd, fd_set* set, int* states, CLIENT* clients) {
    char buf[BUFFER_SIZE];
    int chunk = read(fd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("client(cfd=%d) => server: %s\n", fd, buf);

    int state = states[fd];
    if (state == STATE_INIT && strcmp(buf, "hello") == 0) {
        write(fd, "file name?", 10);
        printf("server => client(cfd=%d): file name?\n", fd);
        states[fd] += 1;
    } else if (state == STATE_ASK_FILENAME && strlen(buf) > 0) {
        strcpy(clients[fd].reqeusted_filename, buf);
        write(fd, "ready?", 6);
        printf("server => client(cfd=%d): ready?\n", fd);
        states[fd] += 1;
    } else if (state == STATE_READY && strcmp(buf, "yes") == 0) {
        int res_fd = open(clients[fd].reqeusted_filename, O_RDONLY);
        if (res_fd == -1) {
            char* response = "Fail to open the file.";
            write(fd, response, strlen(response));
            printf("server => client: %s\n", response);
            buf[0] = -1;
            write(fd, buf, 1);
            return;
        }

        printf("server => client(cfd=%d): ", fd);
        fflush(stdout);
        while ((chunk = read(res_fd, buf, BUFFER_SIZE))) {
            write(fd, buf, chunk);
            write(STDOUT_FILENO, buf, chunk);
        }
        buf[0] = -1;
        write(fd, buf, 1);
        printf("\n");

        close(res_fd);

        printf("Client socket(cfd=%d) is closed.\n", fd);
        close(fd);
        FD_CLR(fd, set);
    } else {
        write(fd, "Invalid input to protocol.", 26);
        fprintf(stderr, "Invalid input to protocol.\n");
        printf("Client socket(cfd=%d) is closed.\n", fd);
        close(fd);
        FD_CLR(fd, set);
    }
}

cli.c

#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFFER_SIZE 100
#define MAX_FILENAME_LENGTH 100
#define SERV_ADDR "165.246.38.152"
#define SERV_PORT 13579

int main(void) {
    printf("Hi, I am the FTP client.\n");

    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    if (inet_aton(SERV_ADDR, &serv_addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid Address\n");
        exit(EXIT_FAILURE);
    }

    int sfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("Fail to create");
        exit(EXIT_FAILURE);
    }
    printf("Socket(sfd=%d) is created.\n", sfd);

    if (connect(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        perror("Fail to connect");
        exit(EXIT_FAILURE);
    }

    printf("client => server: hello\n");
    write(sfd, "hello", 5);

    char buf[BUFFER_SIZE];
    int chunk = read(sfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("server => client: %s\n", buf);

    if (strcmp(buf, "file name?") != 0) {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(sfd);
        exit(EXIT_FAILURE);
    }

    char filename[MAX_FILENAME_LENGTH];
    printf("client => server: (Enter file name) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    buf[strlen(buf) - 1] = '\0';
    strcpy(filename, buf);
    write(sfd, buf, strlen(buf));

    chunk = read(sfd, buf, BUFFER_SIZE - 1);
    buf[chunk] = '\0';
    printf("server => client: %s\n", buf);

    if (strcmp(buf, "ready?") != 0) {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(sfd);
        exit(EXIT_FAILURE);
    }

    printf("client => server: (Enter yes) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(sfd, buf, strlen(buf) - 1);

    int res_fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 00777);
    if (res_fd == -1) {
        fprintf(stderr, "Fail to open the file.\n");
        exit(EXIT_FAILURE);
    }

    printf("server => client: ");
    fflush(stdout);
    while (1) {
        chunk = read(sfd, buf, BUFFER_SIZE);
        if (buf[chunk - 1] == -1) {
            write(res_fd, buf, chunk - 1);
            write(STDOUT_FILENO, buf, chunk - 1);
            break;
        } else {
            write(res_fd, buf, chunk);
            write(STDOUT_FILENO, buf, chunk);
        }
    }
    printf("\n");

    close(res_fd);

    close(sfd);
    return 0;
}
profile
[~2023.04] 블로그 이전했습니다 ㅎㅎ https://leeeeeyeon-dev.tistory.com/

0개의 댓글