✨컴공주✨ [1052682] · MS 2021 (수정됨) · 쪽지

2025-05-31 00:19:04
조회수 117

컴공 일기278

게시글 주소: https://orbi.kr/00073281944

가산점을 주는 네트워크 과제입니다.


4장 이내 11pt 설계보고서, 소스코드 원본을 제출해야 하며

연구실에서 1:1 인터뷰를 통해 데모 실행 및 설명을 해야 합니다.


TCP/IP 통신기반의 공유 문서 작성 및 읽기 프로그램입니다.

소켓 프로그래밍과 시스템 프로그래밍에서 자주 사용되는 기법을 적절히 조화시켜야 하는데 가장 핵심적인 기능 중 하나는, write 명령이 클라이언트로부터 왔을 때, 서버는 한줄씩 데이터를 받아들이는 겁니다. 이걸 구현하는 것이 이 과제의 핵심 중 하나가 아닌가 생각하네요.


소켓의 본질을 알고 있어야, 이 기능을 구현할 수 있거든요.

소켓의 본질은 파일입니다. 파일은 데이터 단위가 Stream인데,

이 스트림은 시작은 확실히 정해져 있지만, 끝이 어딘지 확실하지 않다는 특징을 갖고 있습니다. 그렇기 때문에 ‘줄 입력‘이 여기서 종료되었다는 판단을 아무런 정보가 없다면 서버는 할 수 없죠.


따라서 적절한 시그널을 주고받는 프로토콜 절차가 있어야 합니다.


인터뷰에서 시그널을 주고받음으로써 줄의 끝이 어디까지인지 서버가 알도록 한다는 말씀을 드렸을 때, 인터뷰 평가 사항에 무엇인가를 막 적고 계시더라구요. 그때 조금 확실히 알게된 것 같습니다.


과제의 의도가 결국 ‘소켓’이 무엇인지 정확히 알고 있느냐라는 걸요.. 사실 이 얘기는 네트워크 이론과 운영체제론을 알고 있어야 이해할 수 있을 겁니다. 조금 더 다듬어서 비동기 입출력까지 지원하는 서버를 한번 만들어 보려구요. 설계 구조를 완전히 바꿔야 겠지만, 오랜만에 아주 재미있는 프로젝트를 하게 되어서 이 내용물은 깃허브에 올려 볼 것 같습니다. 



#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <pthread.h>

#include <netinet/in.h>

#include <arpa/inet.h>


#define MAX_DOCS 100

#define MAX_SECTIONS 10

#define MAX_TITLE 64

#define MAX_LINE 256

#define MAX_LINES 10

#define BUF_SIZE 1024


typedef struct {

    char title[MAX_TITLE];

    char section_titles[MAX_SECTIONS][MAX_TITLE];

    char section_contents[MAX_SECTIONS][MAX_LINES][MAX_LINE];

    int section_line_count[MAX_SECTIONS];

    int section_count;

} Document;


typedef struct WriteRequest {

    int client_sock;

    int estimated_lines;

    struct WriteRequest *next;

} WriteRequest;


Document docs[MAX_DOCS];

int doc_count = 0;


pthread_mutex_t docs_mutex = PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_t section_mutex[MAX_DOCS][MAX_SECTIONS];

pthread_cond_t section_cond[MAX_DOCS][MAX_SECTIONS];

int section_writing[MAX_DOCS][MAX_SECTIONS] = {{0}};


WriteRequest *section_queue[MAX_DOCS][MAX_SECTIONS] = {{{0}}};

pthread_mutex_t section_queue_mutex[MAX_DOCS][MAX_SECTIONS];

pthread_cond_t section_queue_cond[MAX_DOCS][MAX_SECTIONS];


void send_all(int sock, const char *msg) {

    send(sock, msg, strlen(msg), 0);

}


Document* find_doc(const char* title) {

    for (int i = 0; i < doc_count; ++i) {

        if (strcmp(docs[i].title, title) == 0)

            return &docs[i];

    }

    return NULL;

}


ssize_t read_line(int sock, char *buf, size_t max_len) {

    size_t i = 0;

    char ch;

    while (i < max_len - 1) {

        ssize_t n = recv(sock, &ch, 1, 0);

        if (n <= 0) break;

        if (ch == '\n') break;

        buf[i++] = ch;

    }

    buf[i] = '\0';

    return i;

}


