MP3 Player in C.hwp


앞으로의 목표는 가사 파일을 파일로 불러와 읽어서 처리할것이다.

그리고 멈추는 기능도 만들수 있을것 같다.


#include 
#include 
#include 
#include 
#include  
#include 
#include  
#include 


#pragma warning(disable:4996)
#pragma comment(lib, "winmm.lib")
typedef struct _finddata_t  FILE_SEARCH;
void ls();
void cd(char *command);
void all(char* path);
void cls();
char *replaceAll(char *s, const char *olds, const char *news);
void mp3_player(); 
void command_chaeck();
void lyrics();
char temp[400][512];
char strBuffer[_MAX_PATH] = { 0 };
char *pstrBuffer = NULL;
int wav_file_cnt = 0;
int now_file_num = 0;
int play_time[] = {13,16,19,23};
char play_lyrics[500][100] = {"[나연] 모두 나를 가지고 매일 가만 안 두죠","[모모] 내가 너무 예쁘죠 나 때문에 다 힘들죠","[나연] 어딜 걷고 있어도 빨간 바닥인거죠","[사나] Red carpet 같은 기분 모두 날 쳐다 보죠 Oh"};
TCHAR file_name[512] = { 0, };
TCHAR file_set[512] = { 0, };

void main() {

	char command[100];
	const char exit[] = "exit ";
	const char cols[] = "ls ";
	const char cocd[] = "cd ";
	const char coall[] = "all";
	const char cocls[] = "cls";
	const char mp3[] = "mp3";
	char *path=NULL;

	while (1) {
		pstrBuffer = getcwd(strBuffer, _MAX_PATH);
		printf("all입력후 원하시는 절대경로를 입력해주시면 됩니다. (all C:\\) \n\n");
		printf("%s :", pstrBuffer);
		fgets(command, sizeof(command), stdin);
		if (strncmp(command, exit, 4) == 0) break;
		else if (strncmp(command, cols, 2) == 0) ls();
		else if (strncmp(command, cocd, 2) == 0) {
			if (strncmp(command, cocd, 3) == 0)	cd(command);
			else printf("cd : cd is not command!!\n");
		}
		else if (strncmp(command, coall, 3) == 0) {
	
				path = strtok(command, " ");
				while (path != NULL) {
					path = strtok(NULL, "");
					if (path != NULL) break;
				}
				path[strlen(path) - 1] = '\0';
		
			all(path);
			printf("Wav File List!!!!\n\n\n");
			for (int i = 0; i < wav_file_cnt; i++) {
				puts(temp[i]);
			}
		}
		else if (strncmp(command, cocls, 3) == 0) cls();
		else if (strncmp(command, mp3, 3) == 0) {
			if (wav_file_cnt == 0) {
				printf("all을 실행해주세요\n");
			}
			else {
				mp3_player();
			}
		}
		
	}


}
void ls() {
	long h_file;
	FILE_SEARCH file_search;
	char search_Path[100] = ".\\*.*";
	char txt[] = "";
	char *test = NULL;
	int i = 0;
	if ((h_file = _findfirst(search_Path, &file_search)) == -1L) {
		printf("No files in current directory!\n");
		exit(1);
	}
	do {
		if (strstr(file_search.name, txt)) {
			//strcpy(test, file_search.name);
			//i = isFileOrDir(file_search.name);
			if (file_search.attrib == 16) {
				SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 4);
				puts(file_search.name);
				SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
			}
			else {
				puts(file_search.name);
			}
			
		}

	} while (_findnext(h_file, &file_search) == 0);
}
void cd(char *command) {
	char cut[] = " ";
	char end[] = "";
	char new_command[100];
	char cd_command[100];
	char *command_cut = NULL;

	strcpy(new_command, command);
	new_command[strlen(new_command) - 1] = '\0';
	command_cut = strtok(command, cut);

	while (command_cut != NULL) {
		command_cut = strtok(NULL, end);
		if (command_cut != NULL) break;
	}

	pstrBuffer = getcwd(strBuffer, _MAX_PATH);
	strcpy(cd_command, pstrBuffer);
	strcat(cd_command, "\\");
	strcat(cd_command, command_cut);

	command_cut[strlen(command_cut) - 1] = '\0';


	char *ch = replaceAll(cd_command, "\\", "\\\\");
	strcpy(cd_command, ch);



	int error_check = chdir((char*)cd_command);
	if (error_check == -1) {
		printf("%s : %s is not a folder or .\n", new_command, command_cut);
		return;
	}
}
void all(char *path) {
	long h_file;
	FILE_SEARCH file_search;

	
	char search_Path[512];
	strcpy(search_Path, path);

	char search_command[512];
	strcpy(search_command, search_Path);
	strcat(search_command, "*.*");
	
	char temp_[512];
	char old_Path[512];
	strcpy(old_Path, search_Path);

	char txt[] = "";
	char *test = NULL;
	int i = 0;

 	if ((h_file = _findfirst(search_command, &file_search)) == -1L) {
		//printf("No files in current directory!\n");
		return;
	}
	do {

		if (strstr(file_search.name, txt)) {
			if (strstr(file_search.name, ".wav")) {
				strcpy(temp_, search_Path);
				strcat(temp_, file_search.name);
				strcpy(temp[wav_file_cnt],temp_);
				wav_file_cnt++;
			}
			if ((strcmp(file_search.name, ".") == 0) || (strcmp(file_search.name, "..") == 0)) {
				continue;
			}
			else if (file_search.attrib == 16 || file_search.attrib == 17) {

				SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 4);
				puts(file_search.name);
				SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);

				int error_check = chdir(search_Path);
				if (error_check == -1) {
					printf("error\n");
					return;
				}

				strcat(search_Path, file_search.name);
				strcat(search_Path, "\\");
				all(search_Path);
 				strcpy(search_Path, old_Path);
			}
			else {
				puts(file_search.name);
			}
			
		}

	} while (_findnext(h_file, &file_search) == 0);
	int error_check = chdir(pstrBuffer);
	if (error_check == -1) {
		printf("error\n");
		return;
	}
	
}
void cls() {
	system("cls");
}
char *replaceAll(char *s, const char *olds, const char *news) {
	char *result;
	char *sr;
	size_t i, count = 0;
	size_t oldlen = strlen(olds); if (oldlen < 1) return s;
	size_t newlen = strlen(news);

	if (newlen != oldlen) {
		for (i = 0; s[i] != '\0';) {
			if (memcmp(&s[i], olds, oldlen) == 0) count++, i += oldlen;
			else i++;
		}
	}
	else i = strlen(s);


	result = (char *)malloc(i + 1 + count * (newlen - oldlen));
	if (result == NULL) return NULL;


	sr = result;
	while (*s) {
		if (memcmp(s, olds, oldlen) == 0) {
			memcpy(sr, news, newlen);
			sr += newlen;
			s += oldlen;
		}
		else *sr++ = *s++;
	}
	*sr = '\0';
	result[strlen(result) - 1] = '\0';
	return result;
}
void command_chaeck() {
	int key_command;
	while (!kbhit());
	key_command = getch();
	do {
		key_command = getch();
	} while (key_command == 244);
	switch (key_command)
	{
	case 77: {
		now_file_num++;
		if (now_file_num == wav_file_cnt) now_file_num = wav_file_cnt - 1;
		break;
	}
	case 75: {
		if (now_file_num == 0) break;
		now_file_num--;
		break;
	}
	default:
		break;
	}


}
void mp3_player() {
	int max_size = 0; 
	while (1) {
 		MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, temp[now_file_num], strlen(temp[now_file_num]), file_name, 512);
		if (strlen(file_name) > max_size) {
			max_size = strlen(file_name);
		}
		file_name[(max_size-(max_size - strlen(temp[now_file_num])))] = '\0';
		printf("<- 이전노래, -> 다음노래 \n");
		printf("현재 실행되는 노래의 이름: %s\n", temp[now_file_num]);
		PlaySound((LPCWSTR)file_name, NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);
		_beginthread(lyrics, 0, 0);
		command_chaeck();
		system("cls");
	}
}
void lyrics() {
	int time = 0;
	int now = 0;
	while (1) {
		Sleep(1000);
		time++;
		if (play_time[now] < time) {
			printf("%s\n", play_lyrics[now]);
			now++;
		}

	}
}


