SyntaxHighlighter.all();

이어서 멀티 쓰레드 채팅프로그램의 chat_server.c 입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<pthread.h>
#include<time.h>
 
#define BUF_SIZE 100
#define MAX_CLNT 100
#define MAX_IP 30
 
void * handle_clnt(void *arg);
void send_msg(char *msg, int len);
void error_handling(char *msg);
char* serverState(int count);
void menu(char port[]);
 
 
/****************************/
 
int clnt_cnt=0;
int clnt_socks[MAX_CLNT];
pthread_mutex_t mutx;
 
int main(int argc, char *argv[])
{
    int serv_sock, clnt_sock;
    struct sockaddr_in serv_adr, clnt_adr;
    int clnt_adr_sz;
    pthread_t t_id;
 
    /** time log **/
    struct tm *t;
    time_t timer = time(NULL);
    t=localtime(&timer);
 
    if (argc != 2)
    {
        printf(" Usage : %s <port>\n", argv[0]);
        exit(1);
    }
 
    menu(argv[1]);
 
    pthread_mutex_init(&mutx, NULL);
    serv_sock=socket(PF_INET, SOCK_STREAM, 0);
    
    memset(&serv_adr, 0sizeof(serv_adr));
    serv_adr.sin_family=AF_INET;
    serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
    serv_adr.sin_port=htons(atoi(argv[1]));
 
    if (bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1)
        error_handling("bind() error");
    if (listen(serv_sock, 5)==-1)
        error_handling("listen() error");
 
    while(1)
    {
        t=localtime(&timer);
        clnt_adr_sz=sizeof(clnt_adr);
        clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);
 
        pthread_mutex_lock(&mutx);
        clnt_socks[clnt_cnt++]=clnt_sock;
        pthread_mutex_unlock(&mutx);
 
        pthread_create(&t_id, NULL, handle_clnt, (void*)&clnt_sock);
        pthread_detach(t_id);
        printf(" Connceted client IP : %s ", inet_ntoa(clnt_adr.sin_addr));
        printf("(%d-%d-%d %d:%d)\n", t->tm_year+1900, t->tm_mon+1, t->tm_mday,
        t->tm_hour, t->tm_min);
        printf(" chatter (%d/100)\n", clnt_cnt);
    }
    close(serv_sock);
    return 0;
}
 
void *handle_clnt(void *arg)
{
    int clnt_sock=*((int*)arg);
    int str_len=0, i;
    char msg[BUF_SIZE];
 
    while((str_len=read(clnt_sock, msg, sizeof(msg)))!=0)
        send_msg(msg, str_len);
 
    // remove disconnected client
    pthread_mutex_lock(&mutx);
    for (i=0; i<clnt_cnt; i++)
    {
        if (clnt_sock==clnt_socks[i])
        {
            while(i++<clnt_cnt-1)
                clnt_socks[i]=clnt_socks[i+1];
            break;
        }
    }
    clnt_cnt--;
    pthread_mutex_unlock(&mutx);
    close(clnt_sock);
    return NULL;
}
 
void send_msg(char* msg, int len)
{
    int i;
    pthread_mutex_lock(&mutx);
    for (i=0; i<clnt_cnt; i++)
        write(clnt_socks[i], msg, len);
    pthread_mutex_unlock(&mutx);
}
 
void error_handling(char *msg)
{
    fputs(msg, stderr);
    fputc('\n', stderr);
    exit(1);
}
 