void parse_command(const char* input, char* args[], int* argc) {

    *argc = 0;

    const char* p = input;

    while (*p) {

        while (*p == ' ' || *p == '\t') p++;

        if (*p == '"') {

            p++;

            const char* start = p;

            while (*p && *p != '"') p++;

            int len = p - start;

            args[*argc] = malloc(len + 1);

            strncpy(args[*argc], start, len);

            args[*argc][len] = '\0';

            (*argc)++;

            if (*p == '"') p++;

        } else {

            const char* start = p;

            while (*p && *p != ' ' && *p != '\t' && *p != '\n') p++;

            int len = p - start;

            args[*argc] = malloc(len + 1);

            strncpy(args[*argc], start, len);

            args[*argc][len] = '\0';

            (*argc)++;

        }

    }

}


void* client_handler(void* arg);


int main(int argc, char* argv[]) {

    if (argc != 3) {

        fprintf(stderr, "Usage: %s <IP> <Port>\n", argv[0]);

        exit(1);

    }


    for (int i = 0; i < MAX_DOCS; ++i)

        for (int j = 0; j < MAX_SECTIONS; ++j) {

            pthread_mutex_init(§ion_mutex[i][j], NULL);

            pthread_cond_init(§ion_cond[i][j], NULL);

            pthread_mutex_init(§ion_queue_mutex[i][j], NULL);

            pthread_cond_init(§ion_queue_cond[i][j], NULL);

        }


    int server_sock = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in server_addr, client_addr;

    socklen_t addrlen = sizeof(client_addr);


    server_addr.sin_family = AF_INET;

    server_addr.sin_port = htons(atoi(argv[2]));

    inet_pton(AF_INET, argv[1], &server_addr.sin_addr);


    bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr));

    listen(server_sock, 10);


    printf("[Server] Listening on %s:%s\n", argv[1], argv[2]);


    while (1) {

        int *client_sock = malloc(sizeof(int));

        *client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &addrlen);


        pthread_t tid;

        pthread_create(&tid, NULL, client_handler, client_sock);

        pthread_detach(tid);

    }


    close(server_sock);

    return 0;

}