T E T R I S.hwp

Source.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "head.h"
#pragma warning (disable:4996)


int STATUS_Y_GOAL; //GOAL 정보표시위치Y 좌표 저장 
int STATUS_Y_LEVEL; //LEVEL 정보표시위치Y 좌표 저장
int STATUS_Y_SCORE; //SCORE 정보표시위치Y 좌표 저장
typedef enum { NOCURSOR, SOLIDCURSOR, NORMALCURSOR } CURSOR_TYPE; //커서숨기는 함수에 사용되는 열거형 

int new_block_on;
int main_cpy[MAP_Y][MAP_X];
int main_org[MAP_Y][MAP_X];
int b_x = (MAP_X / 2) - 1; //블럭의 x좌표
int b_y = 0; //블럭의 y좌표
int temp[4][4];
int key = 0;
int crash_num = 0;
int game_speed = 1000;
int level=0; 
int re_line = 0;
int old_score=0;
int now_score=0;
int best_score=0;
int map_check = 0;
int block_high = 0;
int hard_down = 0;

void check_crash(int x, int y);
void key_ent();
void move_block(int key);
void map_reset();
void gotoxy(int x, int y);
void map();
char start();
void new_block();
void block_turn(int block[4][4]);
void setcursortype(CURSOR_TYPE c);
void auto_down_blcok(void *p);
void line_check();
void game_over();
void score_map();
void drop_block(void);

void main() {
	char choice;
	setcursortype(NOCURSOR);
	choice = start();
	if (choice == '1') {
		map_reset();
		_beginthread(auto_down_blcok, 0, 0);
		score_map();
		while (1) {
			key_ent();
		}
	}
	else {
		exit(1);
	}
	return;
}

//자동으로 내려가는 부분
void auto_down_blcok(void *p) {
	Sleep(1000);
	while (1) {
		if (map_check != 1 && hard_down == 0) {
			check_crash(b_x, b_y + 1);
			if (crash_num == 0) {
				move_block(DOWN);
				map();
			}
			else {
				for (int i = 0; i < 4; i++)
					for (int j = 0; j < 4; j++)
						if (main_org[b_y + i][b_x +j] == ACTIVE_BLOCK) main_org[b_y + i][b_x+j] = INACTIVE_BLOCK;
				new_block();
			}
			Sleep(game_speed);
		}
		game_over();
	}
}

//커서 없애는 함수
void setcursortype(CURSOR_TYPE c) { //커서숨기는 함수 
	CONSOLE_CURSOR_INFO CurInfo;

	switch (c) {
	case NOCURSOR:
		CurInfo.dwSize = 1;
		CurInfo.bVisible = FALSE;
		break;
	case SOLIDCURSOR:
		CurInfo.dwSize = 100;
		CurInfo.bVisible = TRUE;
		break;
	case NORMALCURSOR:
		CurInfo.dwSize = 20;
		CurInfo.bVisible = TRUE;
		break;
	}
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &CurInfo);
}

//키 입력받는 함수
void key_ent() {
	key = 0;
	if (kbhit()) {

		key = getch();

		if (key == 224) {

			do {
				key = getch();
			} while (key == 244);

			switch (key)
			{
			case DOWN: {
				check_crash(b_x, b_y + 1);
				if (crash_num == 0) move_block(DOWN);
				map();
				break;
			}
			case RIGHT: {
				check_crash(b_x + 1, b_y);
				if (crash_num == 0) move_block(RIGHT);
				map();
				break;
			}
			case LEFT: {
				check_crash(b_x - 1, b_y);
				if (crash_num == 0) move_block(LEFT);
				map();
				break;
			}
			case UP: {
				block_turn(temp);
				map();
				break;
			}
			default:
				break;
			}
		}
		else {
			switch (key)
			{
			case SPACE: {
				move_block(SPACE);
				map();
				break;
			}
			default:
				break;
			}

		}
		fflush(stdin);
	}
}

//초기화면
char start() {
	printf("                                                                           \n\n\n"); Sleep(50);
	printf("                                                ■■■                         \n"); Sleep(50);
	printf("       ■■■■■                ■■■■■    ■    ■             ■■       \n"); Sleep(50);
	printf("           ■      ■■■■          ■        ■■■      ■     ■    ■     \n"); Sleep(50);
	printf("           ■      ■                ■        ■■        ■    ■            \n"); Sleep(50);
	printf("           ■      ■■■■          ■        ■  ■      ■     ■           \n"); Sleep(50);
	printf("           ■      ■                ■        ■    ■    ■       ■■       \n"); Sleep(50);
	printf("                   ■■■■          ■        ■      ■  ■           ■     \n"); Sleep(50);
	printf("                                                           ■            ■    \n"); Sleep(50);
	printf("                                                                  ■    ■     \n"); Sleep(50);
	printf("                                                                    ■■       \n"); Sleep(50);
	printf("                             1. START                                          \n"); Sleep(50);
	printf("                             2. EXIT                                           \n"); Sleep(50);
	printf("                                                                               \n");
	char num = getch();
	system("cls");
	return num;
}

//점수출력
void score_map() {
	int y = 3;
	gotoxy(STATUS_X_ADJ, STATUS_Y_LEVEL = y); printf(" LEVEL : %5d", level);
	gotoxy(STATUS_X_ADJ, STATUS_Y_GOAL = y + 1); printf(" GOAL  : %5d", 10 - re_line);
	gotoxy(STATUS_X_ADJ, y + 2); printf("+-  N E X T  -+ ");
	gotoxy(STATUS_X_ADJ, y + 3); printf("|             | ");
	gotoxy(STATUS_X_ADJ, y + 4); printf("|             | "); //수정
	gotoxy(STATUS_X_ADJ, y + 5); printf("|             | ");
	gotoxy(STATUS_X_ADJ, y + 6); printf("|             | ");
	gotoxy(STATUS_X_ADJ, y + 7); printf("+-- -  -  - --+ ");
	gotoxy(STATUS_X_ADJ, y + 8); printf(" YOUR SCORE :");
	gotoxy(STATUS_X_ADJ, STATUS_Y_SCORE = y + 9); printf("        %6d", now_score);
	gotoxy(STATUS_X_ADJ, y + 10); printf(" LAST SCORE :");
	gotoxy(STATUS_X_ADJ, y + 11); printf("        %6d", old_score);
	gotoxy(STATUS_X_ADJ, y + 12); printf(" BEST SCORE :");
	gotoxy(STATUS_X_ADJ, y + 13); printf("        %6d", best_score);
	gotoxy(STATUS_X_ADJ, y + 15); printf("http://jmy0904.tistory.com");
}

//맵을 그려주는 함수
void map() {
	map_check = 1;
	for (int j = 1; j0) { 
				temp_++;
			}
		}
	}
	if (temp_ != 0) crash_num = 1;
	else { crash_num = 0; }
	line_check();
}

//블럭 이동함수
void move_block(int key) {
	
	switch (key)
	{
	case DOWN: { //아래
 		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 4; j++) {
				if (temp[i][j] == -2) {
					main_org[b_y + i][b_x + j] = EMPTY;
				}
			}
		}
		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 4; j++) {
				if (temp[i][j] == -2)
					main_org[b_y + i + 1][b_x + j] = ACTIVE_BLOCK; //함수로 줄이기
			}
		}
		b_y++;
		break;
	}
	case RIGHT: { //오른쪽
		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 4; j++) {
				if (temp[i][j] == -2 && temp[i][j] != WALL) {
					main_org[b_y + i][b_x + j] = EMPTY;
				}
			}
		}
		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 4; j++) {
				if (temp[i][j] == -2)
					main_org[b_y + i][b_x + j + 1] = ACTIVE_BLOCK;
			}
		}
		b_x++;
		break;
	}
	case LEFT: { //왼쪽
		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 4; j++) {
				if (temp[i][j] == -2) {
					main_org[b_y + i][b_x + j] = EMPTY;
				}
			}
		}
		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 4; j++) {
				if (temp[i][j] == -2)
					main_org[b_y + i][b_x + j - 1] = ACTIVE_BLOCK;
			}
		}
		b_x--;
		break;
	}
	case UP: { //턴
		block_turn(temp);
		break;
	}
	/*case SPACE: {
		hard_down = 1;
		drop_block();
		
	}*/
	default:
		break;
	}
	game_over();
		
}

