SyntaxHighlighter.all();

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


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. 테마 및 관련 설정 -> 바탕 화면 아이콘 설정


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




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


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


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






반응형

+ Recent posts