char* serverState(int count)
{
    char* stateMsg = malloc(sizeof(char* 20);
    strcpy(stateMsg ,"None");
    
    if (count < 5)
        strcpy(stateMsg, "Good");
    else
        strcpy(stateMsg, "Bad");
    
    return stateMsg;
}        
 
void menu(char port[])
{
    system("clear");
    printf(" **** moon/sun chat server ****\n");
    printf(" server port    : %s\n", port);
    printf(" server state   : %s\n", serverState(clnt_cnt));
    printf(" max connection : %d\n", MAX_CLNT);
    printf(" ****          Log         ****\n\n");
}
cs


서버를 오픈하면 기본적으로 다음과 같은 화면을 출력해줍니다.


테스트한 port는 9190이고 최대 접속인원은 100명으로 제한을 두었습니다.


이 부분은 필요에 따라 변경해 사용하시면 됩니다.


* linux에서 포트를 사용할 때 한번 사용한 포트는 보안문제 때문에 당분간 사용할 수 없다고 합니다.

  서버를 오픈하실 때 마다 포트번호를 달리 하셔야 오류가 발생하지 않습니다.



log의 아래에는 접속한 클라이언트를 표시해 주는 부분입니다. 

반응형

리눅스에서 멀티쓰레드를 이용한 채팅프로그램을 만들어 보았습니다.


TCP/IP 소켓프로그래밍을 공부하면서 만든 프로그램인데, 기본적인 네트워크에서 통신과정을


이해하는데 도움이 됬습니다. 



* 설명

멀티 쓰레드를 이용한 다중 접속 채팅프로그램입니다. 


Ubuntu 16.04 64bit 환경에서 C언어로 작성하였습니다^^.


chat_server.c 와 chat_client.c 두 파일로 구성되어있고 이름에서도 알 수 있지만 


server.c는 서버의 역할을 하고 client.c 를 통해 접속하는 방식입니다.


사실 암호화 통신을 적용하고 싶었지만 이 부분은 좀더 공부가 필요할 것 같습니다. ㅎㅎ



* chat_client.c 클라이언트 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/**    chat_client **/
 
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<pthread.h>
#include<time.h>
 
#define BUF_SIZE 100
#define NORMAL_SIZE 20
 
void* send_msg(void* arg);
void* recv_msg(void* arg);
void error_handling(char* msg);
 
void menu();
void changeName();
void menuOptions(); 
 
char name[NORMAL_SIZE]="[DEFALT]";     // name
char msg_form[NORMAL_SIZE];            // msg form
char serv_time[NORMAL_SIZE];        // server time
char msg[BUF_SIZE];                    // msg
char serv_port[NORMAL_SIZE];        // server port number
char clnt_ip[NORMAL_SIZE];            // client ip address
 
 
int main(int argc, char *argv[])
{
    int sock;
    struct sockaddr_in serv_addr;
    pthread_t snd_thread, rcv_thread;
    void* thread_return;
 
    if (argc!=4)
    {
        printf(" Usage : %s <ip> <port> <name>\n", argv[0]);
        exit(1);
    }
 
    /** local time **/
    struct tm *t;
    time_t timer = time(NULL);
    t=localtime(&timer);
    sprintf(serv_time, "%d-%d-%d %d:%d", t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour,
    t->tm_min);
 
    sprintf(name, "[%s]", argv[3]);
    sprintf(clnt_ip, "%s", argv[1]);
    sprintf(serv_port, "%s", argv[2]);
    sock=socket(PF_INET, SOCK_STREAM, 0);
 
    memset(&serv_addr, 0sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
    serv_addr.sin_port=htons(atoi(argv[2]));
 
    if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)
        error_handling(" conncet() error");
 
    /** call menu **/
    menu();
 
    pthread_create(&snd_thread, NULL, send_msg, (void*)&sock);
    pthread_create(&rcv_thread, NULL, recv_msg, (void*)&sock);
    pthread_join(snd_thread, &thread_return);
    pthread_join(rcv_thread, &thread_return);
    close(sock);
    return 0;
}
 
void* send_msg(void* arg)
{
    int sock=*((int*)arg);
    char name_msg[NORMAL_SIZE+BUF_SIZE];
    char myInfo[BUF_SIZE];
    char* who = NULL;
    char temp[BUF_SIZE];
 
    /** send join messge **/
    printf(" >> join the chat !! \n");
    sprintf(myInfo, "%s's join. IP_%s\n",name , clnt_ip);
    write(sock, myInfo, strlen(myInfo));
 
    while(1)
    {
        fgets(msg, BUF_SIZE, stdin);
 
        // menu_mode command -> !menu
        if (!strcmp(msg, "!menu\n"))
        {
            menuOptions();
            continue;
        }
 
        else if (!strcmp(msg, "q\n"|| !strcmp(msg, "Q\n"))
        {
            close(sock);
            exit(0);
        }
 
        // send message
        sprintf(name_msg, "%s %s", name,msg);
        write(sock, name_msg, strlen(name_msg));
    }
    return NULL;
}
 
void* recv_msg(void* arg)
{
    int sock=*((int*)arg);
    char name_msg[NORMAL_SIZE+BUF_SIZE];
    int str_len;
 
    while(1)
    {
        str_len=read(sock, name_msg, NORMAL_SIZE+BUF_SIZE-1);
        if (str_len==-1)
            return (void*)-1;
        name_msg[str_len]=0;
        fputs(name_msg, stdout);
    }
    return NULL;
}
 
 
void menuOptions() 
{
    int select;
    // print menu
    printf("\n\t**** menu mode ****\n");
    printf("\t1. change name\n");
    printf("\t2. clear/update\n\n");
    printf("\tthe other key is cancel");
    printf("\n\t*******************");
    printf("\n\t>> ");
    scanf("%d"&select);
    getchar();
    switch(select)
    {
        // change user name
        case 1:
        changeName();
        break;
 
        // console update(time, clear chatting log)
        case 2:
        menu();
        break;
 
        // menu error
        default:
        printf("\tcancel.");
        break;
    }
}
 
 
/** change user name **/
void changeName()
{
    char nameTemp[100];
    printf("\n\tInput new name -> ");
    scanf("%s", nameTemp);
    sprintf(name, "[%s]", nameTemp);
    printf("\n\tComplete.\n\n");
}
 
void menu()
{
    system("clear");
    printf(" **** moon/sum chatting client ****\n");
    printf(" server port : %s \n", serv_port);
    printf(" client IP   : %s \n", clnt_ip);
    printf(" chat name   : %s \n", name);
    printf(" server time : %s \n", serv_time);
    printf(" ************* menu ***************\n");
    printf(" if you want to select menu -> !menu\n");
    printf(" 1. change name\n");
    printf(" 2. clear/update\n");
    printf(" **********************************\n");
    printf(" Exit -> q & Q\n\n");
}    
 
void error_handling(char* msg)
{
    fputs(msg, stderr);
    fputc('\n', stderr);
    exit(1);
}
cs

  


클라이언트를 실행하면 아래처럼 클라이언트 메뉴가 등장합니다. 


허접한 영어로 되어있지만 사실은 인코딩오류를 방지하기 위해 그랬습니다. ㅎㅎ



채팅창에 !menu 명령어를 통해 닉네임 변경과 화면 새로고침을 할 수 있습니다.


맞습니다.  사실 없어도 되는 기능입니다.


다음 포스팅에 chat_server.c 을 소개하겠습니다.



반응형

[ Visual Studio 2017 환경에서 작성하였습니다. ]


비주얼스튜디오로 처음 C언어를 배우면서 한가지 어려웠던 기억이 있습니다.


학교에서는 분명히 win32 콘솔응용프로그램이 있었는데 집에서 설치하니 보이지가 않더군요 ㅜㅜ..


그리하여! 오늘은 Visual Studio 에서 win32 가 없는 문제를 간단하게 해결하는 포스팅을 해봅니다.


해결하기


파일 -> 새로 만들기 -> 프로젝트 또는 Ctrl + Shift + N 을 입력합니다.



여기까지는 학교와 동일합니다. 문제는 다음이었죠.


비슷하게 생긴 Windows 콘솔 응용프그램으로 만들게 되면 뭔가 안됩니다 ㅜㅜㅜ 우리가 하고자 하는 프로그램은 win32 콘솔 응용프로그램입니다.


비슷하지만 다르죠.



Visual C++ -> Windows 데스크톱 -> Windows 데스크톱 마법사 로 들어가줍니다.


그러면 아래의 사진처럼 응용 프로그램 종류를 선택할 수 있는 창이 나오는데


콘솔 응용프로그램(.exe) 를 선택해 주시고 빈프로젝트를 체크해 주시면 학교에서 실습하던 환경으로 코딩을 하실 수 있습니다 ㅎㅎ 



정보) SDL 검사는 보안이슈가 존재하는 함수들을 컴파일 오류를 통해 사용을 금하는 기능입니다. ex) scanf

       scanf의 경우 scanf_s 와 같이 대체함수를 사용하거나 #define _crt_secure_no_warnings 처리를 해주시면 그대로 사용이 가능합니다.