//블럭 회전 함수
void block_turn(int block[4][4]) {
	int arr[4][4];
	int n1, n2;
	n1 = 0;

	for (int i = 0; i < 4; i++) {
		n2 = 0;
		for (int j = 3; j > -1; j--) {
			arr[n1][n2] = block[j][i];
			n2++;
		}
		n1++;
	}
	//check_crash_turn(b_x+1, b_y+1, *arr);
	if (crash_num == 0) {
		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 4; j++) {
				if (temp[i][j] == -2) {
					main_org[b_y + i][b_x + j] = EMPTY;
				}
			}
		}
		for (int i = 0; i < 4; i++)
			for (int j = 0; j < 4; j++)
				temp[i][j] = arr[i][j];
		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 4; j++) {
				if (temp[i][j] == -2)
					main_org[b_y + i][b_x + j] = ACTIVE_BLOCK;
			}
		}
	}
}

//한줄이 다 채워졌는지를 체크후 제거
void line_check() {
	int block = 0;
	for (int i = MAP_Y - 2; i > 3;) {
		block = 0;
		for (int x = 1; x < MAP_X - 1; x++)
			if (main_org[i][x]>0) block++;
		if (block == MAP_X - 2) {
			for (int k = i; k > 1; k--) { //윗줄을 한칸씩 모두 내림(윗줄이 천장이 아닌 경우에만) 
				for (int l = 1; l < MAP_X - 1; l++) {
					if (main_org[k - 1][l] != CEILLING) main_org[k][l] = main_org[k - 1][l];
					if (main_org[k - 1][l] == CEILLING) main_org[k][l] = EMPTY;
				}
			}
			if (re_line == 10) {
				level++; 
				game_speed -= 100;
				re_line = 0;
			}
			now_score += 500;
			re_line++;
			gotoxy(STATUS_X_ADJ, STATUS_Y_LEVEL); printf(" LEVEL : %5d", level);
			gotoxy(STATUS_X_ADJ, STATUS_Y_GOAL); printf(" GOAL  : %5d", 10 - re_line);
			gotoxy(STATUS_X_ADJ, STATUS_Y_SCORE); printf("        %6d", now_score);
		}
		else i--;
	}
}

//게임오버 함수
void game_over() {
	for (int i = 1; i < MAP_X - 1;i++)
		if (main_org[3][i]==INACTIVE_BLOCK) {
			system("cls");
			printf("Game_Over\n");
			exit(1);
		}
}

/*void drop_block(void) {
	while (crash_num == 0) {
		check_crash(b_x, b_y + 1);
		move_block(DOWN);
	}
	hard_down = 0;
}*/

Head.h
#pragma once

#define RIGHT 77
#define LEFT 75
#define UP 72
#define DOWN 80
#define SPACE 32
#define MAP_X 11
#define MAP_Y 23
#define MAIN_X 3
#define MAIN_Y 1

#define ACTIVE_BLOCK -2 //현재 이동중인 블럭
#define CEILLING -1 //블럭이 이동할수 있는 위치는 0이나 음의 정수로 표현
#define EMPTY 0 
#define WALL 1     //블럭이 이동할수 없는 위치는 양의 정수로 표현
#define INACTIVE_BLOCK 2
#define STATUS_X_ADJ MAIN_X+MAP_X+1 //게임정보표시 위치조정 


링크드 리스트




링크드 리스트는 대표적으로 두가지 형태가 있다.


1. 단일 연결리스트

단일 연결리스트는 말그대로 리스트의 형태가 단 하나로 연결되어 있는 형태이다.

말로만 하면 어려우니 아래 그림을 보자


