티스토리 뷰

 지난 기말 과제로 작성했던 프로그램을 흔적을 남기기위해 남겨둡니다. 몇가지 제약과 문제점을 가지고 있기 때문에 완벽한 코드는 절대 아닙니다. 가장 기초적인 코드 작성을 해두었기 때문에 불필요한 부분이 많이 있을 것이라고 생각됩니다. 단순 참고용으로 활용하시고, 이상한 부분은 지적 부탁드립니다.


3가지로 분리하고, 서버와 클라이언트 정의, 서버 소스코드, 클라이언트 소스코드로 분리하여 올려둡니다.

  1. C언어 노트 프로그램 서버와 클라이언트 정의

      http://thdev.net/248

  2. C언어 노트 프로그램 서버

      http://thdev.net/249

  3. C언어 노트프로그램 클라이언트


작성한 프로그램은?

 기말 과제로 나온건 에버노트를 참고하여 프로그램을 작성하는 문제였습니다.

 에버노트를 간단히 정리하면 클라이언트 프로그램이 존재하며, 클라이언트는 동시에 여러개의 PC에서 사용이 가능합니다. 여러개의 PC가 실시간 동기화 처리되지는 않으며, 특정 시간을 정해두고, 해당 시간이 되면 서버와 클라이언트의 동기화가 진행됩니다. 그리고 가장 기본적으로 메모 작성, 수정, 태그 기능이 있고, 1개의 노트북에는 여러개의 노트가 올 수 있고, 한명의 사용자는 여러개의 노트북을 둘 수 있습니다. 태그를 이용하여 분리하기도 하지만 해당 프로그램에는 제외했습니다.

 그렇기 때문에 사용자가 직접 동기화를 요청하거나, 시간이 되어야 동기화가 처리됩니다. 제가 작성한 노트에는 푸쉬형태의 동기화는 처리되지 않고, 새로운 파일을 생성하고, 특정 메뉴를 호출 할 경우에 서버에 전송 및 서버 DB에 저장을 하게 됩니다. 그리고 1명의 여러개의 클라이언트를 가질 수 있고, 모두 다른 세션으로 존재합니다. 사용자는 노트북을 생성하고, 새로운 노트를 수정 삭제 추가 할 수 있습니다.


Client 소스코드

make file

//make
//실행 명령 ./client 서버주소 포트번호
all: client

client: startClient.c clientTCP.h common.h
	gcc -g -Wall startClient.c clientTCP.c -o client

clean:
	rm client

run_client: client
	./client

common.h - 구조체 선언부

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

//노트북 구조체
typedef struct {
	int notebooksID;
	int userID;
	char notebookTitle[32];
	int createTime;
	int updateTime;	
}notebooks;

//노트 구조체
typedef struct {
	int noteID;
	int userID;
	int notebooksID;
	char noteContent[1024];
}notes;

//클라이언트에 가지고 있는 데이터 구조체
typedef struct {
	notebooks notebook[100];
	notes note[100];
}clientData;

//서버와의 데이터 전송을 위한 구조체
//Data 수정, 전송용
typedef struct {
	int dataInputA;
	int dataInputB;
	char content[2048];
}dataSubmit;

//사용자 세션 정보 저장 구조체
typedef struct {
	int session;
	int userID;
	int type;
}userSession;

clientTCP.h - 클라이언트 TCP 생성 부분

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
분
#include "common.h"

#define MAXLINE 2048 //buf size

//cleint 요청 값
#define CLIID 0 //ID
#define CLIWRITE 1 //입력
#define CLIEDIT 2 //수정
#define CLIDELETE 3 //삭제
#define CLIBOOKSWRITE 4 //books 생성
#define CLIBOOKSDELETE 5 //books 수정
#define CLIEXIT 99 //exit

userSession session;
clientData cData;

void noteClient(char *argv[]);
void createNotebooks(char *str, time_t timer, int user);
void insertNote(char *str, int user);
void printfNotebooks();
void printfNote();
void getMenu();

clientTCP.c

 앞서 서버에서는 send를 사용하여 구조체 데이터를 전송하였습니다. send로 전송하게 되면 client에서는 당연히 recv 함수를 사용하여 데이터를 전송 받아야 합니다.