체크를 해제하면 이러한 함수들을 단순히 경고로 표시해줍니다. 


                                                                                                                             


참고 : https://msdn.microsoft.com/ko-kr/library/ms235629.aspx [win32 콘솔응용프로그램 만들기]


반응형

[Window 10 환경에서 작성하였습니다.]


윈도우10을 처음 사용하면서 바탕화면 기본아이콘을 어떻게 생성하는지 몰라서 고민했던 적이 있습니다.


이번 포스팅에서는 아주 간단하게 내 컴퓨터나 휴지통을 나타나게 하는 방법을 알려드리겠습니다.


1. 검색창에 테마 입력




2. 테마 및 관련 설정 -> 바탕 화면 아이콘 설정


바탕 화면 아이콘 설정에 들어가면 아래 사진과 같이 기본 아이콘을 설정할 수 있습니다.




정말 간단한 작업이지만 처음 사용할 때에는 도저히 못찾겠어서 인터넷에 검색을 했었습니다.


역시 쉬운일 처럼 보이더라도 직접 해보지 않고서는 모르는 일이라는 것을 바탕 화면 기본 아이콘이 저에게 알려주었습니다 ㅎㅎ


이상 포스팅 마치겠습니다!






반응형

안녕하세요 ㅎㅎ 


많이 사용하는 자료구조중 하나인 tree 구조의 순회법인 이진탐색을 이용해서 메모장을 만들어 보았습니다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
// 이진 탐색 트리를 이용한 메모 저장 프로그램.
#include <stdio.h>
#include <stdlib.h> // malloc
#include <string.h> // srcmp
#include <conio.h>  // _getch()
#define MAX_NAME 50   // 최대 제목 길이 50
#define MAX_TEXT 300  // 최대 내용 길이 300
 
/** 메모 데이터 정의(데이터노드) **/
typedef struct DataType
{
    char Name[MAX_NAME]; // 메모의 제목
    char Text[MAX_TEXT]; // 메모의 내용
}DataType;
 
/** 메모장 구조 정의(트리) **/
typedef struct MyMemo {
    DataType Memo;
    struct MyMemo* left;
    struct MyMemo* right;
}MyMemo;
 
/** 문자열 비교 함수 **/
int Compare(DataType A, DataType B) //strcmp 를 한번에 할수 없기 때문에 비교결과를 반환하는 함수를 만듦.
{
    return strcmp(A.Name, B.Name); // 각 문자열의 아스키코드 값을 비교
}
 