이 그림은 단일 연결 리스트의 형태를 나타낸 그림이다(출처: http://blog.naver.com/jak500/220220692977)

노드는 우리말로 마디라는 뜻인데 링크드 리스트는 '노드를 연결해서 만드는 리스트'라고 하여 붙여진 이름이다.

링크드 리스트의 노드는 위 그림처럼 데이터를 저장하는 데이터필드와 다음 노드의 주소를 저장하여 연결고리를 만드는 포인터(Ptr)로 이루어져 있다.

링크드 리스트에는 헤드와 테일이 있다.

헤드는 리스트의 첫번째 노드를 지칭하고 테일은 리스트의 마지막 노드를 의미한다. 알아두면 다음 글부터 이해하기 수월하다.

 이제부터 직접 구현을 해보자.


리스트에는 5가지의 주요 기능있다.

1. 노드생성

2. 노드 연결

3. 노드 탐색

4. 노드 삭제

5. 노드 삽입


링크드 리스트의 노드를 C언어로 표현하면 다음 그림과 같은 구조체로 나타낼 수 있다.


이 코드에서 data 필드의 자료형이 int 지만 조금 단순하게 하기 위한 것이니 나중에 공부를 마치고 다양한 프로그램을 제작할 때에는 그에 맞게 바꾸거나 추가해주면 된다. 또한 struct의 앞에 typedef라는 키워드가 붙은 이유는 기존의 struct ListNode를 사용하기 위해서는 struct ListNode List와 같이 struct 키워드를 동반해야 하는 불편함이 있기에 typedef 키워드를 사용하여 구조체를 정의 하여 사용한 것이다. 이제 ListNode List와 같이 간단하게 만들 수있다.


바로 노드의 생성부분을 설명하기 전에 C언어에 존재하는 저장공간에 대해 간단하게 설명하고 진행 하겠다.

C언어에는 3가지 저장공간이 존재한다.

먼저 전역변수나 정적변수등을 저장하는 정적메모리, 지역 변수가 저장되는 자동메모리, 마지막으로 자유 저장소이다.

정적메모리는 프로그램이 종료될때 데이터가 초기화된다.

자동 메모리는 블럭 단위가 종료될때 데이터가 초기화된다.


{
       int a;
       char b;
       double c;
}

위와 같은 코드에서 블럭이 종료되면 블럭안의 변수에 할당된 데이터의 공간 또한 초기화 된다. 

이 과정은 함수에서도 적용된다.

그렇다면 남은 저장공간은 자유 저장공간이다.

이 저장공간에 노드의 데이터를 저장하기 위해서는 malloc()함수가 필요합니다.

void* malloc(size_t size);

malloc() 함수의  반환형인 void*는 '만능 포인터'이다.

어떤 형이라도 가르킬수 있다.

malloc()함수를 이용하여 메모리를 할당해주는 예제는 아래와 같다.

이제 이렇게 head노드가 완성 되었다.


이제 노드 생성 부분의 코드를 보자

new_Node -> data =data  //인자로 노드에 저장할 data를 받고 노드를 생성한뒤 노드의 data필드에 인자값으로 받아온 data를 저장한다.

new_Node -> next = NULL  //그후 노드의 다음 노드에 대한 포인터는 NULL로 초기화 해준다.

return new_Node;  //새로 생성한 노드의 주소를 반환 한다.


이제 새로운 노드를 생성했다.

그럼 생성한 노드를 이전의 링크드 리스트의 테일노드 뒤에 연결하는 역활을 하는 함수가 필요하다 

방금 만든 add()함수로 새로운 노드를 생성한뒤 새로 생성한 노드의 주소를 테일노듸의 next에 저장해 주면 될것이다.

먼저 헤드노드와 새로 연결해줄 노드의 주소값을 받아온다.

그후 head를 검사하는 이유는 처음 실행했을때 head의 값이 NULL이면 새로 추가하는 노드가 head노드가 되기 때문에 if문으로 처리를 해주었다.

head노드가 존재하면 Tail노드에 head노드의 주소를 저장시킨다.

그럼 Tail노드는 head노드의 정보를 가진 노드가 된다.

그후 while()문으로 Tail노드의 next가 끝에 도달할때 까지 반복해준다.

Tail노드의 next가 NULL이면 Tail노드의 next에 new_Node를 저장해준다.


이렇게 생성한 노드를 링크드 리스트의 테일노드의 next에 연결 해주는 과정까지 끝났다.

이번엔 현재 링크드 리스트에 연결되있는 노드를 모두 탐색하는 함수를 만들어 보자.

코드를 살펴보자.

먼저 Head노드의 주소를 받아왔고 Node라는 새로운 노드를 만들었다.

if()문으로 Head의 주소에 있는 데이터가 NULL값이면 NotData를 출력시키고

NULL이 아니면 Node에 Head의 주소를 저장한다.

위에서 테일의 마지막을 찾는 방법과 동일하다. 

즉 우리는 이미 링크드리스트의 노드들을 head노드부터 tail노드까지 탐색하는 방법을 이미 적용 시킨것이다.

탐색하는 방법을 그림으로 나타내보자.

실행 시키면 위 그림과 같은 순서로 링크드 리스트의 노드탐색이 진행된다.

자 이제 이러한 탐색 방법을 이용해 일정한 노드의 위치를 찾거나 원하는 노드를 삭제 할 수 있다!

우선 코드를 보기 전에 링크드 리스트의 노드를 삭제하는 방법에 대해서 알아보자.

노드를 삭제 시키는 것은 간단하다.

바로 free()노드를 사용하는 것이다.

free()함수는 자유저장소 즉 동적메모리를 할당해준 변수의 주소를 정확히 알려주면 그 주소에 할당된 메모리를 삭제한다.

|| void free(void* memblock);

자 그럼 밑에 그림을 보자

위 그림에 있는 설명처럼 이전노드의 next에 삭제한 노드가 가르키던 next의 주소를 저장하면 위와 같은 상태가 된다.

이 상태에서 연결이 끊어진 노드의 메모리를 삭제하게되면 노드는 프로그램 상에서 완전히 사라진다.

그럼 한번 코드로 구현해 보자

head노드의 주소를 Node에 저장하고

deleteNode에는 head노드의 next에 저장되있는 주소를 저장한다.

그후 탐색방법과 같은 방법으로 deleteNode의 data가 삭제하고싶은 data와 일치할때까지 반복해서 다음 주소값을 넣어준다.

deleteNode->data가 삭제할 data와 같으면 Node의 next에 deleteNode의 next의 주소값을 저장한다.

이렇게 삭제 함수도 구현을 끝냈다.


이제 마지막으로 삽입이다.

아래 그림은 삽입을 그림으로 표현 한 것이다.

말이 많아 조금 복잡해 보이지만 읽어보면 생각보다 간단하다.

삽입할 노드의 주소를 이전노드의 next에 저장하고 이전노드에 저장되 있던 next의 주소를 삽입한 노드의 next로 넘겨주면 노드의 삽입이 끝난다.

막상 해석하니 생각보다 간단하지 않은가..?

한번 위의 내용을 가지고 코드로 구현을 해보자

삭제함수와 비슷해보이지만 삭제함수가 이전노드의 next를 다음 노드의 주소값으로 연결을 시켜줬다면

삽입함수는 삽입을 하고자 하는 위치의 이전 노드에 저장된 next의 주소값을 삽입하고자 하는 노드의 next로 넘겨주고

이전노드의 next를 삽입한 노드의 주소로 변경시켜주면 구현이 끝난다.


자 이렇게 링크드 리스트의 중요한 기능들을 구현하고 살펴 보았다.

하지만 이 코드들에는 분명 문제점이 있다.

따로 말하지는 않겠지만 꼭 찾아서 수정하여 오류가 발생하지 않도록 해보길 권장한다

아래는 소스코드의 전체 모습이다.


다음에는 이중연결리스트에 대해서 포스팅 하겠다.

감사합니다.







'프로그래밍 언어 > 알고리즘' 카테고리의 다른 글

알고리즘  (1) 2016.03.28


양재만.hwp


 

 


목차

 

. 프로젝트 실행 동기 및 목적

- 프로젝트 기획 배경 ------------------------------------------------ 3p

- 프로젝트의 목적 ------------------------------------------------ 3p

- 개발환경 ------------------------------------------------ 3p

. 이론적 배경(기존 사례 또는 문헌 연구 결과)

- 소켓통신 ------------------------------------------------ 4p

- 데이터베이스 ------------------------------------------------- 6p

- 안드로이드 ------------------------------------------------ 7p

- 다이얼로그 ------------------------------------------------ 7p

- 기존의 유사한 프로그램 ------------------------------------------------ 8p

- 알고리즘과 프로젝트의 방향 -------------------------------------------- 8p

. 프로젝트 방법 및 과정

- 서버소켓 ------------------------------------------------ 9p

- 클라이언트소켓 ------------------------------------------------ 12p

- 개발 중 발생한 문제점들 ------------------------------------------------ 18p

. 프로젝트 결과

- 프로젝트의 결과 ------------------------------------------------ 19p

. 결론 및 고찰

- 앞으로의 프로젝트 개발 방향 ------------------------------------------- 21p

참고문헌

-소스코드 ------------------------------------------------ 22p

 

. 프로젝트 실행 동기 및 목적

 

- 프로젝트 기획 배경

나는 평소에도 잠이 많아 아침에 일어나기가 쉽지 않다. 어떻게 하면 아침에 쉽게 일어날 수 있을까 생각해 보다 누군가가 알람을 대신 실행시켜주는 프로그램이 있으면 어떨까 생각하게 되었다.

 

 

- 프로젝트의 목적

프로젝트의 목적은 사용자가 다른 사용자의 스마트 폰의 알람을 on/off 할 수 있는 프로그램 제작이었지만 개발 가능성이 희박하다. 그렇기 때문에 다른 사용자의 스마트 폰에 설치된 어플리케이션의 알람을 on/off 할 수 있는 프로그램 개발을 진행하기로 하였다.

 

 

- 개발 환경

 

개발 언어: Java, Android

개발 환경: Windows 8.1 64bit

실행 환경: Android Application

개발 툴: eclipse, Android Studio

 

 

 

 

 

 

 

 

 

. 이론적 배경(기존 사례 또는 문헌 연구 결과)

소켓 통신

 

소켓이란 네트워크를 통해서 여러 대의 컴퓨터들에서 실행되는 프로세스간의 통신 채널의 한 종류이다. 소켓의 통신 방법으로는 TCP 방식과 UDP 방식으로 나눠진다.

항목

TCP

UDP

연결방식

연결기반 (connection-oriented), 연결 후 통신, 1:1 통신 방식

비연결기반(connectionless-oriented) 연결없이 통신(소포), 1:1, 1:n, n:n, 통신방식

특징

데이터의 경계를 구분안함 (byte-stream). 신뢰성 있는 데이터 전송

데이터의 전송순서가 보장됨, 수신여부를 확인함 (손실되면 재전송)

패킷을 관리할 필요가 없음, UDP보다 전송속도가 느림

데이터의 경계를 구분함(datagram), 신뢰성이 없는 데이터 전송

데이터의 전송순서가 바뀔 수 있음, 수신여부를 확인 안함 (데이터가 손실되어도 알 수 없음)

패킷을 관리해줘야 함, TCP보다 전송속도가 빠름

관련 클래스

Socket, ServerSocket

DatagramSocket, DatagramPacket, MulticastSocket

TCP 방식과 UDP 방식을 간단하게 나타낸 표이다.

출처(http://gangzzang.tistory.com/entry/Java-%EC%86%8C%EC%BC%93-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D)

 

 

 

표와 같이 TCP 방식은 연결 후 통신이다. 연결 후 통신이란 사용자의 컴퓨터에 실행되는 프로세스와 다른 사용자의 프로세스가 정상적으로 연결이 되어야 서로의 컴퓨터끼리 통신이 가능하게 되는 방식이다. 이와 반대로 UDP 방식은 연결 없이 통신을 하는 방식이다. 즉 사용자들의 컴퓨터의 연결이 이루어지지 않은 상태로도 데이터를 보낼 수 있다.

 

TCP - 연결지향이며, 자체적으로 오류를 처리하며, 네트워크 전송 도중 순서가 뒤바뀐 메시지를 교정시켜주는 기능을 가지고 있다. 연결지향이란 말은 데이터를 전송하는 측과 데이터를 전송받는 측에서 전용의 데이터 전송 선로(Session)를 만든다는 의미이다. 데이터의 신뢰도가 중요하다고 판단될 때 주로 사용된다.

 

그림입니다.

원본 그림의 이름: 2.png

원본 그림의 크기: 가로 394pixel, 세로 308pixel 이 그림은 TCP 방식을 그림으로 나타낸 것이다.

 

UDP - 비연결지향이며, 오류를 처리하거나 순서를 재조합시켜주는 기능을 가지고 있지 않다. 단순히 데이터를 받거나, 던져주기만 하는 프로토콜이다. UDP는 특히 실시간 멀티미디어 정보를 처리하기 위해서 주로 사용한다.

 

그림입니다.

원본 그림의 이름: 1.png

원본 그림의 크기: 가로 447pixel, 세로 486pixel 이 그림은 UDP 방식을 그림으로 표현한 것이다.

 

출처(http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/Network_Programing/Documents/IntroTCPIP 3번 항목)

데이터베이스

 

데이터베이스는 작성된 목록으로써 여러 응용 시스템들의 통합된 정보들을 저장하여 운영할 수 있는 공용 데이터들의 묶음이다. 데이터베이스에는 장점과 단점이 있다.

장점

단점

데이터 중복 최소화

데이터베이스 전문가 필요

데이터 저장 공간 절약

데이터 백업과 복구가 어려움

일관성, 무결성, 보안성 유지

시스템의 복잡함

위 표는 데이터베이스의 장단점을 표로 만들어 놓은 모습이다.

출처https://ko.wikipedia.org/wiki/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4#.EB.8D.B0.EC.9D.B4.ED.84.B0.EB.B2.A0.EC.9D.B4.EC.8A.A4.EC.9D.98_.EC.9E.A5.EB.8B.A8.EC.A0.90

 

데이터베이스를 사용하는 가장 큰 장점은 데이터 저장 공간을 절약하고 관리하기 쉽다는 점이다.

하지만 절약과 관리가 되는 만큼 시스템이 복잡해지고 데이터베이스에 대한 전문적인 지식이 있어야 한다는 단점이 있다.

주로 데이터베이스를 이용하는 것은 주로 사용자의 개인정보를 저장하는 용도로 많이 사용한다.

또한 데이터베이스를 사용하기 위해서는 SQL이라는 언어를 알아야한다.

SQL란 관계형식의 데이터베이스의 데이터를 관리 및 처리하기 위해서 설계된 프로그래밍 언어다.

SQL은 크게 데이터정의언어, 데이터 조작언어, 데이터 제어언어 로 3가지 문법으로 구분할 수 있다. 데이터 정의언어는 테이블과 인덱스 값을 관리하고, 데이터 조작언어는 데이터를 쓰거나 읽거나 지우는데 사용한다.

마지막으로 데이터 제어언어는 권한 부여 및 트랜잭션 관리에 사용된다.

 

 

안드로이드

 

안드로이드는 OHA에서 개발된 운영체제와 미들웨어 및 핵심 모바일 어플리케이션을 포함한 모바일 기기를 위한 소프트웨어 스택이라고 할 수 있습니다. 또한 안드로이드는 공개된 리눅스 커널을 기반으로 하고 있습니다. 쉽게 모바일 기기를 위한 하나의 OS이다.

 

이 프로젝트에서는 안드로이드 환경에서 개발을 하였다.

그 이유는 안드로이드는 풍부한 어플리케이션을 구축하는데 사용할 수 있는 광범위한 유용한 라이브러리들을 제공하여 보다 빠르고 쉽게 어플리케이션을 구축하기 쉽고 또한 오픈소스 등의 유용한 자료가 많이 공개되어 있기 때문에 안드로이드 어플리케이션으로 만들게 되었습니다.

 

다이얼로그

다이얼로그란 사용자에게 정보를 보여 주거나 응답을 받는 사용자 인터페이스에서 사용되는 특별한 창이다. 다이얼로그의 종류에는 2가지가 있다.

모달방식 2. 모달리스방식

모달방식

 

모달 창이 열렸을 때는 기존의 창을 이용하지 못한다.

제어 권을 독점하여 모달 창을 종료하기 전까지는 기존 창을 작업할 수 없는 방식

ex) visual studio의 정보대화 상자

 

모달리스방식

 

모달과는 반대의 개념으로 새로운 창이 열리더라도 기존의 창에서 작업을 할 수 있다. 사용자의 동작에 관계없이 프로그램 상태를 일관성 있게 유지시켜야 하는 과정에서 모달방식보다 프로그래밍이 더 어렵다.

ex) visual studio의 편집메뉴

 