recv 함수 원형 (참고 사이트 주소 : http://bit.thdev.net/N1RkBg

  int recv(int s, void *buf, size_t len, int flags);

  int s : 소켓 디스크립터

 void * buf : 전송 받을 데이터 void * 형태

 size_t len : 전송 받을 데이터의 byte 크기

 int flags : MSG_WAITALL 모든 데이터가 수신될 때까지 대기

                   MSG_DONTWAIT : 전송 준비전 대기상태가 필요할 경우 -1을 반환하고 복귀

                   MSG_NOSIGNAL : recv와의 연결이 끊어지면 시그널을 받지 않도록 처리


 recv 함수를 사용하면 마지막에 flags 값을 넣을 수 있습니다. 이를 통해 데이터가 완전히 받아 지기 전까지 대기 상태를 만들 수 있는 MSG_WAITALL을 사용하였습니다.

#include "clientTCP.h"

char buf[MAXLINE];

void noteClient(char *argv[]) {
	struct sockaddr_in servaddr;
	socklen_t strlen = sizeof(servaddr);
	int sockfd;
	int sessionConnect;
	int Exit = 0;

	if((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
		perror("socket fail");
		exit(0);
	}

	memset(&servaddr, 0, strlen);
	servaddr.sin_family = AF_INET;
	inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
	servaddr.sin_port = htons(atoi(argv[2]));

	if(connect(sockfd, (struct sockaddr *)&servaddr, strlen) < 0) {
		perror("connect fail");
		exit(0);
	}

	printf("ID 입력 : ");
	fgets(buf, sizeof(buf), stdin);
	write(sockfd, &buf, sizeof(buf));
	read(sockfd, &session, sizeof(session));
	write(sockfd, "ok", 3);
	
	printf("Session No : %d, %d\n", session.session, session.userID);

	printf("%lu\n", sizeof(cData));
	recv(sockfd, &cData, sizeof(cData), MSG_WAITALL);
	printfNotebooks();
	printfNote();
	
	getMenu();

	while(1) {
		buf[0] = '\0';
		puts("메뉴 보기는 100");
		printf("메뉴 선택 : ");
		scanf("%d", &session.type);

		write(sockfd, &session, sizeof(session));
		read(sockfd, &sessionConnect, sizeof(sessionConnect));
		printf("%d\n", sessionConnect);
		if(sessionConnect == -1) {
			printf("Error : Connect Session Error....\n");
			close(sockfd);
			break;
		}

		dataSubmit dSubmit;
		switch(session.type) {
		case CLIWRITE:
			puts("메모 입력");
			printfNotebooks();
			printf("노트 선택 : ");
			scanf("%d", &dSubmit.dataInputA);
			printf("메모 작성 : ");
			scanf("%s", dSubmit.content);
			write(sockfd, &dSubmit, sizeof(dSubmit));
			break;

		case CLIEDIT:
			puts("메모 수정");
			printfNotebooks();
			printf("노트 선택 : ");
			scanf("%d", &dSubmit.dataInputA);
			printfNote();
			printf("메모 선택 : ");
			scanf("%d", &dSubmit.dataInputB);
			printf("메모 수정 : ");
			scanf("%s", dSubmit.content);

			write(sockfd, &dSubmit, sizeof(dSubmit));
			break;

		case CLIDELETE:
			puts("메모 삭제");
			printfNotebooks();
			printf("노트 선택 : ");
			scanf("%d", &dSubmit.dataInputA);
			printfNote();
			printf("메모 선택 : ");
			scanf("%d", &dSubmit.dataInputB);

			write(sockfd, &dSubmit, sizeof(dSubmit));
			break;

		case CLIBOOKSWRITE:
			puts("노트북 추가");
			printf("노트 명 : ");
			scanf("%s", dSubmit.content);

			write(sockfd, &dSubmit, sizeof(dSubmit));
			break;

		case CLIBOOKSDELETE:
			puts("노트북 삭제 - 노트북 삭제시 노트북에 해당하는 데이터 모두 삭제됩니다.");
			printfNotebooks();
			printf("삭제 노트 선택 : ");
			scanf("%d", &dSubmit.dataInputA);
			write(sockfd, &dSubmit, sizeof(dSubmit));
			break;

		case CLIEXIT:
			read(sockfd, &buf, sizeof(buf));
			printf("%s\n", buf);
			close(sockfd);
			Exit = 1;
			break;

		case 100:
			getMenu();
			break;
		}
		if(Exit)
			break;

                //서버로 부터 구조체를 받아 옴
		recv(sockfd, &cData, sizeof(cData), MSG_WAITALL);
		printfNotebooks();
		printfNote();
	}
}

void printfNotebooks() { //노트북 정보를 보여주기 위한 함수
	int i;
	puts("\n-----Notebooks-----");
	for(i=0;i<100;i++) {
		if(cData.notebook[i].userID == session.userID) {
			printf("NoteBook No. %d, Note Title : %s\n", cData.notebook[i].notebooksID, cData.notebook[i].notebookTitle);
		}
	}
	puts("--------------");
}

void printfNote() { //노트북 정보를 보여주기 위한 함수
	int i;
	puts("\n------Note------");
	for(i=0;i<100;i++) {
		if(cData.note[i].userID == session.userID) {
			printf("Note No. %d, NoteBook No. %d\n", cData.note[i].noteID, cData.note[i].notebooksID);
			printf("Content : %s\n", cData.note[i].noteContent);
		}
	}
	puts("-----------------");
}

void getMenu() { //클라이언트 노트의 메뉴
	puts("원하는 번호를 입력 하세요.");
	puts("1. Note 작성");
	puts("2. Note 수정");
	puts("3. Note 삭제");
	puts("4. NoteBook 추가");
	puts("5. NoteBook 삭제");
	puts("98. 전체 내용 보기");
	puts("99. Exit");
}

startClient.c - client를 실행하기 위한 부분

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

int main(int argc, char *argv[]) {
	noteClient(argv);
	return 0;
}

마무리

 이상으로 C언어로 작성한 노트 프로그램의 흔적 남기기를 마칩니다. 허접한 소스코드 봐주셔서 감사합니다.

 Client는 파일 입출력 부분을 사용하지 않았기에 저장을 해두지는 않고 항상 서버에서 모든 데이터를 가지고 있는 문제가 있습니다.





댓글