/** 메모 삽입 함수 **/
void AddMemo(MyMemo** Root, DataType Memo)
{
    MyMemo* Current = (*Root); // 현재노드. 처음엔 루트를 넣음
    MyMemo* Parent = NULL;     // 부모노드
    MyMemo* NewNode = NULL;    // 새로운 노드
 
    while (Current != NULL) // 자식이 없는 노드를 찾는 알고리즘
    {
        if (Compare(Memo, Current->Memo) == 0)
        {
            printf("  > 메모 생성 실패. 동일한 제목이 존재합니다.\n");
            return;
        }
        Parent = Current;
        if (Compare(Memo, Current->Memo) < 0//작으면 왼쪽
            Current = Current->left;
        else
            Current = Current->right;  // 크면 오른쪽
    }
 
    NewNode = (MyMemo*)malloc(sizeof(MyMemo)); // 새로운 노드 생성!
    if (NewNode == NULL)
        return;
    NewNode->Memo = Memo;
    NewNode->left = NULL;
    NewNode->right = NULL;
 
    if (Parent != NULL) // 위 알고리즘에서 부모를 찾은 경우
    {
        if (Compare(Memo, Parent->Memo) < 0)
            Parent->left = NewNode;
        else
            Parent->right = NewNode;
    }
    else // 부모노드를 못찾은 경우는 새 노드가 루트가 된다.
        *Root = NewNode;
}
 
/** 메모 삭제 함수 **/
void DeleteMemo(MyMemo** Root, DataType Target) // 아니왜삭제가안돼개짜증나게
{
 
    MyMemo* Current = (*Root);
    MyMemo* Parent = NULL;
    MyMemo* Child;
    MyMemo* succ;
    MyMemo* succ_p;
 
    while (Current != NULL && Compare(Current->Memo, Target) != 0// 타켓 노드의 바로 위까지 검색!
    {
        Parent = Current; // 삭제하고자 하는 노드의 부모를 찾는다.
 
        if (Compare(Current->Memo, Target) < 0// 노드가 비교대상 보다 작으면 -1, 크면 1
            Current = Current->right;
        else
            Current = Current->left;
    }
    if (Current == NULL) {// 예외
        //printf(" :: 메모가 존재하지 않습니다!\n\n");
        return;
    }
 
    // 자식 노드가 없는 경우 -> Parent 는 Current 의 부모노드이다.
    if ((Current->left == NULL) && (Current->right == NULL)) //and연산 -> 지우고자 하는 Current 노드의 자식이 없는 경우.
    {
        if (Parent != NULL)
        {
            if (Parent->left == Current)
                Parent->left = NULL;
            else
                Parent->right = NULL;
        }
        else // 부모 노드가 없으므로 루트노드이다.
            *Root = NULL;
    }
 
    //하나의 자식 노드가 있는 경우
    else if ((Current->left == NULL) || (Current->right == NULL)) //or연산
    {
        if (Current->left != NULL) // 왼쪽, 오른쪽 노드 중 존재하는 노드를 자식으로 대입.
            Child = Current->left;
        else
            Child = Current->right;
 
        if (Parent != NULL)
        {
            if (Parent->left == Current) // 현재 노드의 자식을 부모에게 연결한다.
                Parent->left = Child;
            else
                Parent->right = Child;
        }
        else
            *Root = Child; // 부모가 없는 경우 자식이 루트로 된다.
    }
 
    // 자식이 2개인 경우. 
    else
    {
        succ_p = Current;
        succ = Current->right;
 
        while (succ->left != NULL)
        {
            succ_p = succ;
            succ = succ->left;
        }
        if (succ_p->left = succ)
            succ_p->left = succ->right;
        else
            succ_p->right = succ->right;
 
        Current->Memo = succ->Memo;
        Current = succ;
    }
    printf("\n * ─────────────────── *\n");
    printf(" :: %s > 삭제완료 \n\n", Current->Memo.Name);
    free(Current); // 노드 공간 반환.
}
 
/** 전체 메모 출력(제목만 출력됨) **/
int PrintMemo(MyMemo* Root)
{
    MyMemo* Current = Root;
    if (Current != NULL)
    {
        printf(" > %s\n", Current->Memo.Name); // 전위순회
 
        if (Current->left != NULL)
            PrintMemo(Current->left);
        if (Current->right != NULL)
            PrintMemo(Current->right);
    }
    else
    {
        printf(" > 메모가 존재하지 않습니다!\n\n");
        return 0;
    }
    return 1;
}
 
/** 전위 출력**/
void Preorder(MyMemo* Root)
{
    MyMemo* Current = Root;
    if (Current != NULL)
    {
        printf(" > %s\n", Current->Memo.Name);
        Preorder(Current->left);
        Preorder(Current->right);
    }
}
/** 중위 출력**/
void Inorder(MyMemo* Root)
{
    MyMemo* Current = Root;
    if (Current != NULL)
    {
        Preorder(Current->left);
        printf(" > %s\n", Current->Memo.Name);
        Preorder(Current->right);
    }
}
/** 후위 출력**/
void Postorder(MyMemo* Root)
{
    MyMemo* Current = Root;
    if (Current != NULL)
    {
        Preorder(Current->left);
        Preorder(Current->right);
        printf(" > %s\n", Current->Memo.Name);
    }
}
 
/** 메모 검색(제목입력 -> 본문출력) **/
MyMemo* SerchMemo(MyMemo* Root, DataType Target)
{
    MyMemo* Current = Root;
 
    if (Root = NULL)
        return Root;
 
    while (Current != NULL)
    {
        switch (Compare(Current->Memo, Target))
        {
        case 1:
            Current = Current->left; //노드 값이 더 크면 왼쪽.
            break;
        case 0:
            return Current;
            break;
        case -1:
            Current = Current->right; // 노드값이 더 작으면 오른쪽.
            break;
        }
    }
}
 
/** 메모 개수 카운트 **/
int CountMemo(MyMemo* Root)
{
    MyMemo* Current = Root;
    int Count = 0;
 
    if (Current != NULL)
        Count = + CountMemo(Current->left) + CountMemo(Current->right);
 
    return Count;
}
 
/** 화면 정리 대기 **/
void CLS()
{
    printf("\n\n 메뉴로 돌아가려면 Enter 입력.");
    _getch(); // Enter 을 입력받으면 화면을 지운다.
    system("cls"); // 화면을 지우는 명령어.
}
 
int main(void)
{
 
    DataType MemoData;   // 메모의 데이터
    MyMemo* Root = NULL; // 트리 생성 
    MyMemo* Temp;        // 메모의 데이터를 임시 저장하는 노드.
    FILE* file;
    int UserSelect, exit = 0// 스위치 변수, 메뉴 종료 변수.
    while (exit != 1)
    {
        fflush(stdin); // 버퍼비움.
        printf(" *────  메모장 ────*\n");
        printf("│    1.메모작성    │\n");
        printf("│    2.메모삭제    │\n");
        printf("│    3.메모검색    │\n");
        printf("│    4.전체메모    │\n");
        printf("│    5.메모개수    │\n");
        printf("│    6.순회출력    │\n");
        printf("│    7.내보내기    │\n");
        printf("│    8.종료        │\n");
        printf(" *─────────────────*\n");
        printf(" :: 명령입력 > ");
        scanf_s("%d"&UserSelect);
        putchar('\n');
        switch (UserSelect)
        {
        case 1// 메모작성
            getchar(); // 버퍼를 비워준다.
            printf(" :: 메모를 작성합니다. \n\n");
            printf(" :: 제목 > ");
            gets(MemoData.Name);
            printf("\n :: 내용 > ");
            gets(MemoData.Text);
            AddMemo(&Root, MemoData);
            printf(" * ─────────────────── *\n");
            printf(" :: 제목 >   [%s]\n\n", MemoData.Name);
            printf(" :: 내용 >   %s", MemoData.Text);
            CLS();
            break;
        case 2// 메모 삭제
            getchar();
            printf("\n :: 메모를 삭제합니다. \n\n");
            if (PrintMemo(Root))
            {
                printf("\n :: 제목 > ");
                gets(MemoData.Name);
                DeleteMemo(&Root, MemoData);
            }
            CLS();
            break;
        case 3// 메모 검색(내용보기)
            getchar();
            if (PrintMemo(Root)) // PrintMemo -> 보여줄 메모가 있으면 1을 반환. 없으면 0을 반환.
            {// 검색 전 모든 메모를 보여준다.
                printf("\n :: 검색할 메모의 제목 입력 > ");
                gets(MemoData.Name);
                Temp = SerchMemo(Root, MemoData);
                if (Temp != NULL) //예외처리 조건문. 함수에서 오류메세지를 빼냄.
                {
                    printf("\n * ────  %s ──── *\n", Temp->Memo.Name);
                    printf(" %s\n\n", Temp->Memo.Text);
                }
            }
            CLS();
            break;
        case 4// 메모 전체 출력
            getchar();
            PrintMemo(Root);
            CLS();
            break;
        case 5// 메모 개수 카운트
            getchar();
            printf(" :: 메모의 개수 > %d", CountMemo(Root));
            CLS();
            break;
        case 6// 출력 순회 변경
            getchar();
            printf(" *───  출력순회 ───*\n");
            printf("  :: 1.전위순회\n");
            printf("  :: 2.중위순회\n");
            printf("  :: 3.후위순회\n");
            printf(" *─────────────────*\n");
            printf(" :: 명령입력 > ");
            scanf("%d"&UserSelect);
            putchar('\n');
            if (UserSelect == 1)
                Preorder(Root);
            else if (UserSelect == 2)
                Inorder(Root);
            else if (UserSelect == 3)
                Postorder(Root);
            else
                printf(" :: 올바른 메뉴를 입력하세요!\n");
            CLS();
            break;
        case 7// 파일 출력
            getchar();
            if (PrintMemo(Root)) 
            {
                printf("\n :: 파일로 만들 메모의 제목 입력 > ");
                gets(MemoData.Name);
                Temp = SerchMemo(Root, MemoData);
                if (Temp != NULL)
                {
                    file = fopen(Temp->Memo.Name, "w");
                    fprintf(file,Temp->Memo.Text);
                    printf("\n :: %s < 저장성공!\n",Temp->Memo.Name);
                    fclose(file);
                }
                else
                    printf(" :: 내용이 없습니다!\n");
            }
            CLS();
            break;
        case 8// 종료
            getchar();
            printf(" :: 프로그램을 종료합니다. \n\n");
            exit = 1;
            return 0;
        default// 예외
            getchar();
            printf(" :: 올바른 메뉴를 입력하세요!\n");
            CLS();
            break;
        }
    }
}
cs

코드는 대부분 주석을 달아놓아서 이해가 안되시는 부분은 없을거라 생각합니다.


제가 공부를 더 해서 코드던져놓기 말고 자료구조에 대해 포스팅하는 기회도 생기면 좋겠습니다.  ㅎㅎ 

반응형

[블록체인] Linux 에서 Electroneum node 설치하기 -1 : https://good-coding.tistory.com/12


이전에 설치했던 Electroneum 을 실행하고 블록을 synchronization 하는 과정과 wallet 실행 후 지갑주소 발행까지 해보도록 하겠습니다.


일렉트로니움을 설치한 폴더에서 build -> release -> bin 폴더로 이동합니다.


1
2
3
4
5
cd build
cd release
cd bin
 
./electroneumd
cs



이후 실행파일인 electroneumd 를 실행합니다.


그러면 daemon 이 실행되며 일렉트로니움 네트워크에 블록들을 동기화하기 시작합니다. 



아래와 같이 블록들을 다운받게 되는데, 대부분의 블록체인이 synchronization 과정을 거쳐야만 노드로서 인정받고 활동할 수 있습니다.


이 과정은 상당히 오래걸리며 블록체인이 해결해야할 과제중 하나라고 생각합니다ㅜㅜ. 저도 테스트용 리눅스로 설치를 했기 때문에 모든 블록을 동기화하


기까지 얼마나 걸리는지는 실험해 보지 못했지만 몇일 ~ 1주일 정도까지 걸리는 것으로 알고 있습니다.


블록체인 네트워크든 사람이든 어디선가 인정받고 살아가려면 험난한 사회화 과정을 거쳐야 한다는 점을 간접적으로나마 느끼는 순간입니다.


이러한 이유로 백그라운드에서 작업을 해주시면 되겠습니다.



* 백그라운드 실행


1
2
3
ctrl + z 
jobs
bg %1 (electroeumd 의 프로세스 번호)
cs

동기화 되는 동안 저희는 일렉트로니움 지갑을 만들어 보겠습니다.

우선 같은 폴더에서 electroneum-wallet-cli 를 실행합니다. 

지갑 이름, 비밀번호, 비밀번호 확인, 언어선택 순으로 진행이 됩니다. 


저는 당연히 1 : English 로 선택을 했습니다. 대부분의 프로그램들에서 한국어도 볼 수 있으면 좋겠어요 ㅜㅜ


해당 과정을 마치면 아래와 같이 지갑 주소를 얻게됩니다.



지갑이 실행된 상태에서 help 명령어를 입력하면 다양한 지갑의 명령어를 볼 수 있습니다.


저는 balance 명령어를 실행해서 현재 잔고를 확인해보겠습니다.



따흑 ㅜㅜ 0원이네요... 당연합니다 아직 동기화도 되지 않았기 때문이요 ㅎㅎ (no daemon 상태)

* 참고로 ETN은 일정 단위의 ETN이 모이면 지급이 된다고 합니다. 즉 unlocked balance 는 당장 사용할 수 없는 자산입니다.


기회가 된다면 직접 리눅스에서 채굴을 하여 자산이 늘어나는 것을 보여드리고 싶지만 ㅎㅎ.. 가난한 제 pc환경에서는 벅차서 아쉽습니다.


다음 포스팅에는 직접 암호화폐를 간단하게 만들어 보는 내용을 다루고자 합니다. 감사합니다.

반응형


이번 포스팅에서는 이전에 소개했던 암호화폐 일렉트로니움을 


실제 리눅스 환경에서 설치 후 구동하는 방법을 소개하겠습니다. 


1. VirtualBox 설정의 권장사양


18-07-05 기준 일렉트로니움의 블록 길이는 약 350,000개 입니다. 


여유있게 50GB 정도 공간을 할당해 주시면 될 것 같습니다. (테스트는 해보지 못했습니다.)


메모리는 4GB, 코어는 3개 정도 할당해 주시면 되겠습니다.



2. 필요한 패키지 다운로드


우선 구동에 필요한 패키지를 다운로드합니다. 


사실 모든 패키지가 필요하지는 않은 것으로 보이나 개발팀이 권장하는 사항이니 만큼 모든 기능을 사용하려면 설치하는것이 좋아보입니다.


아래 사진에 패키지들이 어떤 것인지 설명되어 있으니 파악하시면 되겠습니다.




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
sudo apt-get update
sudo apt-get upgrade

sudo apt-get install git
sudo apt-get install vim
 
sudo apt-get install bulid-essential
sudo apt-get install cmake
sudo apt-get install pkg-config
sudo apt-get install libboost-all-dev
sudo apt-get install libssl-dev
sudo apt-get install libunbound-dev
sudo apt-get install libminiupnpc-dev
sudo apt-get install libunwind8-dev
sudo apt-get install liblzma-dev
sudo apt-get install libldns-dev
sudo apt-get install libexpat1-dev
sudo apt-get install libgtest-dev
sudo apt-get install doxygen
sudo apt-get install graphviz
cs


설치과정에서 git 과 vi에디터가 필요함으로 함께 설치해 주시면 됩니다 ㅎㅎ


3. 프로젝트 소스 다운로드 및 컴파일

이후 폴더를 만들어 줍니다. 저는 downloads 라고 하였는데 이름은 딱히 상관없습니다.

해당 폴더로 이동하신 뒤, git 주소로 일렉트로니움 소스를 가져옵니다.


1
2
3
4
5
6
mkdir /downloads
cd /downloads
git clone https://github.com/electroneum/electroneum
 
cd /electroneum
make 
cs


위의 명령어를 실행하면 downloads 폴더에 프로젝트 파일이 복사됩니다.

이후 downloads 폴더 안에 electroneum 폴더로 이동하시면 아래와 같이 Makefile 이 있습니다. 


make 해 주시면 25분정도 컴파일이 되는데 make -j<number of threads> 로 스레드를 여러개로 수행하면 

좀 더 빠르게 진행됩니다.  ex) make -j3

* 컴파일이 완료된 모습


4. 오류 해결


컴파일 도중 터미널 창이 멈추고 반응이 없는 경우가 있습니다.

해당 소스를 컴파일 하기 위해서는 ubuntu-64bit 환경에서 테스트 해본 결과 메모리가 4GB 이상으로 여유가 있는 상태어야 합니다.

메모리가 부족한 경우 (VirtualBox 의 기본설정 메모리는 1GB) 설정에서 메모리를 더 할당해 주신 후, 재부팅 해주시면 됩니다.

다른 이슈가 발생할 경우 https://github.com/electroneum/electroneum/issues 에서 찾아보시거나 댓글로 남겨 주세요.

                                         

다음 포스팅에서는 wallet 실행과 메인넷 동기화를 진행하겠습니다.

[블록체인] Linux 에서 Electroneum node 설치하기 -2 : https://good-coding.tistory.com/13


반응형

java를 이용하여 4bit 2진수를 입력받아 2의 보수로 변환해 주는 2의보수 변환프로그램입니다. 


원래는 다른 프로그램에서 사용하던 모듈화된 클래스파일이라 단순한 로직임에도 불구하고 복잡한 면이 있습니다. 


혹시 2진수를 2의 보수변환을 하시는 분은 conversionCom() 메소드만 가져다가 사용하시면 될 것 같습니다 ㅎㅎ


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package complement_4bit;
 
import java.util.Scanner;
 
public class Main {
 
    /* 2진수를 2의 보수로 변환 or 다시 양수로 변환 메소드 */
    private int[] conversionCom(int[] arr) {
        int[] arrComplement = new int[4];
        // 0->1, 1->0 으로 변환
        for (int i = 3; i >= 0; i--) {
            if (arr[i] == 0) {
                arrComplement[i] = 1;
            } else {
                arrComplement[i] = 0;
            }
        }
        // +1 을 해주는 과정
        arrComplement[3+= 1;
        for (int j = 3; j >= 0; j--) {
            if (arrComplement[j] == 2) {
                arrComplement[j] = 0;
                arrComplement[j - 1+= 1;
            }
        }
        return arrComplement;
    }
    
    /* 4비트 배열에 적절한 값이 들어오는지 검사 */
    private int inputCheck(int param) {
        if (param == -1)
            param = -1//  프로그램 종료
        
        if (param < -|| param > 1)
            param = -3// 값을 다시 입력받음
        
        return param;
    }
    
    /* 배열을 출력해준다 */
    private void arrPrint(int[] arr) {
        int size = arr.length;
        System.out.print("\t결과값     >> " );
        for (int i = 0; i < size; i++) {
            System.out.print(" "+ arr[i]);
        }
    }
 
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        Main m = new Main();
        
        int register[] = new int[4];
        int nInput;
 
        boolean ctrtMenu = true;
        while (ctrtMenu) {
            System.out.println("\n\n\t─── 4bit 2의 보수 변환기 ──────");
            System.out.println("\t1. 2진수 입력");
            System.out.println("\t──────────────────────────");
            System.out.println("\t:: 첫 번째 자리가 1이면 보수로 판단.");
            System.out.println("\t:: 입력은 4bit 만 작동합니다.");
            System.out.println("\t:: 입력 단위는 스페이스또는 엔터입니다.");
            System.out.println("\t:: EX)1 0 0 1 , 0 0 0 1");
            System.out.println("\t:: 종료는 -1");
            System.out.println("\t──────────────────────────");
            System.out.print("\t2진수 입력 >> ");
        
            for (int i = 0; i < 4; i++) {
                nInput = m.inputCheck(input.nextInt());
                
                if (nInput == -1) {
                    System.out.println("\n\t프로그램을 종료합니다.");
                    System.exit(0);     
                }
                
                if (nInput == -3) {
                    i--;
                    System.out.println("\terror : 2진수를 입력해 주세요!");
                    System.out.print("\t2진수 입력 >> ");
                    continue;
                }
                register[i] = nInput; // 배열에 유효성검사를 한 수를 넣는다.
            }
            m.arrPrint(m.conversionCom(register)); // 출력
        }
    }
}
 
 
 
cs

18번 라인의 +1은 2의 보수이기 때문에 비트역전을 한 뒤, +1를 해주는 과정입니다. 


72~82번 라인까지는 1과 0 이외의 숫자가 입력됬을시를 처리하는 부분이며 종료명령인 -1를 인식하여 


프로그램을 종료합니다. 

반응형

'JAVA' 카테고리의 다른 글

[JAVA] java 기본 - 프로그램 종료하기  (0) 2018.09.16

java 에서 프로그램을 종료할 때에는 System.exit(0) 메소드를 사용해 주시면 됩니다.


1
2
3
4
5
6
7
8
9
10
package exit;
 
public class Exit {
 
    public static void main(String[] args) {
        System.out.println("\n\n\t프로그램을 종료합니다.");
        System.exit(0);
    }
}
 
cs

exit 명령어를 사용하면 아래와 같이 프로그램을 종료할 수 있습니다. 


반응형

'JAVA' 카테고리의 다른 글

[JAVA] 2진수를 입력받아 2의 보수로 변경하는 프로그램  (0) 2018.09.16

현재 블록체인을 이용한 다양한 암호화폐들이 존재합니다.


그중 일렉트로니움은 익명성이 강한 모네로를 모체로 하는 블록체인입니다.


사실 모네로도 대쉬와 동일하게 익명성이 짙은 cryptonote 를 기반으로 만들어졌는데, 자세한 내용은 다음에 포스팅해보도록 하겠습니다.


일렉트로니움의 주요 특징


1. 추적이 불가능한 트랜잭션


2. 연결되지 않는 트랜잭션 


3. 분석 방지


4. 평등한 작업증명


5. 모바일 채굴 및 빠른 속도


모바일 지갑, 채굴이 가능하다.


일렉트로니움은 모바일 생태계를 목표료 구현되었으며 21억개의 발행량, 빠른 트랜젝션, 높은 익명성의 특징을 가지고 있으며,


확실히 다른 코인들과 차별점이 존재합니다.


현재 일렉트로니움은 현재 Android, IOS 두 운영체제에서 모두 사용이 가능합니다. 


여기서 주목할 점은 모바일 프로세서를 통한 채굴이 가능하다는 점입니다. 과연 모바일 프로세서가 얼마나 기여할지는 모르겠지만


확실하게 보상을 준다는 것에 초점을 맞추면 되겠습니다.

* 오피셜에 의하면 모바일 채굴은 블록체인 네트워크에 그다지 큰 기여는 하지 못한다고 합니다.


스크린샷 이미지


이렇게 마이닝을 시작하면 백그라운드에서 가동되며 채굴을 합니다. 


기존 GPU 를 이용한 채굴방식에 비하면 상당히 접근성있고 가벼운? 보상시스템이라고 생각합니다. 


많은 사람이 참여할 수록 좋은 블록체인이기 때문에 이 부분은 상당히 강점이 되겠군요.


모바일을 통해 현재 시세로 하루에 약 2~300원 정도의 ETN 채굴이 가능하다고 합니다. 


다만 채굴프로그램을 가동할 경우 휴대폰이 쉬지 않고 일을 하기 때문에 지금 사용하시는 휴대폰 보다는 


사용하지 않는 휴대폰으로 먼저 어느 정도 자원을 소모하는지 테스트할 필요가 있을것 같습니다.



거래소


현재 일렉트로니움 거래량의 약 80%를 차지하는 상위 3개의 거래소 목록입니다. 이외에도 6곳 정도의 거래소에서 거래되고 있습니다.





다음 포스팅에는 일렉트로니움 지갑을 리눅스 환경에서 설치해 보도록 하겠습니다. 


[블록체인] Linux 에서 Electroneum node 설치하기 -1 : https://good-coding.tistory.com/12

                                                                


공식사이트 : https://electroneum.com/

git : https://github.com/electroneum/electroneum



반응형

+ Recent posts