프로젝트에서 다이얼로그를 띄운 상태에서는 다른 작업을 하면 안 되기 때문에 모달방식으로 프로그램을 제작하였다.

 

기존의 유사한 프로그램

 

커플 어플리케이션

 

카카오 톡

 

알람

 

프로젝트의 프로그램과 비슷한 기능을 가진 기존 프로그램들은 위와 같이 크게 3가지로 나눌 수 있다.

 

비고

카카오 톡

커플 어플리케이션

알람

유사한 점

소켓통신을 이용하여 메시지를 보낸다.

서로 연결을 한다.

일정한 행동을 명령받으면 소리를 출력한다.

다른 점

메시지가 아닌 명령을 보낸다.

X

상대방이 명령을 보내면 실행된다.

 

1번은 소켓통신을 이용하여 메시지를 보낸다는 것이다. 하지만 그저 채팅과 같은 메시지가 명령으로써 서버에서 실행되는 행동을 명령한다.

 

2번과 제작하고자 하는 프로그램과 유사한 점은 서로 연결을 한다는 점이다. 연결을 하는 것 외에는 대부분이 다르기 때문에 서술하지 않았다.

 

3번은 일정한 시간 즉 일정한 행동을 명령받으면 소리를 출력해주는 프로그램이기 때문에 비슷하다. 하지만 일정한 시간 또는 사용자가 원하는 시간이 아닌 사용자와 연결이 되어있는 다른 사용자의 어플리케이션에서 명령이 올 때 실행이 된다.

 

 

알고리즘과 프로젝트의 방향

 

프로젝트의 실행은 우선적으로 사용자와 사용자의 연결이 정상적으로 이루어졌을 때만 정상적인 실행이 가능하다. 사용자와 다른 사용자의 연결을 위해서는 사용자마다 각각의 정보가 있어야한다. 그렇기 때문에 회원가입시스템을 사용하게 되었다. 회원가입시스템을 사용하면서 사용자의 개인정보를 관리할 공간으로 데이터베이스를 사용하였다. 이렇게 생성된 사용자의 개인정보에 사용자의 소켓번호를 KEY값으로 데이터베이스에 함께 저장시켜 자신이 연결하고자 하는 사용자에게 연결요청을 보내고 검색하는 용도로 사용을 하게 되었다. 연결이 된 후 깨우기 버튼을

누르면 자신과 연결되어있는 사용자의 스마트폰에서 미리 설정된 음악파일이 실행되게 된다.

 

 

 

 

 

 

. 프로젝트 방법 및 과정

 

프로젝트에 주로 사용된 기능은 위에서 설명한 소켓통신과 데이터베이스이다.

 

 

서버소켓

그림입니다.

원본 그림의 이름: 도표1.png

원본 그림의 크기: 가로 717pixel, 세로 446pixel

 

위 그림은 서버가 하는 일을 간략하게 설명한 것이다.

위 그림에서 알 수 있듯이, 서버는 여러 클라이언트로부터 받는 데이터를 서버 내부에서

관리하고 처리하여 나온 명령들을 다른 클라이언트로 보내준다. 그 후 작업은 클라이언트 내부에서 실행된다.

 

서버는 총 3가지의 주요 명령을 받는다.

join (회원가입)

2. connect (상대방과 연결)

3. Wake_up (깨우기)

 

1번과 2번의 경우는 데이터베이스와 연동되어 실행된다.

이 과정에서 이클립스와 데이터베이스의 연동이 필요하였다.

