리눅스에서 멀티쓰레드를 이용한 채팅프로그램을 만들어 보았습니다.
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, 0, sizeof(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 을 소개하겠습니다.
'C > 소켓프로그래밍' 카테고리의 다른 글
[소켓프로그래밍] 멀티쓰레드 채팅 프로그램 (Linux) -2 (7) | 2018.12.28 |
---|