void* client_handler(void* arg) {

    int client_sock = *(int*)arg;

    free(arg);

    char buf[BUF_SIZE];

    char* args[64];

    int argc;


    while (1) {

        memset(buf, 0, sizeof(buf));

        if (read_line(client_sock, buf, sizeof(buf)) <= 0) break;


        parse_command(buf, args, &argc);

        if (argc == 0) continue;


        if (strcmp(args[0], "create") == 0) {

            pthread_mutex_lock(&docs_mutex);

            if (argc < 3 || doc_count >= MAX_DOCS) {

                pthread_mutex_unlock(&docs_mutex);

                send_all(client_sock, "[Error] Invalid create command.\n");

                continue;

            }

            if (find_doc(args[1])) {

                pthread_mutex_unlock(&docs_mutex);

                send_all(client_sock, "[Error] Document already exists.\n");

                continue;

            }

            int section_count = atoi(args[2]);

            if (section_count <= 0 || section_count > MAX_SECTIONS || argc != 3 + section_count) {

                pthread_mutex_unlock(&docs_mutex);

                send_all(client_sock, "[Error] Invalid section count or titles.\n");

                continue;

            }

            strcpy(docs[doc_count].title, args[1]);

            docs[doc_count].section_count = section_count;

            for (int i = 0; i < section_count; ++i) {

                strcpy(docs[doc_count].section_titles[i], args[3 + i]);

                docs[doc_count].section_line_count[i] = 0;

            }

            ++doc_count;

            pthread_mutex_unlock(&docs_mutex);

            send_all(client_sock, "[OK] Document created.\n");

        }


        else if (strcmp(args[0], "write") == 0) {

            if (argc < 3) {

                send_all(client_sock, "[Error] Invalid write command.\n");

                continue;

            }

            pthread_mutex_lock(&docs_mutex);

            Document* doc = find_doc(args[1]);

            if (!doc) {

                pthread_mutex_unlock(&docs_mutex);

                send_all(client_sock, "[Error] Document not found.\n");

                continue;

            }

            int section_idx = -1;

            for (int i = 0; i < doc->section_count; ++i)

                if (strcmp(doc->section_titles[i], args[2]) == 0) {

                    section_idx = i;

                    break;

                }

            if (section_idx == -1) {

                pthread_mutex_unlock(&docs_mutex);

                send_all(client_sock, "[Error] Section not found.\n");

                continue;

            }

            int doc_idx = doc - docs;

            pthread_mutex_unlock(&docs_mutex);


            send_all(client_sock, "[OK] You can start writing. Send <END> to finish.\n>> ");


            int line_count = 0;

            char line[MAX_LINE];

            char temp_lines[MAX_LINES][MAX_LINE];

            while (1) {

                if (read_line(client_sock, line, sizeof(line)) <= 0) break;

                if (strcmp(line, "<END>") == 0) break;

                if (line_count < MAX_LINES)

                    strncpy(temp_lines[line_count++], line, MAX_LINE - 1);

                send_all(client_sock, ">> ");

            }


            WriteRequest *req = malloc(sizeof(WriteRequest));

            req->client_sock = client_sock;

            req->estimated_lines = line_count;

            req->next = NULL;


            pthread_mutex_lock(§ion_queue_mutex[doc_idx][section_idx]);

            if (!section_queue[doc_idx][section_idx] || line_count < section_queue[doc_idx][section_idx]->estimated_lines) {

                req->next = section_queue[doc_idx][section_idx];

                section_queue[doc_idx][section_idx] = req;

            } else {

                WriteRequest *cur = section_queue[doc_idx][section_idx];

                while (cur->next && cur->next->estimated_lines <= line_count)

                    cur = cur->next;

                req->next = cur->next;

                cur->next = req;

            }

            pthread_cond_signal(§ion_queue_cond[doc_idx][section_idx]);

            pthread_mutex_unlock(§ion_queue_mutex[doc_idx][section_idx]);


            pthread_mutex_lock(§ion_mutex[doc_idx][section_idx]);

            while (section_queue[doc_idx][section_idx]->client_sock != client_sock)

                pthread_cond_wait(§ion_queue_cond[doc_idx][section_idx], §ion_mutex[doc_idx][section_idx]);


            pthread_mutex_lock(&docs_mutex);

            doc->section_line_count[section_idx] = 0;

            for (int i = 0; i < line_count && i < MAX_LINES; ++i)

                strncpy(doc->section_contents[section_idx][i], temp_lines[i], MAX_LINE - 1);

            doc->section_line_count[section_idx] = line_count;

            pthread_mutex_unlock(&docs_mutex);


            section_queue[doc_idx][section_idx] = section_queue[doc_idx][section_idx]->next;

            pthread_cond_broadcast(§ion_queue_cond[doc_idx][section_idx]);

            pthread_mutex_unlock(§ion_mutex[doc_idx][section_idx]);


            send_all(client_sock, "[Write_Completed]\n");

        }


        else if (strcmp(args[0], "read") == 0) {

            pthread_mutex_lock(&docs_mutex);

            if (argc == 1) {

                for (int i = 0; i < doc_count; ++i) {

                    char line[BUF_SIZE];

                    snprintf(line, sizeof(line), "%s\n", docs[i].title);

                    send_all(client_sock, line);

                    for (int j = 0; j < docs[i].section_count; ++j) {

                        snprintf(line, sizeof(line), "    %d. %s\n", j + 1, docs[i].section_titles[j]);

                        send_all(client_sock, line);

                    }

                }

            } else if (argc >= 3) {

                Document* doc = find_doc(args[1]);

                if (!doc) {

                    pthread_mutex_unlock(&docs_mutex);

                    send_all(client_sock, "[Error] Document not found.\n");

                    continue;

                }

                int found = 0;

                for (int i = 0; i < doc->section_count; ++i)

                    if (strcmp(doc->section_titles[i], args[2]) == 0) {

                        found = 1;

                        char header[BUF_SIZE];

                        snprintf(header, sizeof(header), "%s\n    %d. %s\n", doc->title, i + 1, doc->section_titles[i]);

                        send_all(client_sock, header);

                        for (int j = 0; j < doc->section_line_count[i]; ++j) {

                            char line[BUF_SIZE];

                            snprintf(line, sizeof(line), "       %s\n", doc->section_contents[i][j]);

                            send_all(client_sock, line);

                        }

                        break;

                    }

                if (!found)

                    send_all(client_sock, "[Error] Section not found.\n");

            }

            send_all(client_sock, "__END__\n");

            pthread_mutex_unlock(&docs_mutex);

        }


        else if (strcmp(args[0], "bye") == 0) {

            send_all(client_sock, "[Disconnected]\n");

            break;

        } else {

            send_all(client_sock, "[Error] Unknown command.\n");

        }


        for (int i = 0; i < argc; ++i) free(args[i]);

    }

    close(client_sock);

    return NULL;

}


0 XDK (+0)

  1. 유익한 글을 읽었다면 작성자에게 XDK를 선물하세요.