이클립스와 데이터베이스의 연동에는 이클립스 SQL Explorer플러그인, 또는 JDBC 드라이버를 사용하는 방법이 있다. 이클립스 SQL Explorer플러그인은 간단한 데이터베이스사용, 간단한 데이터베이스의 자료검색 등의 작업에 편리하다. JDBC 드라이버는 자바에서 데이터베이스 접근을 위한 표준 플랫폼이다. 그렇기 때문에 많은 데이터베이스연동 예시가 있다. SQL Explorer플러그인도 예제가 없지는 않지만 JDBC보다 자세한 예제들이 많이 있지 않았다. 그러한 이유로 나는 JDBC를 이용하여 이클립스와 데이터베이스를 연동하였다.

 

1번에서는 회원가입을 원하는 명령어를 받으면 사용자가 입력하는 이름, 닉네임, 아이디, 비밀번호, 전화번호, 이메일을 임시로 저장하여 데이터베이스에 저장하게 된다.

그림입니다.

원본 그림의 이름: 도표1.png

원본 그림의 크기: 가로 723pixel, 세로 683pixel

 

 

 

 

 

Name: 사용자의 이름

Nic_Name: 사용자의 닉네임

ID: 사용자의 아이디

PW: 사용자의 비밀번호

TEL: 사용자의 전화번호

E-MAIL: 사용자의 이메일

 

 

 

 

위 그림은 사용자가 회원가입을 진행할 때 서버에서 데이터베이스로 저장하는 과정을 표현한 것이다.

 

회원가입 시 작동하는 과정은 클라이언트에서 회원가입의 데이터를 받고 서버에서 처리 후 명령문과 함께 데이터베이스에 전송한다. 데이터베이스에서는 명령문에 해당하는 동작을 하며 서버로부터 받은 데이터를 저장하게 된다.

String sql = "insert into join_database.test values(" + socket_num + "," + "'" + name + "'" + "," +"'"+ nic_name + "'" + "," + "'"+ id + "'" + "," + "'" + pw + "'" + "," + "'" + tel + "'" + "," + "'" + e + "'" + ")";

 

위 코드가 데이터베이스로 명령과 데이터를 함께 보내는 코드이다.

 

 

 

 

2번에서는 데이터베이스 테이블의 자료를 검색하고 해당하는 자료의 값을 추출해내어 그 추출해온 값을 토대로 사용자들의 연결을 가능하게 만들어준다.

 

1번의 경우는 데이터베이스에 자료를 삽입한다면 2번은 데이터베이스에서 값을 추출한다.

그림입니다.

원본 그림의 이름: 도표1.png

원본 그림의 크기: 가로 396pixel, 세로 447pixel 2번의 작동을 그림으로 표현

위 그림과 같이 먼저 서버에서 데이터베이스로 명령을 보낸다. 그럼 데이터베이스에서는 그 명령을 받아 그 명령에 해당하는 동작을 한다. 1번에서 데이터베이스가 서버에서 보내는 자료를 저장하는 동작을 한다면 2번에서는 데이터베이스 테이블 내에 있는 모든 자료들 중에 서버가 보낸 데이터와 일치하는 데이터를 찾는다.

ex) 서버에서 사용자의 닉네임 A를 보냈다. 데이터베이스에는 A,B,C의 닉네임을 가진 사용자의 개인정보가 있다. 그럼 데이터베이스에서는 0번 인덱스부터 인덱스의 끝까지 검색을 한다. 만약 데이터가 있다면 그 데이터가 포함된 정보 즉 사용자의 모든 개인정보를 추출할 수 있다.

 

이와 같은 방법으로 연결요청을 받는 사람의 닉네임을 클라이언트에서 입력받아 서버로 전송되고 서버에서 데이터베이스로 명령과 함께 전달하여 찾고자하는 사용자의 정보를 추출해 낼 수 있었다.

 

 

클라이언트 소켓

 

클라이언트는 사용자의 스마트폰에 설치되는 어플리케이션을 말한다.

클라이언트는 사용자가 원하는 기능을 수행할 때 명령문을 서버로 보내고 서버에서 처리된 명령을 받아 클라이언트 내부에서 그 명령에 해당하는 동작을 수행한다.

 

그림입니다.

원본 그림의 이름: 도표1.png

원본 그림의 크기: 가로 346pixel, 세로 215pixel

위 그림은 클라이언트가 명령문을 보내 작동하는 동작을 그림으로 표현한 것이다.

 

그림입니다.

원본 그림의 이름: 캡처.PNG

원본 그림의 크기: 가로 379pixel, 세로 649pixel 위 그림에서 알 수 있듯이, 클라이언트는 서버로 데이터를 보낸다. 서버는 그 데이터를 판별하여 데이터에 해당하는 명령을 서버로 보낸다. 클라이언트는 그 명령을 받게 되면 그에 해당하는 동작을 클라이언트 내부에서 실행하게 된다.

 

 

 

 

 

 

 

 

 

현재 클라이언트의 모습 옆의 사진과 같이 클라이언트에서 서버로 명령을 전송하는 기능은 총 3가지이다.

1. 회원가입

2. 상대방과 연결

 

3. 깨우기 , Start

 

1번의 경우는 클라이언트에서 사용자의 이름,아이디,비밀번호,전화번호,이메일을 입력받는다. 이 과정은 다이얼로그를 이용하여 처리하였다.

그림입니다.

원본 그림의 이름: 캡처.PNG

원본 그림의 크기: 가로 374pixel, 세로 666pixel 회원가입 버튼을 누르면 생기는 다이얼로그

이름,아이디,비밀번호,전화번호,이메일을 입력받으면 그 데이터를 클라이언트에서 서버로 전송시킨다.

서버로 전송할 때 먼저 명령을 보내고 그 뒤에 데이터를 보낸다. 서버에서는 설명했던 것과 같이 먼저 명령을 받고 그 명령에 해당되는 코드를 실행한다.

 

 

 

그림입니다.

원본 그림의 이름: 도표1.png

원본 그림의 크기: 가로 700pixel, 세로 428pixel

회원가입 시 데이터의 전송과정을 그림으로 표현

 

2번은 상대방과의 연결이다.

2번에서는 먼저 자신이 연결요청을 보내고 싶은 사용자의 이름을 입력하고 같이 보내고 싶은 메시지를 입력한다. 이 과정 또한 다이얼로그를 사용하였다.

 

사용자는 자신과 연결을 하고 싶은 상대방의 이름을 입력한다. 그 후 그 상대방에게 보내는 메시지를 입력한다. 그렇게 입력된 이름과 메시지는 서버로 전송되게 된다.

이때 보내는 사용자의 이름도 함께 전송된다.

 

서버는 그 데이터를 받아서 이름을 통하여 데이터베이스에서 소켓번호를 추출해 그 소켓번호로 보내는 사람의 이름과 메시지를 담은 명령을 추출한 소켓번호의 클라이언트로 전송시켜준다.

그림입니다.

원본 그림의 이름: 캡처.PNG

원본 그림의 크기: 가로 380pixel, 세로 673pixel 현재 개발된 연결창의 이미지

 

그림입니다.

원본 그림의 이름: 도표1.png

원본 그림의 크기: 가로 823pixel, 세로 552pixel

클라이언트 A에서 B로 메시지를 보내는 과정을 그림으로 나타낸 것이다.

 

 

 

연결요청을 받은 클라이언트는 명령에 해당하는 행동을 한다.

연결요청에서는 아래와 같이 다이얼로그를 이용하여 연결요청이 왔다는 것을 사용자에게 알린다.

 

 

그림입니다.

원본 그림의 이름: 캡처.PNG

원본 그림의 크기: 가로 291pixel, 세로 258pixel 이 사진은 연결을 요청하는 사진이다.

 

그림입니다.

원본 그림의 이름: 캡처.PNG

원본 그림의 크기: 가로 374pixel, 세로 176pixel

위 사진은 연결요청을 받은 사진이다.

 

위 사진들과 같이 연결요청을 받으면 사용자의 이름과 메시지가 연결요청을 받은 클라이언트로 전송되어 다이얼로그로 출력되게 된다.

여기서 연결요청을 받은 사용자가 확인을 누르게 되면 서버로 연결요청을 수락했다는 명령을 보내준다. 서버가 그 명령을 받으면 연결요청을 보낸 사용자의 데이터와 연결요청을 수락한 사용자의 데이터를 서버내의 저장 공간에 저장한다.

 

 

 

 

 

 

