컴공 일기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)
유익한 글을 읽었다면 작성자에게 XDK를 선물하세요.
-
대학 1학년인데 자퇴하고 27수능 참전하려고 합니다... 0 0
경기권이고 애교심이 정말 0.1도 없으니 학교 생활도 하나도 안 하게 되고 과제도...
-
피셋 언논 풀어봤는데 0 1
80점나옴 이거하러간다
-
예전에 24미적 물로켓론 밀던 블로그도 있었는데 4 0
실력은 좋아보이던데 대체 왜.......
-
기상 6 0
중식 먹으러 뛰어 갓!!
-
잘 때 목걸이 반지 다 끼고 자는 사람도 있던데 저는 진짜 도저히 못 끼고...
-
시간 너무 부족할까요 강민철 듣다가 너무 안 맞는 것 같애서 강기분 반절 하고...
-
늙어서 대학 오지 마라 7 0
진짜다
-
물2 개어려워 ㅅㅂ 3 1
너무 저능해서 안풀림 ㅅㅂ
-
3년차 수험생 예비 킬캠 후기 1 0
1회 : 96점 29번 틀 29번은 수열의 정의와 극한을 묻는 문제 2회 : 96점...
-
화작할까 2 0
저능해서그런가 국어수학과탐만 해도 이미 영어 언매를 못하겠던데.. 혹시 화작이 매체보다 쉽나요?
-
궁금증 2 0 0
2과탐에 영어만 9등급이고 나머지 만점이면 연대 공대 가능함?
-
단순 궁금증 0 0
내신 cc받고 수능 만점에 24수능 생2화2 표점이면 설대 공대 뚫음? 공대중 가장 높은 과 기준
-
문장을 잘 못 읽는 사람 0 0
26수능 국어커리어 로우 그게 나야
-
2026 서킷 합본이랑 2027 브릿지 전국 10회분 풀려고하는데 0 0
뭐 먼저할까요 3모 1컷이에요
-
킬캠 1회 개쳐말렸네 2 0
객관식 20분컷햇는데 20,22번 30분박고 28번 계산잘못한채로 개뻘짓해서 하.. 개같노
-
이번주 본가가는데 4 0
목요일날 가서 1일 새벽 쿠팡뛰고 약속 2일 주간 쿠팡뛰고 약속 3일 주간 쿠팡뛰고...
-
이게 무슨..
-
수학문제 하나에서 모든 수학문제의 공통점을 찾으려는데 1 0
어떻게 푼다는건 나와있는데 정확히 이런 모든 수학문제의 공통점이 뭔지 분석하려는데...
-
국어 이원준 0 0
제가 글 읽는 속도다 진짜 느리고 정보량 많으면 거의 날리는 느낌이어서 이원준...
-
과제가너무많아요 5 0
ㅠㅠㅠㅠㅠㅠㅠ
-
아 공부하기개싫가 5 0
말도안돼
-
정오와 현역 여중생의 기상 6 2
아나타니~
-
저는 내일 플렉스를 할 거예요 3 1
팔찌랑 목걸이 중에 고민중 뭐사지...
-
얼버기 9 3
늦잠잔거같지만 기분탓입니당
-
난작수때 확통을 말아먹었음 0 0
공통 3틀하고 확통에서 8점을 받았음 ㅅㅂ
-
너 참 0 0
ㄱㅇㅇ
-
고3 높2 한완수ㅜ 1 0
현역 3모 기준 공통 22 하나 틀렷는데 뭔가 완성된 느낌은 아니라서 한완수 사려고...
-
쉴 틈이 없네 3 1
학기중에 이제 과외 2탕+근로까지 주 노동 20시간 이상 하면서 수능공부 해야하고...
-
[속보] “삼전닉스로 직행”…연고대 반도체과 합격선 ‘역대급’ 폭등 1 0
연세대와 고려대 반도체 계약학과의 2026학년도 수시 내신 합격선이 학과 개설 이후...
-
귀여운것같음 바로 볼따꾸 뜯어먹고 쮸왑쮸왑 닉언 마려운데 참겠음.
-
확통은 뉴런말고 그냥 수분감하고 n제 풀면서 하려고 하는데 뉴런 듣는게 이득인가요?
-
아마도 출제자와 해설자가 다른게 아닌가 싶은데 해설지가 문제를 오해해서 이상하게...
-
노트북아 왜 먹통이니… 0 0
검은 화면에 커서만 나오는데 이거 어캄
-
고2 겨울방학 0 0
고2 겨울방학때 수학 공부를 어떻게 해야함? 겨울방학되기전에 개발점 대수,...
-
얼버기 2 1
-
한의대 사탐 2 0
화작미적정법사문하면 경한 많이 어려움?? 정법 표점이 너무 낮아서..
-
한명만 수업을 더 들으면 딱 좋을텐데 13 4
국어과외들을사람없나
-
아이폰 프맥 ㅅㅂ 1 0
새끼손가락이 아작날 거 같은 무게다 살려줘라..
-
늦버기 1 0
-
저번에 내신 이의제기 관련 글 올린 사람인데 이렇게 이의제기 ? 9 3
이의제기 제출서? 썼는데 이정도면 될까요? 아니면 좀 더 넣어야 햘거 ㅊㅊ좀.....
-
일반물리 9 2
대학 처음왔는데 일반물리 도저히 못하겟던데 정상인가; 메디컬 지망이었고 일반과...
-
답지해설 강의 해설과 비교했을 때 좀 구린가요?
-
삼반수 고민 1 1
현역때 중경외시 공대, 반수해서 연고대 자연과학계열 다니고 있는...
-
[속보]백악관 기자단 만찬서 총격음…피신 트럼프 “멋진 저녁” 1 0
도널드 트럼프 미국 대통령이 25일(현지 시간) 백악관 출입기자단 만찬 도중 총성이...
-
팀운이 너무없다 7 0
진짜 말도안돼
-
작년 국어 69잘 수망 팩폭 ㄷㄷ 12 2
출처: 꿈많은그곳 요약) 양치기빨 저능저능아들 전부 참교육당함, 9모 물로켓 아.
-
그 생각 드는 순간 이미 딴길로 샌거임
-
경북대 공대를 갔을 텐데.. 생각보다 문과 취업은 더 힘들고
-
오르비 안녕하세요 3 1
반갑습니다
-
봉사하러왔는데 6 2
그냥 거의 폰하면서 앉아있으면 되네


반가워요