Difference between r1.3 and the current
@@ -725,7 +725,8 @@
=== 버그 ===
* 터미널 세로 크기가 20 이하일 경우 화면이 꼬임
* 메세지 작성 중, 다른 유저가 메세지를 보낼 시 작성 중이던 부분이 보이지 않음.
* 서버 주소가 127.0.0.1(loopback)로 설정되어있음.
----
* 터미널 세로 크기가 20 이하일 경우 화면이 꼬임
* 메세지 작성 중, 다른 유저가 메세지를 보낼 시 작성 중이던 부분이 보이지 않음.
* ID 최대 생성 갯수가 100개임.
* ID 최대 생성 갯수를 넘을 시 렉이 걸림
* 부재 중 대화 내역이 100개가 넘을시 그 이전 기록은 볼 수 없음.
=== 기타 사항 ===* 서버 주소가 127.0.0.1(loopback)로 설정되어있음.
----
서버 ¶
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<arpa/inet.h> #include<sys/types.h> #include<sys/socket.h> #include<pthread.h> #define BUFF_SIZE 1024 #define ACCEPT 0 #define REJECT 1 #define DISCONNECT -1 int thread_num[25]; //스레드 번호 (해당 스레드 활성화시 번호 값 + 1, 비활성화시 0) //ex) thread_num[스레드 번호]==스레드 번호+1 int thread_id[25]; //스레드 ID 번호 //ex) 스레드 아이디 = id[thread_id[스레드 번호]] int client_socket_array[25]; //클라이언트 소캣, 각 스레드 마다 자신의 번호에 해당하는 소캣 사용 //ex) 스레드가 사용 중인 소캣 == client_socket_array[스레드 번호] char id[100][256]; char password[100][256]; char id_state[100]; int id_num; char chat[100][BUFF_SIZE+5]; char chat_s[100], chat_e; int stcmp(char* a,char *b) { int i; for(i=0; a[i]&&b[i] ;i++) { if(a[i]!=b[i]) return 1; } return 0; } int send_i(int client_socket,char* arry)//명령어 전송 { char buff_snd[BUFF_SIZE+5]; sprintf(buff_snd,"i %s\n",arry); send(client_socket, buff_snd, strlen(buff_snd)+1,0); } int send_m(int client_socket,char arry[])//메세지 전송 { char buff_snd[BUFF_SIZE+5]; sprintf(buff_snd,"m %s\n",arry); send(client_socket, buff_snd, strlen(buff_snd)+1,0); } int login(int t_num) { int i; char buff_rcv[BUFF_SIZE+5]; int client_socket; client_socket=client_socket_array[t_num-1]; printf("%dth clinet try to login\n",t_num); while(1) { send_i(client_socket,"i"); if(recv(client_socket, buff_rcv,BUFF_SIZE,0)<=0) { return DISCONNECT; } for(i=0;i<id_num;i++) { if(!stcmp(id[i],buff_rcv)) break; } if(i==id_num) { send_m(client_socket,"ID is wrong"); while(1) { if(recv(client_socket,buff_rcv,BUFF_SIZE,0)<=0) return DISCONNECT; if(buff_rcv[0]=='a') break; } continue; } else { if(id_state[i]) { send_m(client_socket,"ID is being used."); while(1) { if(recv(client_socket,buff_rcv,BUFF_SIZE,0)<=0) return DISCONNECT; if(buff_rcv[0]=='a') break; } continue; } } send_i(client_socket,"p");//password 요구 if(recv(client_socket, buff_rcv,BUFF_SIZE,0)<=0) { return DISCONNECT; } if(stcmp(password[i],buff_rcv)) { send_m(client_socket, "password is wrong"); while(1) { if(recv(client_socket,buff_rcv,BUFF_SIZE,0)<=0) return DISCONNECT; if(buff_rcv[0]=='a') break; } continue; } send_i(client_socket,"a");//접속 확인 return i; } } int new_ID(int t_num) { int i; char buff_rcv[BUFF_SIZE+5]; int client_socket; client_socket=client_socket_array[t_num-1]; printf("%dth clinet try to create ID\n",t_num); while(1) { send_i(client_socket,"i"); if(recv(client_socket, buff_rcv,BUFF_SIZE,0)<=0) { return DISCONNECT; } for(i=0;i<id_num;i++) { if(!stcmp(id[i],buff_rcv)) break; } if(i==id_num) { sprintf(id[id_num],"%s",buff_rcv); id_num++; } else { send_m(client_socket,"ID is already existing.."); while(1) { if(recv(client_socket,buff_rcv,BUFF_SIZE,0)<=0) return DISCONNECT; if(buff_rcv[0]=='a') break; } continue; } send_i(client_socket,"p");//password 요구 if(recv(client_socket, buff_rcv,BUFF_SIZE,0)<=0) { return DISCONNECT; } sprintf(password[i],"%s",buff_rcv); send_i(client_socket,"a");//접속 확인 return i; } } void command(int t_num, char buff_rcv[]) { int i,j,k; char temp_buff[BUFF_SIZE+1]; char buff_snd[BUFF_SIZE+1]; sprintf(temp_buff,"./help\n"); if(!stcmp(temp_buff,buff_rcv)) { send_m(client_socket_array[t_num-1],"<Command list>"); send_m(client_socket_array[t_num-1],"help : show list of command"); send_m(client_socket_array[t_num-1],"list : show list of online user"); send_m(client_socket_array[t_num-1],"message \"ID\" \"message\" : send message to ID"); send_m(client_socket_array[t_num-1],"\n"); return; } sprintf(temp_buff,"./list\n"); if(!stcmp(temp_buff,buff_rcv)) { send_m(client_socket_array[t_num-1],"<Online user list>"); for(i=0;i<20;i++) if(thread_num[i] && thread_id[i]>=0) { send_m(client_socket_array[t_num-1],id[thread_id[i]]); } send_m(client_socket_array[t_num-1],"\n"); return; } sprintf(temp_buff,"./message "); for(i=0;i<10;i++) { if(temp_buff[i]!=buff_rcv[i]) break; } if(i==10) { for(i=0;i<id_num;i++) { for(j=0;buff_rcv[j+10]!=' ';j++) { if(id[i][j]!=buff_rcv[j+10]) break; } if(buff_rcv[j+10]==' ') { if(id_state[i]) { sprintf(temp_buff,"send to %s : %s", id[i], &buff_rcv[j+11] ); send_m(client_socket_array[t_num-1],temp_buff); for(k=0;k<20;k++) if(thread_id[k]==i) { sprintf(temp_buff,"message from %s : %s", id[thread_num[t_num-1]],&buff_rcv[j+11]); send_m(client_socket_array[k],temp_buff); break; } break; } else { send_m(client_socket_array[t_num-1],"ID is offline"); break; } } } if(i==id_num) send_m(client_socket_array[t_num-1],"ID is wrong"); } } void message(int t_num, char buff_rcv[]) { int i; char buff_snd[BUFF_SIZE+1]; sprintf(buff_snd,"%s : %s",id[thread_id[t_num-1]],buff_rcv); for(i=0;i<20;i++) { if(thread_num[i] && thread_id[i]>=0) send_m(client_socket_array[i],buff_snd); } sprintf(chat[chat_e],"%s",buff_snd); chat_e++; chat_e%=100; for(i=0;i<100;i++) if(id_state[i] || i>=id_num) chat_s[i]=chat_e; } void disconnect(int t_num, int i_num) { printf("%dth client is disconnected\n",t_num); id_state[i_num]=0; thread_num[t_num-1]=0; close(client_socket_array[t_num-1]); client_socket_array[t_num-1]=0; thread_id[t_num-1]=0; } void* rutine(void* data)//data = &thread_num[스레드 번호] { int t_num,i_num;//스레드 번호, 아이디 번호 int client_socket; char buff_rcv[BUFF_SIZE+5],buff_snd[BUFF_SIZE+5]; int rcv; int i,j; t_num=*((int*)data); //사용자가 이해하기 쉽도록 스레드 번호에 +1 값을 쓰도록 한다. client_socket=client_socket_array[t_num-1]; //코드의 간결화를 위해 값을 복사한다. printf("%dth client is connected, socket_num is %d\n",t_num, client_socket); thread_id[t_num-1]=-1; while(1) { if(recv(client_socket,buff_rcv,BUFF_SIZE,0)<=0) { disconnect(t_num,i_num); return; } if(buff_rcv[0] == 'l') { send_i(client_socket,"a"); while(1) { if(recv(client_socket,buff_rcv,BUFF_SIZE,0)<=0) { disconnect(t_num,i_num); return; } else { if(buff_rcv[0]=='a') break; } } i_num=login(t_num); break; } else { if(buff_rcv[0] == 'n') { send_i(client_socket,"a"); while(1) { if(recv(client_socket,buff_rcv,BUFF_SIZE,0)<=0) { disconnect(t_num,i_num); return; } else { if(buff_rcv[0]=='a') break; } } i_num=new_ID(t_num); break; } else { send_i(client_socket,"c"); } } } if(i_num==DISCONNECT) { disconnect(t_num,i_num); return; } thread_id[t_num-1]=i_num; id_state[i_num]=1; //로그인 도중 메세지가 전송되는 것을 방지, 클라이언트가 작업을 완료하는 것을 기다린다. while(recv(client_socket,buff_rcv,BUFF_SIZE,0)) { if(buff_rcv[0]=='a') break; } sprintf(buff_snd,"log\n"); send_m(client_socket,buff_snd); for(;chat_s[i_num]!=chat_e;chat_s[i_num]++) { sprintf(buff_snd,"%s\n",chat[chat_s[i_num]]); send_m(client_socket, buff_snd); } sprintf(buff_snd,"%s has joined",id[i_num]); for(i=0;i<20;i++) { if(thread_num[i] && thread_id[i]>=0) send_m(client_socket_array[i],buff_snd); } while(1) { rcv=recv(client_socket,buff_rcv,BUFF_SIZE,0); if(rcv>0) { printf("received\n"); if(buff_rcv[0]=='.' && buff_rcv[1]=='/') command(t_num,buff_rcv); else message(t_num,buff_rcv); } else { break; } } sprintf(buff_snd,"%s has left",id[i_num]); for(i=0;i<20;i++) { if(thread_num[i]) send_m(client_socket_array[i],buff_snd); } disconnect(t_num, i_num); } int main() { int server_socket; int client_socket; pthread_t p_thread[25]; struct sockaddr_in server_addr; struct sockaddr_in client_addr; int client_addr_size; int i; server_socket=socket(PF_INET, SOCK_STREAM, 0); if(server_socket==-1) { printf("socket error\n"); exit(1); } memset(&server_addr,0,sizeof(server_addr)); server_addr.sin_family =AF_INET; server_addr.sin_port =htons(4000); server_addr.sin_addr.s_addr=htonl(INADDR_ANY); if(-1==bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr))) { printf("bind error\n"); exit(1); } printf("server started\n"); for(i=0;i<20;i++) thread_num[i]=0; while(1) { if(-1==listen(server_socket,5)) { printf("listen error\n"); exit(1); } client_socket = accept(server_socket, (struct sockaddr*)&client_addr,&client_addr_size); if(client_socket==-1) continue; for(i=0;i<20;i++) { if(!thread_num[i]) { client_addr_size= sizeof(client_addr); client_socket_array[i] = client_socket; thread_num[i]=i+1; memset(&p_thread[i],0,sizeof(p_thread[i])); pthread_create(&p_thread[i],NULL,rutine,(void *)&thread_num[i]); break; } } if(i==20) printf("error : Too many clients connected\n"); } close(server_socket); return 0; }
클라이언트 ¶
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<sys/types.h> #include<sys/socket.h> #include<arpa/inet.h> #include<pthread.h> #define BUFF_SIZE 1024 void gotoxy(int x,int y) { printf("\033[%d;%df",y,x); //터미널 출력 위치 변경 함수, y값이 화면 길이보다 크면 화면 길이로 처리된다.(버그의 원인) fflush(stdout); } int client_socket; int check; void* rcv_thread(void *data) { int i; char buff_rcv[BUFF_SIZE+5]; int rcv; while(1) { rcv=recv(client_socket, buff_rcv, BUFF_SIZE, 0); if(rcv>0) { while(check==1); check=2; for(i=0;i<rcv;) { if(buff_rcv[i]=='m') { i+=2; gotoxy(0,100);//gotoxy의 특징을 이용하여 화면을 한칸 올린다. printf("\n"); gotoxy(0,14); printf(" "); gotoxy(0,14); printf("%s",&buff_rcv[i]); } if(buff_rcv[i]=='i') { } i+=strlen(&buff_rcv[i])+1; } } else { printf("disconnected\n"); break; } gotoxy(0,15); printf("write message to send : "); gotoxy(25,15); fflush(stdout); check=0; } } void* snd_thread(void *data) { char buff_snd[BUFF_SIZE+5]; fgets(buff_snd,BUFF_SIZE,stdin);//버퍼 청소 while(1) { while(check==2); check=1; gotoxy(0,15); printf("write message to send : "); gotoxy(25,15); check=0; fgets(buff_snd,BUFF_SIZE,stdin); check=0; if(send(client_socket, buff_snd, strlen(buff_snd)+1,0)<=0) { printf("disconnected\n"); break; } } } int login(int client_socket) { char buff_rcv[BUFF_SIZE+5]; char buff_snd[BUFF_SIZE+5]; //로그인 시스템 while(1) { printf("new id=n / login=l : "); fflush(stdout); scanf("%s",buff_snd); send(client_socket,buff_snd,strlen(buff_snd)+1,0); while(1) { if(recv(client_socket,buff_rcv,BUFF_SIZE,0)<=0) { printf("disconnected\n"); close(client_socket); return -1; } if(buff_rcv[0]=='i') break; if(buff_rcv[0]=='m') printf("%s",&buff_rcv[2]); } if(buff_rcv[2]=='a') break; } sprintf(buff_snd,"a"); send(client_socket,buff_snd,strlen(buff_snd)+1,0); while(1) { if(recv(client_socket,buff_rcv,BUFF_SIZE,0)>0) { if(buff_rcv[0]=='i') { switch(buff_rcv[2]) { case 'i' : printf("ID : "); fflush(stdout); scanf("%s",buff_snd); send(client_socket,buff_snd,strlen(buff_snd)+1,0); break; case 'p' : printf("PASSWORD : "); fflush(stdout); scanf("%s",buff_snd); send(client_socket,buff_snd,strlen(buff_snd)+1,0); break; } if( buff_rcv[2] == 'a' ) break; } if(buff_rcv[0]=='m') { printf("%s",&buff_rcv[2]); sprintf(buff_snd,"a"); send(client_socket, buff_snd, strlen(buff_snd)+1, 0); } } else { printf("disconnected\n"); close(client_socket); return -1; } } return 0; } int main() { struct sockaddr_in server_addr; char buff_snd[BUFF_SIZE+5]; char buff_rcv[BUFF_SIZE+5]; int rcv; int i; pthread_t p_thread[2]; client_socket=socket(PF_INET, SOCK_STREAM, 0); if(client_socket==-1) { printf("socket error\n"); return 1; } memset( &server_addr, 0, sizeof( server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons( 4000); server_addr.sin_addr.s_addr= inet_addr( "127.0.0.1"); if(-1==connect(client_socket,(struct sockaddr*)&server_addr, sizeof( server_addr) ) ) { printf("connect error\n"); return 1; } if(login(client_socket)) return 0; sprintf(buff_snd,"a"); send(client_socket,buff_snd,strlen(buff_snd)+1,0);//메세지 전송을 허용함. memset(&p_thread[0],0,sizeof(p_thread[0])); pthread_create(&p_thread[0],NULL,rcv_thread,(void *)NULL); memset(&p_thread[1],0,sizeof(p_thread[1])); pthread_create(&p_thread[1],NULL,snd_thread,(void *)NULL); pthread_join(p_thread[0],(void**)&rcv); pthread_join(p_thread[1],(void**)&rcv); close(client_socket); return 0; }
설명 ¶
기능 ¶
- 리눅스 멀티 채팅
- 계정 생성 및 비밀번호 생성
- 계정으로 로그인시 부재 중 대화 내역을 볼 수 있음
- 동시접속 최대 20명까지 가능
- 아이디 최대 100개까지 생성 가능
- 간단한 명령어 가능 (명령어 목록 보기, 접속 인원 보기, 귓속말)
버그 ¶
- 터미널 세로 크기가 20 이하일 경우 화면이 꼬임
- 메세지 작성 중, 다른 유저가 메세지를 보낼 시 작성 중이던 부분이 보이지 않음.
- ID 최대 생성 갯수를 넘을 시 렉이 걸림
- 부재 중 대화 내역이 100개가 넘을시 그 이전 기록은 볼 수 없음.