3번은 사용자가 자신과 연결된 사용자에게 명령을 보내 다른 사용자의 어플리케이션에서 소리가 난다.

 

사용자 A와 사용자 B가 연결된 상태일 때 AB를 깨우고 싶다면 깨우기 버튼을 누르게 된다. 깨우기 버튼을 누르면 깨우기 명령에 해당하는 명령을 서버에 전송한다. 서버는 그 명령을 받아 A와 연결된 사용자를 서버자체의 저장 공간에서 검색하여 데이터를 찾아낸 후 B라는 클라이언트에 명령을 전송하게 된다.

그림입니다.

원본 그림의 이름: 도표1.png

원본 그림의 크기: 가로 659pixel, 세로 346pixel

위 그림은 클라이언트 A에서 B로 데이터를 전송하는 과정이다.

 

클라이언트A에서 클라이언트B로 데이터를 보내면 클라이언트B는 그 데이터를 받고

어플리케이션 내부에 저장되어있는 음악파일을 재생한다.

클라이언트A에서는 깨우기 버튼을 한번 누르면 버튼의 상태가 변한다.

이때 버튼을 한 번 더 누르게 되면 클라이언트B에 명령이 전달되어 음악파일이 정지된다. 이는 클라이언트B의 사용자가 직접 종료를 하고 다시 잠들 수 있는 상황을 대비한 것이다.

 

 

 

 

 

 

 

 

개발 중 발생한 문제점

 

1. Could not open mysql.plugin table. Some plugins may be not loaded

SQL을 처음 사용할 때 오류가 발생하였다. 해결방법은 다시 삭제하고 설치한 후 my.ini파일에서 utf-8set-up해줬다.

오류가 난 원인은 설정되어있지 않은 utf-8의 문자속성을 추가하였기 때문이다.

 

2. 쓰레드 안에서의 UI구현

기본적으로 Android에서는 쓰레드 안에서의 UI변경에 제한을 두고 있다.

그러한 이유로 인하여 쓰레드 안에서 UI의 변경을 시도할 시 오류가 발생하였다.

오류의 해결 방법은 RunOnUIThread방법이다.

RunOnUIThread방법이란 Activity에서 Thread를 호출 하였을 경우에 사용할 수 있는 메소드이다. 이 메소드를 사용하게 되면 그 메소드 안에서만 UI의 변경이 가능하게 된다. 하지만 이 메소드는 MainActivity안에서만 사용이 가능하다는 단점이 있다.

 

3. ArrayList<ArrayList<String>>(); 으로 연결된 데이터를 관리

위에 방법으로 시도 ArrayList안의 ArrayList<String>의 값이 달라지면 ArrayList의 값도 변경되는 문제점 발생

검색 후 ArrayList<String[]>의 방식으로 새로운 String형 배열을 생성하면서 ArrayList에 저장

 

4. 회원가입 시 SQL인젝션

회원가입 테스트 중 SQL인젝션 현상을 발견 SQL인젝션이란 SQL을 연동시킨 후 명령을 전송할 때 SQL소스에 문제를 발생시킬 수 있는 명령어 등을 입력하여 프로그램에 오류를 발생시키는 것이다. 이러한 문제점을 없애기 위하여 클라이언트에서 회원가입 시 필터 기법을 사용하였다.

 

Pattern pattern = Pattern.compile("[a-zA-Z--]*$");

 

 

 

 

 

 

 

 

 

 

. 프로젝트 결과

 

구현 된 기능 : 회원가입, 데이터베이스 연동, 상대방에게 연결요청 메시지 전송,

알람 기능

 

프로젝트를 진행하면서 알 수 없는 오류로 인하여 프로젝트가 중단되었다.

그림입니다.

원본 그림의 이름: 캡처.PNG

원본 그림의 크기: 가로 1365pixel, 세로 633pixel

위 사진을 보면 equls에 데이터를 저장하는 부분이 없다. 하지만 equlsjoin이라는 값이 저장된다. 또한 System.out.println(equls);에서 join이 출력되어야 정상이지만 출력 값이 없다.

그림입니다.

원본 그림의 이름: 캡처.PNG

원본 그림의 크기: 가로 1236pixel, 세로 539pixel

사진에서 볼 수 있듯이 equls에는 값이 제대로 들어간다. 출력 또한 정상적으로 이루어진다.

프로그램 자체의 로직이 서로 꼬여버려서 발생한 문제인지, 프로세스 내부에서 문제점이 있는 것인지 더 조사를 해봐야 할 것 같다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

. 결론 및 고찰

현재 개발된 부분은 회원가입, 깨우기, 상대방과 연결기능이다. 하지만 오류가 발생하여 그 오류를 해결하는 과정에서 많은 시간이 소요될 것 같다.

나의 생각으로는 오류가 소스코드상의 문제가 아닌 프로세스적인 문제일 것 같다.

한번 값을 추적해봐야겠지만 데이터를 받지 않고 자동적으로 저장이 되기 위해서는 소스코드 내부에서가 아닌 소스코드를 실행하는 도중의 문제일 가능성이 크다고 생각한다.

또한 프로젝트를 여기서 그만두는 것이 아니라, 어떤 부분에서 오류가 발생하는지, 발생한다면 이유는 무엇인지를 찾아내어 처음부터 끝까지 설계를 하고 설계를 기반으로 개발을 할 것이다.

 

추가할 기능

 

11 연결이 아닌 1n연결을 통하여 친구목록을 생성, 친구목록에서 깨우고 싶은 친구를 선택 후 깨우는 기능

 

페이스 북 연동을 통해 보다 쉽게 친구목록을 불러온다.

 

채팅기능 추가

 

위젯기능 추가

 

 

 

참고문헌

 

1. TCP/IP 29p ~ 74p 소켓 프로그래밍에 대한 이해

 

2. Do it! 안드로이드 앱 프로그래밍 440p ~450 p 쓰레드 관련 부분

 

3. http://lapislazull.tistory.com/80 다이얼로그

 

4. http://gangzzang.tistory.com/entry/Java-%EC%86%8C%EC%BC%93-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D

소켓 프로그래밍

 

5. http://ra2kstar.tistory.com/134 데이터베이스 연동

사용된 부분

소스코드

설명

서버소켓통신

serverSocket = new ServerSocket(7777);

서버소켓과 클라이언트소켓을 연결시켜주는 채널을 생성한다. 이때 채널은 7777번 포트이다.

socket = serverSocket.accept();

서버소켓에 연결을 요청하는 소켓정보를 socket에 저장한다. 이때 소켓의 연결요청이 오지 않으면 다음 문장으로 넘어가지 않고 계속 기다린다.

ArrayList<Socket> socketlist

socketlist.add(socket);

ArrayList를 사용하여 접속한 소켓의 정보를 socketlist에 저장한다.

클라이언트

소켓통신

socket = new Socket(ip,7777);

socketip의 주소의 7777번 포트에 연결요청을 보낸다.

Data INput/

Output

input = new DataInputStream(socket.getInputStream());

모든 소켓의InputStream을 저장한다.

input.readUTF();

input으로 보내진 데이터를 읽어온다.

output = new new DataOutputStream(socketlist.get(1).getOutputStream());

Arraylist에 저장된 소켓들중 x번째 소켓의 정보의 OutputStream을 저장한다.

output.writeUTF("Data“);

output에 저장된 소켓에 Data를 전송한다.

데이터베이스

DriverManager.getConnection("jdbc:mysql://localhost", "A", "1");

자바를 통해 연동된 데이터베이스에 로그인한다. USER이름은 A이고 비밀번호는 1이다.

stmt = conn.createStatement()

stmt.executeQuery(sql_key1)

stmt를 사용가능한 상태로 만들어주고 sql_key에있는 명령을 실행시킨다.

rs = stmt.getResultSet();

위에서 실행된 명령을 통해 나온 결과를 rs에 저장한다.

rs.getString("name");

테이블에서 name이라는 항목의 데이터를 얻어온다.

안드로이드

MediaPlayer music;

music.start();

music.stop();

music.prepare();

안드로이드에서 미디어 파일을 실행시키기 위해 필요하다. start()는 실행

stop()은 정지

prepare()은 정지 상태에서 다시 실행을 시키기 위해 준비상태처럼 만들어준다.

runOnUiThread(new Runnable() {

@Override

public void run()

{ } });

쓰레드 안에서 UI를 변경하기 위해서 사용한다.

메인액티비티 안에서만 사용이 가능하다.


안드로이드

Pattern pattern = Pattern.compile

("[a-zA-Z--]*$");

회원가입을 할 때 특수문자등의 입력을 제한한다. Pattern을 이용하여 SQL인젝션을 방지하였다.

 

 

 양재만.hwp

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


알고리즘이란 무엇일까?

알고리즘이란 떠한 문제를 해결하기 위한 여러 동작들의 모임이다. 유한성을 가지며, 언젠가는 끝나야 하는 속성을 가지고 있다.


또한 알고리즘은 다음과 같은 조건을 만족해야 한다.


1. 입력 : 외부에서 받는 자료의 개수가 0개 이상 존재해야 한다.

2. 출력 : 외부로 출력하는 결과의 값이 적어도 2개이상 존재해야 한다.

3. 명확성 : 알고리즘의 수행과정이 명확하고 애매 모호하지않은 명령들로 구성되어야 한다. 

4. 유한성 : 명령의 개수가 유한해야하고 유한한 시간내에 종료되어야 한다.

5. 효율성 : 모든 알고리즘의 과정은 실행가능해야 한다.


알고리즘에는 복잡도라는 개념이 있다.

복잡도는 알고리즘에서 그 알고리즘이 어느정도 복잡하고 어느정도의 작업량을 가지는지를 알수 있게 해주는 개념이다.

주로 빅오 표기법으로도 부른다.


빅오 표기법 이란?


빅오 표기법 이란 프로그램의 복잡도를 시간적으로 표현하는 방법 중 하나이다.

주로 알고리즘의 시간복잡도를 계산하여 알고리즘의 성능을 표현하는데 사용한다.

 

빅오 표기법에는 대표적으로

O(1), O(log N), O(n), O(N log N), O(n^2), O(n^3) 등이 있다.

 

O(1): 상수형 빅-오 표기, 데이터 수에 상관없이 연산의 횟수가 고정 된 형태의 알고리즘


O( log N ): 로그형 빅-오 표기, 데이터 수가 증가율에 비하여 연산횟수의 증가율이 훨씬 적은 형태의 알고리즘

 

O(n): 선형 빅-오 표기, 데이터 수의 증가에 따라 연산횟수가 비례하여 증가한다.

 

O( N log N ): 선형 로그형 빅-오 표기법, 데이터 수가 증가하면 증가되는 연산횟수가

로그형으로 표기한 알고리즘 보다 조금 더 증가하는 알고리즘

 

O(n^2): 데이터수의 제곱에 해당하는 연산횟수를 가진 알고리즘

 

O(n^3): 데이터수의 세제곱에 해당하는 연산횟수를 가진 알고리즘

 

O(n!): 팩토리얼형 빅-오 표기법, 데이터의 수가 증가할수록 연산횟수가 매우 커진다.


빅오 표기법들의 시간복잡도 그래프

(평평한 표기법일수록 효율이 좋다.)

 

위에서 설명한 빅오 표기법을 시간복잡도 순서로 나열하면 아래와 같다.

(시간복잡도가 적은 순서대로)

 

O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!)


우선 기초적인 내용은 이정도로 설명하고 다음 글 부터는 자료구조에 대해서 설명할것이다.

자료구조의 설명은 여기서 한다.


자료구조란 무엇일까?


 전산학에서 자료를 효율적으로 이용할 수 있도록 컴퓨터에 저장하는 방법이다. 사용자가 신중하게 선택한 자료구조는 알고리즘의 효율을 향상시키는데 큰 역활을 한다. 효과적으로 설계된 자료구조는 실행시간 혹은 메모리 용량과 같은 자원을 최소한으로 사용하면서 연산을 수행하도록 해준다.


자료구조의 종류로는 리스트, 스택, 큐, 트리, 정령, 탐색 등등 매우 많은 자료구조가 있다.

앞으로 자료구조의 종류에 대해서 설명하고 직접 구현하는 과정으로 진행할것이다.

'프로그래밍 언어 > 알고리즘' 카테고리의 다른 글

링크드 리스트 (단일 연결 리스트)  (1) 2016.03.31


Multi_Server.zip

//자바 서버파일

Cating.apk

//안드로이드 앱



Thread

이번에는 쓰레드에 대해서 공부해 보겠다.

컴퓨터 내에서 동작하고 있는 프로그램을 프로세스(Process)라고 한다. 

평균적으로 한 개의 프로세스는 한 가지의 일을 하지만, 쓰레드를 이용하면 한 프로세스 내에서 두 가지 또는 그 이상의 일을 동시에 실행 할 수있게 된다.

public class First_Thread extends Thread { public void run() { System.out.println("thread run."); } public static void main(String[] args) { First_Thread test = new First_Thread(); test.start(); } }

Test클래스가 Thread를 상속한 상태이다.

Thread 클래스의 run 메소드를 구현하여 예제와 같이 test.start() 실행 시 test객체의 run 메소드가 수행이 된다. 

여기서 public class Test extends Thread 에서 extends를 선언하였기때문에 start 메소드를 실행시 자동으로 run이라는 매소드를 실행하게된다.

하지만 위의 예제 하나로는 쓰레드가 어떻게 실행되는지를 확실하게 확인할수 없다.

시import java.util.Scanner;

public class Thread_first extends Thread {
public void run() {
int i = 0;
System.out.println("Start Thread"); //Thread가 실행을 알려준다
while (true) {
System.out.println("Thread: "+i);//Thread는 3초마다 1을 증가시켜 출력시킨다
try {
Thread.sleep(3000);
} catch (InterruptedException e) {

e.printStackTrace();
}
i++;
}
}

public static void main(String[] args) {
Thread_first test = new Thread_first();
Scanner s = new Scanner(System.in);
String ch = "Thread Start";
String in = null;

int i = 0, j = 0;
while (true) {
try {
System.out.println("Main: "+i);//Main은 1초마다 1씩증가 시켜서 출력한다.
i++;
Thread.sleep(1000);
if (j == 0) {
in = s.nextLine(); //입력
j++;
}
if (ch.equals(in)) { //입력이 ch에 저장되어있는 "Thread Start"와 같으면 Thread를 실행시키다.
test.start();
}

} catch (Exception e) {
}

        }
}

}

위 예제를 실행해보면 쓰레드와 메인둘다 실행이 되어있다는 것을 확인할수 있다. 하지만 아직도 쓰레드와 메인이 별개로 움직인다는 것을 확실하게 파악하기는 힘듭니다.

다음예제를 봅시다.

public class Thread_first extends Thread {
public void run() {
for(int i=0;i<10;i++){ //Thread에서는 1~10까지 출력
System.out.println("Thread : " + i);
try {
sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("Thread END");
}

public static void main(String[] args) {
Thread_first thread = new Thread_first();
thread.start();
for(int i=0;i<5;i++){ //Main에서는 1에서 5까지 출력    
System.out.println("Main : " + i);
try {
sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("Main END");
}

}

위 예제를 실행하면 메인과 쓰레드는 별개로 돌아가고 있다는 것을 확인할수 있습니다.

실행결과

Main : 0 Thread : 0 Main : 1 Thread : 1 Main : 2 Thread : 2 Main : 3 Thread : 3 Main : 4 Thread : 4 Main END Thread : 5 Thread : 6 Thread : 7 Thread : 8 Thread : 9 Thread END

실행결과를 보면 5값이 출력되면서 메인은 종료가 된것이 보인다.
하지만 메인이 종료되었어도 쓰레드는 계속해서 돌아가고 있는 것이보인다. 이를 통해 메인과 쓰레드는 별개로 돌아간다는 것을 확인하였다.


'프로그래밍 언어 > JAVA' 카테고리의 다른 글

자바 객제지향  (0) 2016.07.18

C언어로 제작한 은행 프로그램입니다.

Bankprogram.exe




└>소스코드보기

버튼클릭시 이벤트를 이용한 애플리케이션

+ Recent posts